1 /*
2  *  phd.cpp
3  *  PHD Guiding
4  *
5  *  Created by Craig Stark.
6  *  Copyright (c) 2006-2010 Craig Stark.
7  *  All rights reserved.
8  *
9  *  This source code is distributed under the following "BSD" license
10  *  Redistribution and use in source and binary forms, with or without
11  *  modification, are permitted provided that the following conditions are met:
12  *    Redistributions of source code must retain the above copyright notice,
13  *     this list of conditions and the following disclaimer.
14  *    Redistributions in binary form must reproduce the above copyright notice,
15  *     this list of conditions and the following disclaimer in the
16  *     documentation and/or other materials provided with the distribution.
17  *    Neither the name of Craig Stark, Stark Labs nor the names of its
18  *     contributors may be used to endorse or promote products derived from
19  *     this software without specific prior written permission.
20  *
21  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  *  POSSIBILITY OF SUCH DAMAGE.
32  *
33  */
34 
35 #include "phd.h"
36 
37 #include "phdupdate.h"
38 
39 #include <curl/curl.h>
40 #include <memory>
41 #include <wx/cmdline.h>
42 #include <wx/evtloop.h>
43 #include <wx/snglinst.h>
44 
45 #ifdef  __linux__
46 # include <X11/Xlib.h>
47 #endif // __linux__
48 
49 //#define DEVBUILD
50 
51 // Globals
52 
53 PhdConfig *pConfig = nullptr;
54 Mount *pMount = nullptr;
55 Mount *pSecondaryMount = nullptr;
56 Scope *pPointingSource = nullptr;
57 MyFrame *pFrame = nullptr;
58 GuideCamera *pCamera = nullptr;
59 
60 DebugLog Debug;
61 GuidingLog GuideLog;
62 
63 int XWinSize = 640;
64 int YWinSize = 512;
65 
66 static const wxCmdLineEntryDesc cmdLineDesc[] =
67 {
68     { wxCMD_LINE_OPTION, "i", "instanceNumber", "sets the PHD2 instance number (default = 1)", wxCMD_LINE_VAL_NUMBER, wxCMD_LINE_PARAM_OPTIONAL},
69     { wxCMD_LINE_OPTION, "l", "load", "load settings from file and exit", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL },
70     { wxCMD_LINE_SWITCH, "R", "Reset", "Reset all PHD2 settings to default values" },
71     { wxCMD_LINE_OPTION, "s", "save", "save settings to file and exit", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL },
72     { wxCMD_LINE_NONE }
73 };
74 
75 enum ConfigOp
76 {
77     CONFIG_OP_NONE,
78     CONFIG_OP_SAVE,
79     CONFIG_OP_LOAD,
80 };
81 static ConfigOp s_configOp = CONFIG_OP_NONE;
82 static wxString s_configPath;
83 
84 wxIMPLEMENT_APP(PhdApp);
85 
DisableOSXAppNap()86 static void DisableOSXAppNap()
87 {
88 #ifdef __APPLE__
89     // this is obsolete (2020-02-04) now that we disable AppNap in a
90     // launcher script (run_phd2_macos), but it is harmless so we can
91     // leave it in for a release or two
92 # define  APPKEY "org.openphdguiding.phd2"
93     int major = wxPlatformInfo::Get().GetOSMajorVersion();
94     int minor = wxPlatformInfo::Get().GetOSMinorVersion();
95     if (major > 10 || (major == 10 && minor >= 9))  // Mavericks or later -- deal with App Nap
96     {
97         wxArrayString out, err;
98         wxExecute("defaults read " APPKEY " NSAppSleepDisabled", out, err);
99         if (err.GetCount() > 0 || (out.GetCount() > 0 && out[0].Contains("0"))) // it's not there or disabled
100         {
101             wxExecute("defaults write " APPKEY " NSAppSleepDisabled -bool YES");
102             wxMessageBox("OSX 10.9's App Nap feature causes problems.  Please quit and relaunch PHD to finish disabling App Nap.");
103         }
104     }
105 # undef APPKEY
106 #endif
107 }
108 
109 // ------------------------  Phd App stuff -----------------------------
110 
111 struct ExecFuncThreadEvent;
112 wxDEFINE_EVENT(EXEC_IN_MAIN_THREAD_EVENT, ExecFuncThreadEvent);
113 
114 struct ExecFuncThreadEvent : public wxThreadEvent
115 {
116     std::function<void()> func;
ExecFuncThreadEventExecFuncThreadEvent117     ExecFuncThreadEvent(std::function<void()> func_)
118             : wxThreadEvent(EXEC_IN_MAIN_THREAD_EVENT), func(func_)
119     { }
120 };
121 
PhdApp()122 PhdApp::PhdApp()
123 {
124     m_resetConfig = false;
125     m_instanceNumber = 1;
126 #ifdef  __linux__
127     XInitThreads();
128 #endif // __linux__
129 
130     Bind(EXEC_IN_MAIN_THREAD_EVENT, [](ExecFuncThreadEvent& evt) { evt.func(); });
131 };
132 
HandleRestart()133 void PhdApp::HandleRestart()
134 {
135     // wait until prev instance (parent) terminates
136     while (true)
137     {
138         std::unique_ptr<wxSingleInstanceChecker> si(new wxSingleInstanceChecker(wxString::Format("%s.%ld", GetAppName(), m_instanceNumber)));
139         if (!si->IsAnotherRunning())
140             break;
141         wxMilliSleep(200);
142     }
143 
144     // copy command-line args skipping "restart"
145     wchar_t **targv = new wchar_t*[argc * sizeof(*targv)];
146     targv[0] = wxStrdup(argv[0].wc_str());
147     int src = 2, dst = 1;
148     while (src < argc)
149         targv[dst++] = wxStrdup(argv[src++].wc_str());
150     targv[dst] = 0;
151 
152     // launch a new instance
153     wxExecute(targv, wxEXEC_ASYNC);
154 
155     // exit the helper instance
156     wxExit();
157 }
158 
RestartApp()159 void PhdApp::RestartApp()
160 {
161     // copy command-line args inserting "restart" as the first arg
162     wchar_t **targv = new wchar_t*[(argc + 2) * sizeof(*targv)];
163     targv[0] = wxStrdup(argv[0].wc_str());
164     targv[1] = wxStrdup(_T("restart"));
165     int src = 1, dst = 2;
166     while (src < argc)
167         targv[dst++] = wxStrdup(argv[src++].wc_str());
168     targv[dst] = 0;
169 
170     // launch the restart process
171     wxExecute(targv, wxEXEC_ASYNC);
172 
173     // gracefully exit this instance
174     TerminateApp();
175 }
176 
IdleClosing(wxIdleEvent & evt)177 static void IdleClosing(wxIdleEvent& evt)
178 {
179     Debug.Write("IdleClosing\n");
180 
181     // If a modal dialog box is up then the app will crash if we try to close pFrame,
182     // As a workaround keep exiting any nested event loops until we get back to the
183     // main event loop, then close pFrame
184 
185     wxEventLoopBase *el = wxEventLoopBase::GetActive();
186     if (!el->IsMain())
187     {
188         el->Exit(-1);
189         evt.RequestMore();
190         return;
191     }
192 
193     wxGetApp().Unbind(wxEVT_IDLE, &IdleClosing);
194     pFrame->Close(true /*force*/);
195 }
196 
TerminateApp()197 void PhdApp::TerminateApp()
198 {
199     // The wxEVT_CLOSE_WINDOW message may not be processed if phd2 is sitting idle
200     // when the client invokes shutdown. As a workaround pump some timer event messages to
201     // keep the event loop from stalling and ensure that the wxEVT_CLOSE_WINDOW is processed.
202 
203     (new wxTimer(&wxGetApp()))->Start(20); // this object leaks but we don't care
204 
205     wxGetApp().Bind(wxEVT_IDLE, &IdleClosing);
206     wxGetApp().WakeUpIdle();
207 }
208 
209 #ifdef __WINDOWS__
210 # if wxCHECK_VERSION(3, 1, 0)
211 #  pragma message ("FIXME: obsolete code -- remove and use wxGetOsDescription()")
212 # endif
213 #include <wx/dynlib.h>
wx_3_1_wxGetWindowsVersionInfo()214 static OSVERSIONINFOEXW wx_3_1_wxGetWindowsVersionInfo()
215 {
216     OSVERSIONINFOEXW info;
217     memset(&info, 0, sizeof(info));
218     info.dwOSVersionInfoSize = sizeof(info);
219 
220     // The simplest way to get the version is to call the kernel
221     // RtlGetVersion() directly, if it is available.
222 #if wxUSE_DYNLIB_CLASS
223     wxDynamicLibrary dllNtDll;
224     if ( dllNtDll.Load(wxS("ntdll.dll"), wxDL_VERBATIM | wxDL_QUIET) )
225     {
226         typedef LONG /* NTSTATUS */ (WINAPI *RtlGetVersion_t)(OSVERSIONINFOEXW*);
227 
228         RtlGetVersion_t wxDL_INIT_FUNC(pfn, RtlGetVersion, dllNtDll);
229         if ( pfnRtlGetVersion &&
230              (pfnRtlGetVersion(&info) == 0 /* STATUS_SUCCESS */) )
231         {
232             return info;
233         }
234     }
235 #endif // wxUSE_DYNLIB_CLASS
236 
237 #ifdef __VISUALC__
238 #pragma warning(push)
239 #pragma warning(disable:4996) // 'xxx': was declared deprecated
240 #endif
241 
242     if ( !::GetVersionExW(reinterpret_cast<OSVERSIONINFOW *>(&info)) )
243     {
244         // This really shouldn't ever happen.
245         wxFAIL_MSG( "GetVersionEx() unexpectedly failed" );
246     }
247 
248 #ifdef __VISUALC__
249 #pragma warning(pop)
250 #endif
251 
252     return info;
253 }
254 
wxIsWindowsServer()255 static int wxIsWindowsServer()
256 {
257 #ifdef VER_NT_WORKSTATION
258     switch ( wx_3_1_wxGetWindowsVersionInfo().wProductType )
259     {
260         case VER_NT_WORKSTATION:
261             return false;
262 
263         case VER_NT_SERVER:
264         case VER_NT_DOMAIN_CONTROLLER:
265             return true;
266     }
267 #endif // VER_NT_WORKSTATION
268 
269     return -1;
270 }
271 
wx_3_1_wxGetOsDescription()272 static wxString wx_3_1_wxGetOsDescription()
273 {
274     wxString str;
275 
276     const OSVERSIONINFOEXW info = wx_3_1_wxGetWindowsVersionInfo();
277     switch (info.dwPlatformId)
278     {
279     case VER_PLATFORM_WIN32s:
280         str = _("Win32s on Windows 3.1");
281         break;
282 
283     case VER_PLATFORM_WIN32_WINDOWS:
284         switch (info.dwMinorVersion)
285         {
286         case 0:
287             if (info.szCSDVersion[1] == 'B' ||
288                 info.szCSDVersion[1] == 'C')
289             {
290                 str = _("Windows 95 OSR2");
291             }
292             else
293             {
294                 str = _("Windows 95");
295             }
296             break;
297         case 10:
298             if (info.szCSDVersion[1] == 'B' ||
299                 info.szCSDVersion[1] == 'C')
300             {
301                 str = _("Windows 98 SE");
302             }
303             else
304             {
305                 str = _("Windows 98");
306             }
307             break;
308         case 90:
309             str = _("Windows ME");
310             break;
311         default:
312             str.Printf(_("Windows 9x (%d.%d)"),
313                 info.dwMajorVersion,
314                 info.dwMinorVersion);
315             break;
316         }
317         if (!wxIsEmpty(info.szCSDVersion))
318         {
319             str << wxT(" (") << info.szCSDVersion << wxT(')');
320         }
321         break;
322 
323     case VER_PLATFORM_WIN32_NT:
324         switch (info.dwMajorVersion)
325         {
326         case 5:
327             switch (info.dwMinorVersion)
328             {
329             case 0:
330                 str = _("Windows 2000");
331                 break;
332 
333             case 2:
334                 // we can't distinguish between XP 64 and 2003
335                 // as they both are 5.2, so examine the product
336                 // type to resolve this ambiguity
337                 if (wxIsWindowsServer() == 1)
338                 {
339                     str = _("Windows Server 2003");
340                     break;
341                 }
342                 //else: must be XP, fall through
343 
344             case 1:
345                 str = _("Windows XP");
346                 break;
347             }
348             break;
349 
350         case 6:
351             switch (info.dwMinorVersion)
352             {
353             case 0:
354                 str = wxIsWindowsServer() == 1
355                     ? _("Windows Server 2008")
356                     : _("Windows Vista");
357                 break;
358 
359             case 1:
360                 str = wxIsWindowsServer() == 1
361                     ? _("Windows Server 2008 R2")
362                     : _("Windows 7");
363                 break;
364 
365             case 2:
366                 str = wxIsWindowsServer() == 1
367                     ? _("Windows Server 2012")
368                     : _("Windows 8");
369                 break;
370 
371             case 3:
372                 str = wxIsWindowsServer() == 1
373                     ? _("Windows Server 2012 R2")
374                     : _("Windows 8.1");
375                 break;
376             }
377             break;
378 
379         case 10:
380             str = wxIsWindowsServer() == 1
381                 ? _("Windows Server 2016")
382                 : _("Windows 10");
383             break;
384         }
385 
386         if (str.empty())
387         {
388             str.Printf(_("Windows NT %lu.%lu"),
389                 info.dwMajorVersion,
390                 info.dwMinorVersion);
391         }
392 
393         str << wxT(" (")
394             << wxString::Format(_("build %lu"), info.dwBuildNumber);
395         if (!wxIsEmpty(info.szCSDVersion))
396         {
397             str << wxT(", ") << info.szCSDVersion;
398         }
399         str << wxT(')');
400 
401         if (wxIsPlatform64Bit())
402             str << _(", 64-bit edition");
403         break;
404     }
405 
406     return str;
407 }
408 #endif // __WINDOWS__
409 
GetOsDescription()410 static wxString GetOsDescription()
411 {
412 #ifdef __WINDOWS__
413     // wxGetOsDescription in wxWidgets versions prior to 3.1.0 on Windows uses GetVersionEx which does not
414     // report versions past Windows 8.1
415     return wx_3_1_wxGetOsDescription();
416 #else
417     return wxGetOsDescription();
418 #endif
419 }
420 
OpenLogs(bool rollover)421 static void OpenLogs(bool rollover)
422 {
423     bool debugEnabled = rollover ? Debug.IsEnabled() : true;
424     bool forceDebugOpen = rollover ? true : false;
425     Debug.InitDebugLog(debugEnabled, forceDebugOpen);
426 
427     Debug.Write(wxString::Format("PHD2 version %s %s execution with:\n", FULLVER,
428                                  rollover ? "continues" : "begins"));
429     Debug.Write(wxString::Format("   %s\n", GetOsDescription()));
430 #if defined(__linux__)
431     Debug.Write(wxString::Format("   %s\n", wxGetLinuxDistributionInfo().Description));
432 #endif
433     Debug.Write(wxString::Format("   %s\n", wxVERSION_STRING));
434     float dummy;
435     Debug.Write(wxString::Format("   cfitsio %.2lf\n", ffvers(&dummy)));
436 #if defined(CV_VERSION)
437     Debug.Write(wxString::Format("   opencv %s\n", CV_VERSION));
438 #endif
439 
440     if (rollover)
441     {
442         bool guideEnabled = GuideLog.IsEnabled();
443         GuideLog.CloseGuideLog();
444         GuideLog.EnableLogging(guideEnabled);
445     }
446     else
447         GuideLog.EnableLogging(true);
448 }
449 
450 struct LogToStderr
451 {
452     wxLog *m_prev;
LogToStderrLogToStderr453     LogToStderr()
454     {
455         m_prev = wxLog::SetActiveTarget(new wxLogStderr());
456     }
~LogToStderrLogToStderr457     ~LogToStderr()
458     {
459         delete wxLog::SetActiveTarget(m_prev);
460     }
461 };
462 
463 // A log class for duplicating wxWidgets error messages to the debug log
464 //
465 struct EarlyLogger : public wxLog
466 {
467     bool m_closed;
468     wxLog *m_prev;
469     wxString m_buf;
EarlyLoggerEarlyLogger470     EarlyLogger()
471         : m_closed(false)
472     {
473         wxASSERT(wxThread::IsMain());
474         m_prev = wxLog::SetActiveTarget(this);
475         DisableTimestamp();
476     }
~EarlyLoggerEarlyLogger477     ~EarlyLogger()
478     {
479         Close();
480     }
CloseEarlyLogger481     void Close()
482     {
483         if (m_closed)
484             return;
485 
486         wxLog::SetActiveTarget(m_prev);
487         m_prev = nullptr;
488 
489         if (!m_buf.empty())
490         {
491             if (Debug.IsOpened())
492                 Debug.Write(wxString::Format("wx error: %s\n", m_buf));
493 
494             wxLogError(m_buf);
495 
496             m_buf.clear();
497         }
498 
499         m_closed = true;
500     }
DoLogTextEarlyLogger501     void DoLogText(const wxString& msg) override
502     {
503         if (!m_buf.empty() && *m_buf.rbegin() != '\n')
504             m_buf += '\n';
505         m_buf += msg;
506     }
507 };
508 
OnInit()509 bool PhdApp::OnInit()
510 {
511 #ifdef __APPLE__
512     // for newer versions of OSX the app will hang if the wx log code
513     // tries to display a message box in OnOnit.  As a workaround send
514     // wxLog output to stderr instead
515     LogToStderr _logstderr;
516 #endif
517 
518     // capture wx error messages until the debug log has been opened
519     EarlyLogger logger;
520 
521     if (argc > 1 && argv[1] == _T("restart"))
522         HandleRestart(); // exits
523 
524     if (!wxApp::OnInit())
525     {
526         return false;
527     }
528 
529 #if defined(__WINDOWS__)
530     // on MSW, do not strip off the Debug/ and Release/ build subdirs
531     // so that GetResourcesDir() is the same as the location of phd2.exe
532     wxStandardPaths::Get().DontIgnoreAppSubDir();
533 #endif
534 
535     m_instanceChecker = new wxSingleInstanceChecker(wxString::Format("%s.%ld", GetAppName(), m_instanceNumber));
536     if (m_instanceChecker->IsAnotherRunning())
537     {
538         wxLogError(wxString::Format(_("PHD2 instance %ld is already running. Use the "
539             "-i INSTANCE_NUM command-line option to start a different instance."), m_instanceNumber));
540         delete m_instanceChecker; // OnExit() won't be called if we return false
541         m_instanceChecker = 0;
542         return false;
543     }
544 
545 #ifndef DEBUG
546 # if (wxMAJOR_VERSION > 2 || wxMINOR_VERSION > 8)
547     wxDisableAsserts();
548 # endif
549 #endif
550 
551     SetVendorName(_T("StarkLabs"));
552     // use SetAppName() to ensure the local data directory is found even if the name of the executable is changed
553 #ifdef __APPLE__
554     SetAppName(_T("PHD2"));
555 #else
556     SetAppName(_T("phd2"));
557 #endif
558     pConfig = new PhdConfig(m_instanceNumber);
559 
560     if (s_configOp == CONFIG_OP_LOAD)
561     {
562         bool err = pConfig->RestoreAll(s_configPath);
563         ::exit(err ? 1 : 0);
564         return false;
565     }
566     else if (s_configOp == CONFIG_OP_SAVE)
567     {
568         bool err = pConfig->SaveAll(s_configPath);
569         ::exit(err ? 1 : 0);
570         return false;
571     }
572 
573     m_logFileTime = DebugLog::GetLogFileTime();     // GetLogFileTime implements grouping by imaging-day, the 24-hour period starting at 09:00 am local time
574     OpenLogs(false /* not for rollover */);
575 
576     logger.Close(); // writes any deferrred error messages to the debug log
577 
578 #if defined(__WINDOWS__)
579     HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
580     Debug.Write(wxString::Format("CoInitializeEx returns %x\n", hr));
581 #endif
582 
583     DisableOSXAppNap();
584 
585 #if defined(__WINDOWS__)
586     // use per-thread locales on windows to that INDI can set the locale in its thread without side effects
587     _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
588 #endif
589 
590     curl_global_init(CURL_GLOBAL_DEFAULT);
591 
592     if (m_resetConfig)
593     {
594         ResetConfiguration();
595     }
596 
597 #ifdef __linux__
598     // on Linux look in the build tree first, otherwise use the system location
599     m_resourcesDir = wxFileName(wxStandardPaths::Get().GetExecutablePath()).GetPath() + "/share/phd2";
600     if (!wxDirExists(m_resourcesDir))
601         m_resourcesDir = wxStandardPaths::Get().GetResourcesDir();
602 #else
603     m_resourcesDir = wxStandardPaths::Get().GetResourcesDir();
604 #endif
605 
606     wxString ldir = GetLocalesDir();
607     Debug.Write(wxString::Format("locale: using dir %s exists=%d\n", ldir, wxDirExists(ldir)));
608     wxLocale::AddCatalogLookupPathPrefix(ldir);
609 
610     int langid = pConfig->Global.GetInt("/wxLanguage", wxLANGUAGE_DEFAULT);
611     bool ok = m_locale.Init(langid);
612     Debug.Write(wxString::Format("locale: initialized with lang id %d (r=%d)\n", langid, ok));
613     if (!m_locale.AddCatalog(PHD_MESSAGES_CATALOG))
614     {
615         Debug.Write(wxString::Format("locale: AddCatalog(%s) failed\n", PHD_MESSAGES_CATALOG));
616     }
617     wxSetlocale(LC_NUMERIC, "C");
618 
619     wxTranslations::Get()->SetLanguage((wxLanguage)langid);
620     Debug.Write(wxString::Format("locale: wxTranslations language set to %d\n", langid));
621 
622     Debug.RemoveOldFiles();
623     GuideLog.RemoveOldFiles();
624 
625     pConfig->InitializeProfile();
626 
627     PhdController::OnAppInit();
628 
629     ImageLogger::Init();
630 
631     wxImage::AddHandler(new wxJPEGHandler);
632     wxImage::AddHandler(new wxPNGHandler);
633 
634     pFrame = new MyFrame();
635 
636     pFrame->Show(true);
637 
638     if (pConfig->IsNewInstance() || (pConfig->NumProfiles() == 1 && pFrame->pGearDialog->IsEmptyProfile()))
639     {
640         pFrame->pGearDialog->ShowProfileWizard();               // First-light version of profile wizard
641     }
642 
643     PHD2Updater::InitUpdater();
644 
645     return true;
646 }
647 
OnExit()648 int PhdApp::OnExit()
649 {
650     assert(!pMount);
651     assert(!pSecondaryMount);
652     assert(!pCamera);
653 
654     ImageLogger::Destroy();
655 
656     PhdController::OnAppExit();
657 
658     delete pConfig;
659     pConfig = nullptr;
660 
661     curl_global_cleanup();
662 
663     delete m_instanceChecker;
664     m_instanceChecker = nullptr;
665 
666     return wxApp::OnExit();
667 }
668 
OnInitCmdLine(wxCmdLineParser & parser)669 void PhdApp::OnInitCmdLine(wxCmdLineParser& parser)
670 {
671     parser.SetDesc(cmdLineDesc);
672     parser.SetSwitchChars(wxT("-"));
673 }
674 
OnCmdLineParsed(wxCmdLineParser & parser)675 bool PhdApp::OnCmdLineParsed(wxCmdLineParser& parser)
676 {
677     bool bReturn = true;
678 
679     parser.Found("i", &m_instanceNumber);
680 
681     if (parser.Found("l", &s_configPath))
682         s_configOp = CONFIG_OP_LOAD;
683     if (parser.Found("s", &s_configPath))
684         s_configOp = CONFIG_OP_SAVE;
685 
686     m_resetConfig = parser.Found("R");
687 
688     return bReturn;
689 }
690 
Yield(bool onlyIfNeeded)691 bool PhdApp::Yield(bool onlyIfNeeded)
692 {
693     bool bReturn = !onlyIfNeeded;
694 
695     if (wxThread::IsMain())
696     {
697         bReturn = wxApp::Yield(onlyIfNeeded);
698     }
699 
700     return bReturn;
701 }
702 
ExecInMainThread(std::function<void ()> func)703 void PhdApp::ExecInMainThread(std::function<void()> func)
704 {
705     if (wxThread::IsMain())
706     {
707         func();
708     }
709     else
710     {
711         wxQueueEvent(&wxGetApp(), new ExecFuncThreadEvent(func));
712     }
713 }
714 
GetLocalesDir() const715 wxString PhdApp::GetLocalesDir() const
716 {
717     return m_resourcesDir + PATHSEPSTR + _T("locale");
718 }
719 
720 // get the imaging date for a given date-time
721 //    log files started after midnight belong to the prior imaging day
722 //    imaging day rolls over at 9am
ImagingDay(const wxDateTime & dt)723 wxDateTime PhdApp::ImagingDay(const wxDateTime& dt)
724 {
725     if (dt.GetHour() >= 9)
726         return dt.GetDateOnly();
727     // midnight .. 9am belongs to previous day
728     static wxDateSpan ONE_DAY(0 /* years */, 0 /* months */, 0 /* weeks */, 1 /* days */);
729     return dt.GetDateOnly().Subtract(ONE_DAY);
730 }
731 
IsSameImagingDay(const wxDateTime & a,const wxDateTime & b)732 bool PhdApp::IsSameImagingDay(const wxDateTime& a, const wxDateTime& b)
733 {
734     return ImagingDay(a).IsSameDate(ImagingDay(b));
735 }
736 
CheckLogRollover()737 void PhdApp::CheckLogRollover()
738 {
739     wxDateTime now = wxDateTime::Now();
740     if (IsSameImagingDay(m_logFileTime, now))
741         return;
742 
743     m_logFileTime = now;
744     OpenLogs(true /* rollover */);
745 }
746 
UserAgent() const747 wxString PhdApp::UserAgent() const
748 {
749     return _T("phd2/") FULLVER _T(" (") PHD_OSNAME _T(")");
750 }
751 
ResetConfiguration()752 void PhdApp::ResetConfiguration()
753 {
754     for (unsigned int i = 0; i < pConfig->NumProfiles(); i++)
755         MyFrame::DeleteDarkLibraryFiles(i);
756 
757     pConfig->DeleteAll();
758 }
759