1 //#define NTDDI_VERSION NTDDI_WIN7
2 //#include <shobjidl.h>
3 #include <TCFoundation/TCDefines.h>
4 #include <windows.h>
5 #include <windowsx.h>
6 #include "ModelLoader.h"
7 #include "SSConfigure.h"
8 #include <TCFoundation/TCAutoreleasePool.h>
9 #include <TCFoundation/TCDictionary.h>
10 #include <TCFoundation/TCUserDefaults.h>
11 #include <TCFoundation/TCLocalStrings.h>
12 #include <TCFoundation/mystring.h>
13 #include <CUI/CUIWindow.h>
14 #include "SSPreview.h"
15 #include "Resource.h"
16 #include <LDLib/LDUserDefaultsKeys.h>
17 #include <stdio.h>
18 #include <ctime>
19 #include <cstdlib>
20
21 //#include <TCFoundation/TCImage.h>
22 //#include <TCFoundation/TCJpegOptions.h>
23
24 #if defined(_MSC_VER) && _MSC_VER >= 1400 && defined(_DEBUG)
25 #define new DEBUG_CLIENTBLOCK
26 #endif
27
28 static bool debugToFile = false;
29
30 typedef BOOL (__stdcall *PFNATTACHCONSOLE)(DWORD dwProcessId);
31 typedef HRESULT (__stdcall *PFNDLLREGISTERSERVER)(void);
32
createConsole(void)33 void createConsole(void)
34 {
35 if (AllocConsole())
36 {
37 COORD size = {80, 1000};
38 SMALL_RECT rect = {0, 0, 79, 24};
39 // HANDLE hOrigStdOut;
40 HANDLE hStdOut;
41 SECURITY_ATTRIBUTES securityAttributes;
42
43 securityAttributes.nLength = sizeof SECURITY_ATTRIBUTES;
44 securityAttributes.lpSecurityDescriptor = NULL;
45 securityAttributes.bInheritHandle = TRUE;
46 hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
47 // hStdOut = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
48 // FILE_SHARE_READ | FILE_SHARE_WRITE, &securityAttributes,
49 // CONSOLE_TEXTMODE_BUFFER, NULL);
50 SetConsoleScreenBufferSize(hStdOut, size);
51 SetConsoleWindowInfo(hStdOut, TRUE, &rect);
52 SetConsoleActiveScreenBuffer(hStdOut);
53 // SetStdHandle(STD_OUTPUT_HANDLE, hStdOut);
54 freopen("CONOUT$", "w", stdout);
55 freopen("CONIN$", "r", stdin);
56 }
57 }
58
isScreenSaver(void)59 bool isScreenSaver(void)
60 {
61 UCSTR programName;
62 bool retVal;
63 UCSTR commandLine = NULL;
64 UCSTR spot;
65
66 commandLine = copyString(GetCommandLine());
67 if (commandLine[0] == '"')
68 {
69 if ((spot = ucstrchr(commandLine + 1, '"')) != NULL)
70 {
71 *spot = 0;
72 }
73 programName = commandLine + 1;
74 }
75 else
76 {
77 if ((spot = ucstrchr(commandLine, ' ')) != NULL)
78 {
79 *spot = 0;
80 }
81 programName = commandLine;
82 }
83 retVal = ucstrlen(programName) > 4 &&
84 ucstrcasecmp(programName + ucstrlen(programName) - 4, _UC(".scr")) == 0;
85 delete commandLine;
86 return retVal;
87 }
88
debugOut(char * fmt,...)89 void debugOut(char *fmt, ...)
90 {
91 if (!debugToFile)
92 {
93 return;
94 }
95 static const char* userProfile = getenv("USERPROFILE");
96 std::string debugPath = "C:\\LDViewDebug.txt";
97 if (userProfile != NULL)
98 {
99 debugPath = std::string(userProfile) + "\\LDViewDebug.txt";
100 }
101 FILE* debugFile = ucfopen(debugPath.c_str(), "a+");
102
103 if (debugFile)
104 {
105 va_list marker;
106
107 std::time_t t = std::time(nullptr);
108 char mbstr[100];
109 if (std::strftime(mbstr, sizeof(mbstr), "%F %T", std::localtime(&t)))
110 {
111 fprintf(debugFile, "%s: ", mbstr);
112 }
113 va_start(marker, fmt);
114 vfprintf(debugFile, fmt, marker);
115 va_end(marker);
116 fclose(debugFile);
117 }
118 }
119
mainLoop()120 int mainLoop()
121 {
122 MSG msg;
123 HACCEL hAccel;
124 bool screenSaver = isScreenSaver();
125 // DWORD startTickCount = GetTickCount();
126
127 hAccel = LoadAccelerators(CUIWindow::getLanguageModule(),
128 MAKEINTRESOURCE(IDR_ACCELERATORS));
129 while (1)
130 {
131 HWND parentWindow;
132 HWND topParent;
133 HWND newParent;
134 DWORD tickCount = GetTickCount();
135
136 //debugOut("%d\n", tickCount);
137 /*
138 if (tickCount > startTickCount + 3000 || tickCount < startTickCount)
139 {
140 OleUninitialize();
141 return 0;
142 }
143 */
144 if (!GetMessage(&msg, NULL, 0, 0))
145 {
146 OleUninitialize();
147 return (int)msg.wParam;
148 }
149 #ifdef _DEBUG
150 // _CrtDbgReport(_CRT_WARN, NULL, 0, NULL, "hWnd: 0x%6X msg: 0x%X\n", msg.hwnd, msg.message);
151 #endif // _DEBUG
152 parentWindow = GetParent(msg.hwnd);
153 topParent = msg.hwnd;
154 while ((newParent = GetParent(topParent)) != NULL)
155 {
156 topParent = newParent;
157 }
158 if (screenSaver || !TranslateAccelerator(
159 topParent, // handle to receiving window
160 hAccel, // handle to active accelerator table
161 &msg))
162 {
163 if (!parentWindow || !IsDialogMessage(parentWindow, &msg))
164 {
165 TranslateMessage(&msg);
166 DispatchMessage(&msg);
167 if (screenSaver && msg.message == WM_DESTROY)
168 {
169 debugOut("WM_DESTROY\n", tickCount);
170 PostQuitMessage(0);
171 }
172 }
173 }
174 TCAutoreleasePool::processReleases();
175 }
176 }
177
flushPreviewEvents(HWND hWindow)178 BOOL flushPreviewEvents(HWND hWindow)
179 {
180 // In preview mode, "pause" (enter a limited message loop) briefly
181 // before proceeding, so the display control panel knows to update itself.
182 BOOL waitForInputIdle = TRUE;
183
184 // Post a message to mark the end of the initial group of window messages
185 PostMessage( hWindow, WM_USER, 0, 0 );
186
187 MSG msg;
188 while( waitForInputIdle )
189 {
190 // If GetMessage returns FALSE, it's quitting time.
191 if( !GetMessage( &msg, NULL, 0, 0 ) )
192 {
193 return TRUE;
194 }
195
196 TranslateMessage( &msg );
197 if (msg.message == WM_USER && msg.hwnd == hWindow)
198 {
199 waitForInputIdle = FALSE;
200 }
201 else
202 {
203 DispatchMessage( &msg );
204 }
205 }
206 return TRUE;
207 }
208
screenSaverLoop(HINSTANCE,HWND hWindow)209 void screenSaverLoop(HINSTANCE /*hInstance*/, HWND hWindow)
210 {
211 MSG msg;
212 BOOL bGotMsg;
213
214 msg.message = WM_NULL;
215 while ( msg.message != WM_QUIT && msg.message != WM_DESTROY )
216 {
217 bGotMsg = PeekMessage( &msg, NULL, 0, 0, PM_REMOVE );
218 if( bGotMsg )
219 {
220 TranslateMessage( &msg );
221 DispatchMessage( &msg );
222 debugOut("Message: 0x%08X\n", msg.message);
223 }
224 else
225 {
226 debugOut("No Message\n");
227 if (!GetParent(hWindow))
228 {
229 PostQuitMessage(0);
230 }
231 Sleep(100);
232 }
233 }
234 /*
235 while( 1 )
236 {
237 FILE* debugFile;
238 // If GetMessage returns FALSE, it's quitting time.
239 if( !GetMessage( &msg, NULL, 0, 0 ) )
240 {
241 debugFile = ucfopen("LDViewDebug.txt", "a+");
242
243 if (debugFile)
244 {
245 fprintf(debugFile, "EXIT\n");
246 fclose(debugFile);
247 }
248 // Post the quit message to handle it later
249 return;
250 }
251
252 TranslateMessage( &msg );
253 DispatchMessage( &msg );
254 debugFile = ucfopen("LDViewDebug.txt", "a+");
255 if (debugFile)
256 {
257 fprintf(debugFile, "Message: 0x%08X\n", msg.message);
258 fclose(debugFile);
259 }
260 if (msg.message == WM_DESTROY)
261 {
262 debugFile = ucfopen("LDViewDebug.txt", "a+");
263
264 if (debugFile)
265 {
266 fprintf(debugFile, "WM_DESTROY\n");
267 fclose(debugFile);
268 }
269 return;
270 // PostQuitMessage(0);
271 }
272 }
273 */
274 }
275
doPreview(HINSTANCE hInstance,LPCSTR lpCmdLine)276 int doPreview(HINSTANCE hInstance, LPCSTR lpCmdLine)
277 {
278 const char *spot = strchr(lpCmdLine, ' ');
279
280 debugOut("Command line: %s\n", lpCmdLine);
281 if (spot)
282 {
283 long long parentWindowNum;
284
285 spot++;
286 if (sscanf(spot, "%lld", &parentWindowNum) == 1)
287 {
288 HWND hParentWindow = (HWND)parentWindowNum;
289 SSPreview* ssPreview = new SSPreview(hParentWindow, hInstance);
290
291 if (ssPreview->run())
292 {
293 return 0;
294 }
295 else
296 {
297 return 1;
298 }
299 }
300 }
301 return 1;
302 }
303
loadLocalStrings(const char * filename,UINT resourceId,bool replace)304 static void loadLocalStrings(const char *filename, UINT resourceId, bool replace)
305 {
306 HMODULE hModule = (HMODULE)CUIWindow::getLanguageModule();
307 HRSRC hLocalStringsResource = FindResource(hModule,
308 MAKEINTRESOURCE(resourceId),
309 RT_RCDATA);
310 bool done = false;
311
312 if (hLocalStringsResource)
313 {
314 HGLOBAL hLocalStrings = LoadResource(hModule, hLocalStringsResource);
315
316 if (hLocalStrings)
317 {
318 TCByte *data = (TCByte *)LockResource(hLocalStrings);
319
320 if (data)
321 {
322 DWORD length = SizeofResource(hModule, hLocalStringsResource);
323
324 if (length)
325 {
326 //char *localStrings = new char[length + 1];
327
328 //memcpy(localStrings, data, length);
329 //localStrings[length] = 0;
330 TCLocalStrings::setStringTable(data, length, replace);
331 //delete localStrings;
332 done = true;
333 }
334 UnlockResource(hLocalStrings);
335 }
336 }
337 }
338 if (!done)
339 {
340 TCLocalStrings::loadStringTable(filename, replace);
341 }
342 }
343
setupLocalStrings(void)344 static void setupLocalStrings(void)
345 {
346 loadLocalStrings("LDViewMessages.ini", IDR_LOCAL_STRINGS, true);
347 loadLocalStrings("LDExportMessages.ini", IDR_EXPORTER_STRINGS, false);
348 //TCLocalStrings::dumpTable("C:\\Temp\\ST-ASCII.txt", "ASCII");
349 }
350
loadLanguageModule(void)351 static void loadLanguageModule(void)
352 {
353 // This function is something of a hack. We want to force the language
354 // module to load with certain pre-conditions. The first is that prior to
355 // going into this function, the app name was set to LDView. The next is
356 // that we want to change into the LDView directory, so that the
357 // LoadLibrary call will find language modules in that directory, so that
358 // if we're running as a screensaver, it will still find the language
359 // modules.
360 UCCHAR originalPath[1024];
361 DWORD maxPath = COUNT_OF(originalPath);
362 DWORD dirResult = GetCurrentDirectory(maxPath, originalPath);
363 bool dirChange = false;
364
365 if (dirResult > 0 && dirResult <= maxPath)
366 {
367 UCSTR installPath =
368 TCUserDefaults::stringForKeyUC(INSTALL_PATH_4_1_KEY, NULL, false);
369
370 if (installPath)
371 {
372 SetCurrentDirectory(installPath);
373 delete[] installPath;
374 dirChange = true;
375 }
376 }
377 // The following forces CUIWindow to load (and cache) the language module.
378 CUIWindow::getLanguageModule();
379 if (dirChange)
380 {
381 SetCurrentDirectory(originalPath);
382 }
383 }
384
setupUserDefaults(LPCSTR lpCmdLine,bool screenSaver,bool removableDrive)385 static bool setupUserDefaults(
386 LPCSTR lpCmdLine,
387 bool screenSaver,
388 bool removableDrive)
389 {
390 char *appName = "Travis Cobbs/LDView";
391 char *sessionName;
392 bool retValue = true;
393
394 TCUserDefaults::setCommandLine(lpCmdLine);
395 debugToFile = TCUserDefaults::boolForKey("DebugToFile", false, false);
396 std::string haveStdOut =
397 TCUserDefaults::commandLineStringForKey("HaveStdOut");
398 if (!haveStdOut.empty())
399 {
400 if (std::strtol(haveStdOut.c_str(), NULL, 10) != 0)
401 {
402 runningWithConsole();
403 }
404 }
405 // IniFile can be specified on the command line; if so, don't load a
406 // different one.
407 if (removableDrive && !TCUserDefaults::isIniFileSet())
408 {
409 if (!TCUserDefaults::setIniFile("LDView.ini"))
410 {
411 retValue = false;
412 }
413 }
414 TCUserDefaults::setAppName(appName);
415 // The language module needs to be loaded using LDView as the app name. So
416 // if we're running in screensaver mode, we'll take care of changing our
417 // app name after that is done.
418 loadLanguageModule();
419 if (screenSaver)
420 {
421 UCSTR ldrawDir =
422 TCUserDefaults::stringForKeyUC(LDRAWDIR_KEY, NULL, false);
423
424 appName = "Travis Cobbs/LDView Screen Saver";
425 TCUserDefaults::setAppName(appName);
426 if (ldrawDir)
427 {
428 TCUserDefaults::setStringForKey(ldrawDir, LDRAWDIR_KEY, false);
429 delete ldrawDir;
430 }
431 }
432 #ifdef _DEBUG
433 // Set the debug level before selecting a pref set.
434 setDebugLevel((int)TCUserDefaults::longForKey(DEBUG_LEVEL_KEY, 1, false));
435 #endif // _DEBUG
436 sessionName =
437 TCUserDefaults::getSavedSessionNameFromKey(PREFERENCE_SET_KEY);
438 if (sessionName && sessionName[0])
439 {
440 TCUserDefaults::setSessionName(sessionName, NULL, false);
441 }
442 delete sessionName;
443 return retValue;
444 }
445
isRemovableDrive(HINSTANCE hInstance)446 static bool isRemovableDrive(HINSTANCE hInstance)
447 {
448 UCCHAR filename[2048];
449
450 if (GetModuleFileName(hInstance, filename, COUNT_OF(filename)) > 0)
451 {
452 if (isalpha(filename[0]) && filename[1] == ':')
453 {
454 UCCHAR driveRoot[4] = _UC("");
455
456 ucstrncpy(driveRoot, filename, 2);
457 driveRoot[2] = '\\';
458 if (GetDriveType(driveRoot) == DRIVE_REMOVABLE)
459 {
460 return true;
461 }
462 }
463 }
464 return false;
465 }
466
467 //static void Win7JumpListStuff(void)
468 //{
469 // HMODULE hShell32 = LoadLibrary("shell32.dll");
470 //
471 // if (hShell32 != NULL)
472 // {
473 // typedef HRESULT (__stdcall *PSetCurrentProcessExplicitAppUserModelID)(PCWSTR AppID);
474 // PSetCurrentProcessExplicitAppUserModelID
475 // pSetCurrentProcessExplicitAppUserModelID =
476 // (PSetCurrentProcessExplicitAppUserModelID)GetProcAddress(hShell32,
477 // "SetCurrentProcessExplicitAppUserModelID");
478 //
479 // if (pSetCurrentProcessExplicitAppUserModelID != NULL)
480 // {
481 // PCWSTR appID = L"Travis.Cobbs.LDView";
482 // pSetCurrentProcessExplicitAppUserModelID(appID);
483 // ICustomDestinationList *cdl;
484 // // Using CLSID_DestinationList from the header file auto-links
485 // // an incompatible copy of uuid.lib, causing link failure.
486 // // "77f10cf0-3db5-4966-b520-b7c54fd35ed6"
487 // IID CLSID_DestinationList = {
488 // 0x77f10cf0, 0x3db5, 0x4966,
489 // { 0xb5, 0x20, 0xb7, 0xc5, 0x4f, 0xd3, 0x5e, 0xd6 }
490 // };
491 // CoCreateInstance(CLSID_DestinationList,NULL,CLSCTX_INPROC_SERVER,
492 // __uuidof(ICustomDestinationList),(void**)&cdl);
493 // if (cdl != NULL)
494 // {
495 // cdl->SetAppID(appID);
496 // // Recent Items begin list
497 // UINT maxCount = 0;
498 // IObjectArray* oa = 0;
499 // HRESULT hr = cdl->BeginList(&maxCount,__uuidof(IObjectArray),(void**)&oa);
500 //
501 // if (SUCCEEDED(hr) && oa != NULL)
502 // {
503 // // Add known category
504 // hr = cdl->AppendKnownCategory(KDC_RECENT);
505 // oa->Release();
506 // //odl->Release();
507 // //tb->Release();
508 // }
509 // cdl->Release();
510 // }
511 // }
512 // FreeLibrary(hShell32);
513 // }
514 //}
515
516 #ifdef TC_NO_UNICODE
WinMain(HINSTANCE hInstance,HINSTANCE,LPSTR lpCmdLine,int nCmdShow)517 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/,
518 LPSTR lpCmdLine, int nCmdShow)
519 #else // TC_NO_UNICODE
520 int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/,
521 LPWSTR lpCmdLine, int nCmdShow)
522 #endif // !TC_NO_UNICODE
523 {
524 #ifdef _DEBUG
525 // MessageBox(NULL, _UC("Attach a debugger now..."), _UC("Debug"), MB_OK);
526 #endif
527 ModelLoader* modelLoader;
528 bool screenSaver = isScreenSaver();
529 int retValue;
530 STARTUPINFO startupInfo;
531
532 debugOut("Command Line: <<%ls>>\n", lpCmdLine);
533 std::string utf8CmdLine;
534 ucstringtoutf8(utf8CmdLine, lpCmdLine);
535 bool udok = setupUserDefaults(utf8CmdLine.c_str(), screenSaver,
536 isRemovableDrive(hInstance));
537 debugOut("Command Line: <<%ls>>\n", lpCmdLine);
538 #ifdef _DEBUG
539 int _debugFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
540 _debugFlag |= _CRTDBG_LEAK_CHECK_DF;
541 _CrtSetDbgFlag(_debugFlag);
542 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG);
543 if (!haveConsole())
544 {
545 createConsole();
546 }
547 #endif // _DEBUG
548 setupLocalStrings();
549 if (TCUserDefaults::boolForKey(DEBUG_COMMAND_LINE_KEY, false, false))
550 {
551 ucstring message = _UC("Command Line:\n");
552
553 message += lpCmdLine;
554 MessageBox(NULL, message.c_str(), _UC("LDView"), MB_OK);
555 }
556 if (!udok && !TCUserDefaults::longForKey("IniFailureShown", 0, 0))
557 {
558 UCCHAR message[2048];
559 ucstring iniPath;
560 utf8toucstring(iniPath, TCUserDefaults::getIniPath());
561
562 sucprintf(message, COUNT_OF(message),
563 TCLocalStrings::get(_UC("IniFailure")), iniPath.c_str());
564 CUIWindow::messageBoxUC(NULL, message, _UC("LDView"), MB_OK);
565 TCUserDefaults::setLongForKey(1, "IniFailureShown", false);
566 }
567 if (screenSaver)
568 {
569 if (strncasecmp(utf8CmdLine.c_str(), "/p", 2) == 0 ||
570 strncasecmp(utf8CmdLine.c_str(), "-p", 2) == 0 ||
571 strncasecmp(utf8CmdLine.c_str(), "p", 1) == 0)
572 {
573 // preview mode
574 int retValue = doPreview(hInstance, utf8CmdLine.c_str());
575 return retValue;
576 }
577 if (strncasecmp(utf8CmdLine.c_str(), "/c", 2) == 0 ||
578 strncasecmp(utf8CmdLine.c_str(), "-c", 2) == 0 ||
579 strncasecmp(utf8CmdLine.c_str(), "c", 1) == 0 ||
580 utf8CmdLine.empty())
581 {
582 SSConfigure *configure;
583
584 configure = new SSConfigure(hInstance);
585 #ifdef _DEBUG
586 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG);
587 createConsole();
588 #endif // _DEBUG
589 configure->run();
590 // configure mode
591 return 1;
592 }
593 // This shouldn't be necessary, but I've received a report of a whole
594 // bunch of copies of the LDView screensaver running at once. This
595 // might not fix things entirely, but it will at least prevent it
596 // from launching multiple times concurrently.
597 CreateMutex(NULL, FALSE, _UC("LDView Screensaver"));
598 if (GetLastError() == ERROR_ALREADY_EXISTS)
599 {
600 return 0;
601 }
602 }
603 #ifdef _LOG_PERFORMANCE
604 LARGE_INTEGER frequency;
605 if (QueryPerformanceFrequency(&frequency))
606 {
607 debugPrintf("Frequency: %I64d\n", frequency.QuadPart);
608 }
609 #endif // _LOG_PERFORMANCE
610 OleInitialize(NULL);
611
612 //Win7JumpListStuff();
613
614 modelLoader = new ModelLoader(CUIWindow::getLanguageModule(), nCmdShow,
615 screenSaver);
616 retValue = mainLoop();
617 modelLoader->release();
618 return retValue;
619 } // WinMain
620