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