OpenCPN Partial API docs
Loading...
Searching...
No Matches
ocpn_app.cpp
1/***************************************************************************
2 *
3 * Project: OpenCPN
4 * Purpose: OpenCPN Main wxWidgets Program
5 * Author: David Register
6 *
7 ***************************************************************************
8 * Copyright (C) 2010 by David S. Register *
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 * This program is distributed in the hope that it will be useful, *
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18 * GNU General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU General Public License *
21 * along with this program; if not, write to the *
22 * Free Software Foundation, Inc., *
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
24 **************************************************************************/
25#include "config.h"
26
27#ifdef __MINGW32__
28#undef IPV6STRICT // mingw FTBS fix: missing struct ip_mreq
29#include <windows.h>
30#endif
31
32#include <wx/wxprec.h>
33
34#ifndef WX_PRECOMP
35#include <wx/wx.h>
36#endif // precompiled headers
37#ifdef __WXMSW__
38// #include "c:\\Program Files\\visual leak detector\\include\\vld.h"
39#endif
40
41#include <algorithm>
42#include <limits.h>
43#include <memory>
44#include <thread>
45
46#ifdef __WXMSW__
47#include <math.h>
48#include <psapi.h>
49#include <stdlib.h>
50#include <time.h>
51#endif
52
53#ifndef __WXMSW__
54#include <setjmp.h>
55#include <signal.h>
56#endif
57
58#ifdef OCPN_HAVE_X11
59#include <X11/Xatom.h>
60#include <X11/Xlib.h>
61#endif
62
63#if (defined(OCPN_GHC_FILESYSTEM) || \
64 (defined(__clang_major__) && (__clang_major__ < 15)))
65// MacOS 1.13
66#include <ghc/filesystem.hpp>
67namespace fs = ghc::filesystem;
68#else
69#include <filesystem>
70#include <utility>
71namespace fs = std::filesystem;
72#endif
73
74using namespace std::literals::chrono_literals;
75
76#include <wx/apptrait.h>
77#include <wx/arrimpl.cpp>
78#include <wx/artprov.h>
79#include <wx/aui/aui.h>
80#include <wx/clrpicker.h>
81#include <wx/cmdline.h>
82#include <wx/dialog.h>
83#include <wx/dialog.h>
84#include <wx/dir.h>
85#include <wx/display.h>
86#include <wx/dynlib.h>
87#include <wx/image.h>
88#include <wx/intl.h>
89#include <wx/ipc.h>
90#include <wx/jsonreader.h>
91#include <wx/listctrl.h>
92#include <wx/power.h>
93#include <wx/printdlg.h>
94#include <wx/print.h>
95#include <wx/progdlg.h>
96#include <wx/settings.h>
97#include <wx/stdpaths.h>
98#include <wx/tokenzr.h>
99
100#include "model/ais_decoder.h"
101#include "model/ais_state_vars.h"
102#include "model/certificates.h"
103#include "model/cmdline.h"
104#include "model/comm_bridge.h"
105#include "model/comm_n0183_output.h"
106#include "model/comm_vars.h"
107#include "model/config_vars.h"
108#include "model/gui_vars.h"
109#include "model/instance_check.h"
110#include "model/local_api.h"
111#include "model/logger.h"
112#include "model/mdns_query.h"
113#include "model/mdns_service.h"
114#include "model/multiplexer.h"
115#include "model/navobj_db.h"
116#include "model/nav_object_database.h"
117#include "model/navutil_base.h"
119#include "model/own_ship.h"
120#include "model/plugin_handler.h"
121#include "model/route.h"
122#include "model/routeman.h"
123#include "model/select.h"
124#include "model/track.h"
125
126#include "about_frame_impl.h"
127#include "about.h"
128#include "ais_info_gui.h"
129#include "ais_target_alert_dlg.h"
130#include "ais_target_list_dlg.h"
131#include "ais_target_query_dlg.h"
132#include "canvas_config.h"
133#include "chartdb.h"
134#include "chcanv.h"
135#include "cm93.h"
136#include "concanv.h"
137#include "config.h"
138#include "config_mgr.h"
139#include "DetailSlider.h"
140#include "dychart.h"
141#include "FontMgr.h"
142#include "gdal/cpl_csv.h"
143#include "glTexCache.h"
144#include "GoToPositionDialog.h"
145#include "Layer.h"
146#include "MarkInfo.h"
147#include "navutil.h"
148#include "observable.h"
149#include "ocpn_app.h"
150#include "OCPN_AUIManager.h"
151#include "ocpn_frame.h"
152#include "OCPNPlatform.h"
153#include "options.h"
154#include "rest_server_gui.h"
155#include "route_ctx_factory.h"
156#include "routemanagerdialog.h"
157#include "routeman_gui.h"
158#include "RoutePropDlgImpl.h"
159#include "s52plib.h"
160#include "s57chart.h"
161#include "S57QueryDialog.h"
162#include "safe_mode_gui.h"
163#include "SoundFactory.h"
164#include "styles.h"
165#include "tcmgr.h"
166#include "thumbwin.h"
167#include "TrackPropDlg.h"
168#include "udev_rule_mgr.h"
169
170#ifdef ocpnUSE_GL
171#include "glChartCanvas.h"
172#endif
173
174#ifdef __WXOSX__
175#include "model/macutils.h"
176#endif
177
178#ifdef __WXMSW__
179#include "model/garmin_protocol_mgr.h" // Used for port probing on Windows
180void RedirectIOToConsole();
181#endif
182
183#if defined(__WXMSW__) && defined(__MSVC__LEAK)
184#include "Stackwalker.h"
185#endif
186
187#ifdef LINUX_CRASHRPT
188#include "crashprint.h"
189#endif
190
191#ifdef __ANDROID__
192#include "androidUTIL.h"
193#else
194#include "serial/serial.h"
195#endif
196#include "wiz_ui.h"
197
198const char *const kUsage =
199 R"(Usage:
200 opencpn -h | --help
201 opencpn [-p] [-f] [-G] [-g] [-P] [-l <str>] [-u <num>] [-U] [-s] [GPX file ...]
202 opencpn --remote [-R] | -q] | -e] |-o <str>]
203
204Options for starting opencpn
205
206 -c, --configdir=<dirpath> Use alternative configuration directory.
207 -p, --portable Run in portable mode.
208 -f, --fullscreen Switch to full screen mode on start.
209 -G, --no_opengl Disable OpenGL video acceleration. This setting will
210 be remembered.
211 -g, --rebuild_gl_raster_cache Rebuild OpenGL raster cache on start.
212 -D, --rebuild_chart_db Rescan chart directories and rebuild the chart database
213 -P, --parse_all_enc Convert all S-57 charts to OpenCPN's internal format on start.
214 -l, --loglevel=<str> Amount of logging: error, warning, message, info, debug or trace
215 -u, --unit_test_1=<num> Display a slideshow of <num> charts and then exit.
216 Zero or negative <num> specifies no limit.
217 -U, --unit_test_2
218 -s, --safe_mode Run without plugins, opengl and other "dangerous" stuff
219 -W, --config_wizard Start with initial configuration wizard
220
221Options manipulating already started opencpn
222 -r, --remote Execute commands on already running instance
223 -R, --raise Make running OpenCPN visible if hidden
224 -q, --quit Terminate already running opencpn
225 -e, --get_rest_endpoint Print rest server endpoint and exit.
226 -o, --open=<GPX file> Open file in running opencpn
227
228Arguments:
229 GPX file GPX-formatted file with waypoints or routes.
230)";
231
232// comm event definitions
233wxDEFINE_EVENT(EVT_N2K_129029, wxCommandEvent);
234wxDEFINE_EVENT(EVT_N2K_129026, wxCommandEvent);
235
236wxDEFINE_EVENT(EVT_N0183_RMC, wxCommandEvent);
237wxDEFINE_EVENT(EVT_N0183_HDT, wxCommandEvent);
238wxDEFINE_EVENT(EVT_N0183_HDG, wxCommandEvent);
239wxDEFINE_EVENT(EVT_N0183_HDM, wxCommandEvent);
240wxDEFINE_EVENT(EVT_N0183_VTG, wxCommandEvent);
241wxDEFINE_EVENT(EVT_N0183_GSV, wxCommandEvent);
242wxDEFINE_EVENT(EVT_N0183_GGA, wxCommandEvent);
243wxDEFINE_EVENT(EVT_N0183_GLL, wxCommandEvent);
244wxDEFINE_EVENT(EVT_N0183_AIVDO, wxCommandEvent);
245
246//------------------------------------------------------------------------------
247// Fwd Declarations
248//------------------------------------------------------------------------------
249
250//------------------------------------------------------------------------------
251// Static variable definition
252//------------------------------------------------------------------------------
253
254WX_DEFINE_OBJARRAY(ArrayOfCDI);
255
256bool g_bFirstRun;
257bool g_bUpgradeInProcess;
258
259bool g_bPauseTest;
260
261// Files specified on the command line, if any.
262
263LayerList *pLayerList;
264
265bool g_bshowToolbar = true;
266bool g_bexpert = true;
267
268wxString ChartListFileName;
269wxString gDefaultWorldMapLocation;
270wxString *pInit_Chart_Dir;
271wxString g_csv_locn;
272wxString g_VisibleLayers;
273wxString g_InvisibleLayers;
274wxString g_VisiNameinLayers;
275wxString g_InVisiNameinLayers;
276
277bool g_bcompression_wait;
278int g_FlushNavobjChangesTimeout;
279
280int user_user_id;
281int file_user_id;
282
283int quitflag;
284int g_tick = 0;
285int g_mem_total, g_mem_initial;
286
287static unsigned int malloc_max;
288
289wxDateTime g_start_time;
290wxDateTime g_loglast_time;
291static OcpnSound *_bells_sounds[] = {SoundFactory(), SoundFactory()};
292std::vector<OcpnSound *> bells_sound(_bells_sounds, _bells_sounds + 2);
293
294bool g_bCruising;
295
296bool g_bTransparentToolbar;
297bool g_bTransparentToolbarInOpenGLOK;
298
299wxArrayPtrVoid *UserColourHashTableArray;
300wxColorHashMap *pcurrent_user_color_hash;
301
302bool bVelocityValid;
303
304int gHDx_Watchdog;
305
306bool g_bDebugS57;
307
308int g_ChartUpdatePeriod;
309
310float g_MarkScaleFactorExp;
311int g_last_ChartScaleFactor;
312
313bool g_bShowTide;
314bool g_bShowCurrent;
315
316s57RegistrarMgr *m_pRegistrarMan;
317
318#ifdef __WXOSX__
319#include "model/macutils.h"
320#endif
321
322// begin rms
323#ifdef __WXOSX__
324#ifdef __WXMSW__
325#ifdef USE_GLU_TESS
326#ifdef USE_GLU_DLL
327// end rms
328extern bool s_glu_dll_ready;
329extern HINSTANCE s_hGLU_DLL; // Handle to DLL
330#endif
331#endif
332#endif
333#endif
334
335AisInfoGui *g_pAISGUI;
336
337int gpIDXn;
338long gStart_LMT_Offset;
339
340wxArrayString *pMessageOnceArray;
341
342bool g_bGDAL_Debug;
343bool g_fog_overzoom;
344double g_overzoom_emphasis_base;
345bool g_oz_vector_scale;
346bool g_bDebugGPSD;
347std::vector<std::string> TideCurrentDataSet;
348
349int options_lastPage = 0;
350int options_subpage = 0;
351
352wxPoint options_lastWindowPos(0, 0);
353wxSize options_lastWindowSize(0, 0);
354
355bool g_bSleep;
356
357int osMajor, osMinor;
358
359bool GetMemoryStatus(int *mem_total, int *mem_used);
360bool g_bHasHwClock;
361bool g_bTrackActive;
362bool g_bDeferredStartTrack;
363bool g_bUseGreenShip;
364//
365int g_NeedDBUpdate; // 0 - No update needed, 1 - Update needed because there is
366 // no chart database, inform user, 2 - Start update right
367 // away
368
369AboutFrameImpl *g_pAboutDlg;
370
371#if wxUSE_XLOCALE || !wxCHECK_VERSION(3, 0, 0)
372wxLocale *plocale_def_lang = 0;
373#endif
374
384int g_BSBImgDebug;
385
386int g_AisTargetList_count;
387bool g_bAisTargetList_autosort;
388
389wxAuiDefaultDockArt *g_pauidockart;
390int g_GPU_MemSize;
391
392// Values returned from WMM_PI for variation computation request.
393// Initialize to invalid so we don't use it if WMM hasn't updated yet
394double gQueryVar = 361.0;
395
396char bells_sound_file_name[2][12] = {"1bells.wav", "2bells.wav"};
397
398int portaudio_initialized;
399
400char nmea_tick_chars[] = {'|', '/', '-', '\\', '|', '/', '-', '\\'};
401
402int g_sticky_projection;
403
404int n_NavMessageShown;
405wxString g_config_version_string;
406
420bool b_inCompressAllCharts;
421
422wxArrayString g_locale_catalog_array;
423bool b_reloadForPlugins;
424bool g_btrackContinuous;
425
426bool g_bmasterToolbarFull = true;
427
428int g_AndroidVersionCode;
429
430int g_memUsed;
431
432WX_DEFINE_ARRAY_PTR(ChartCanvas *, arrayofCanvasPtr);
433
434arrayofCanvasPtr g_canvasArray;
435wxString g_lastAppliedTemplateGUID;
436
437bool b_inCloseWindow;
438bool g_disable_main_toolbar;
439bool g_declutter_anchorage;
440bool g_bhide_route_console;
441
442#ifdef LINUX_CRASHRPT
443wxCrashPrint g_crashprint;
444#endif
445
446#ifndef __WXMSW__
447sigjmp_buf env; // the context saved by sigsetjmp();
448#endif
449
450// {2C9C45C2-8E7D-4C08-A12D-816BBAE722C0}
451#ifdef __WXMSW__
452DEFINE_GUID(GARMIN_DETECT_GUID, 0x2c9c45c2L, 0x8e7d, 0x4c08, 0xa1, 0x2d, 0x81,
453 0x6b, 0xba, 0xe7, 0x22, 0xc0);
454#endif
455
456#ifdef __VISUALC__
457#include <wx/msw/msvcrt.h>
458#endif
459
460#if !defined(NAN)
461static const long long lNaN = 0xfff8000000000000;
462#define NAN (*(double *)&lNaN)
463#endif
464
465// Some static helpers
466void appendOSDirSlash(wxString *pString);
467
468void InitializeUserColors(void);
469void DeInitializeUserColors(void);
470void SetSystemColors(ColorScheme cs);
471
472static bool LoadAllPlugIns(bool load_enabled) {
473 g_Platform->ShowBusySpinner();
474 bool b = PluginLoader::GetInstance()->LoadAllPlugIns(load_enabled);
475 g_Platform->HideBusySpinner();
476 return b;
477}
478
479//------------------------------------------------------------------------------
480// PNG Icon resources
481//------------------------------------------------------------------------------
482
483#if defined(__WXGTK__) || defined(__WXQT__)
484#include "bitmaps/opencpn.xpm"
485#endif
486
487wxString newPrivateFileName(wxString, const char *name,
488 [[maybe_unused]] const char *windowsName) {
489 wxString fname = wxString::FromUTF8(name);
490 wxString filePathAndName;
491
492 filePathAndName = g_Platform->GetPrivateDataDir();
493 if (filePathAndName.Last() != wxFileName::GetPathSeparator())
494 filePathAndName.Append(wxFileName::GetPathSeparator());
495
496#ifdef __WXMSW__
497 wxString fwname = wxString::FromUTF8(windowsName);
498 filePathAndName.Append(fwname);
499#else
500 filePathAndName.Append(fname);
501#endif
502
503 return filePathAndName;
504}
505
506// `Main program` equivalent, creating windows and returning main app frame
507//------------------------------------------------------------------------------
508// MyApp
509//------------------------------------------------------------------------------
510IMPLEMENT_APP(MyApp)
511
512BEGIN_EVENT_TABLE(MyApp, wxApp)
513EVT_ACTIVATE_APP(MyApp::OnActivateApp)
514END_EVENT_TABLE()
515
516static void ActivateRoute(const std::string &guid) {
517 Route *route = g_pRouteMan->FindRouteByGUID(guid);
518 if (!route) {
519 wxLogMessage("Cannot activate guid: no such route");
520 return;
521 }
522 if (g_pRouteMan->GetpActiveRoute()) g_pRouteMan->DeactivateRoute();
523 // If this is an auto-created MOB route, always select the second point
524 // (the MOB)
525 // as the destination.
526 RoutePoint *point;
527 if (wxNOT_FOUND == route->m_RouteNameString.Find("MOB")) {
528 point = g_pRouteMan->FindBestActivatePoint(route, gLat, gLon, gCog, gSog);
529 } else {
530 point = route->GetPoint(2);
531 }
532 g_pRouteMan->ActivateRoute(route, point);
533 if (g_pRouteMan) g_pRouteMan->on_routes_update.Notify();
534 route->m_bRtIsSelected = false;
535}
536
537static void ReverseRoute(const std::string &guid) {
538 Route *route = g_pRouteMan->FindRouteByGUID(guid);
539 if (!route) {
540 wxLogMessage("Cannot activate guid: no such route");
541 return;
542 }
543 route->Reverse();
544 if (g_pRouteMan) g_pRouteMan->on_routes_update.Notify();
545}
546
547void MyApp::InitRestListeners() {
548 auto activate_route = [&](wxCommandEvent ev) {
549 auto guid = ev.GetString().ToStdString();
550 ActivateRoute(guid);
551 };
552 rest_activate_listener.Init(m_rest_server.activate_route, activate_route);
553 auto reverse_route = [&](wxCommandEvent ev) {
554 auto guid = ev.GetString().ToStdString();
555 ReverseRoute(guid);
556 };
557 rest_reverse_listener.Init(m_rest_server.reverse_route, reverse_route);
558}
559
560bool MyApp::OpenFile(const std::string &path) {
561 NavObjectCollection1 nav_objects;
562 auto result = nav_objects.load_file(path.c_str());
563 if (!result) {
564 std::string s(_("Cannot load route or waypoint file: "));
565 s += std::string("\"") + path + "\"";
566 wxMessageBox(s, "OpenCPN", wxICON_WARNING | wxOK);
567 return false;
568 }
569
570 int wpt_dups;
571 // Import with full vizibility of names and objects
572 nav_objects.LoadAllGPXObjects(!nav_objects.IsOpenCPN(), wpt_dups, true);
573
574 if (pRouteManagerDialog && pRouteManagerDialog->IsShown())
575 pRouteManagerDialog->UpdateLists();
576 LLBBox box = nav_objects.GetBBox();
577 if (box.GetValid()) {
578 gFrame->CenterView(gFrame->GetPrimaryCanvas(), box);
579 }
580 return true;
581}
582
583#ifndef __ANDROID__
584void MyApp::OnInitCmdLine(wxCmdLineParser &parser) {
585 // Add OpenCPN specific command line options. Help message
586 // is hardcoded in kUsage;
587 parser.AddSwitch("h", "help", "", wxCMD_LINE_OPTION_HELP);
588 parser.AddSwitch("p", "portable");
589 parser.AddOption("c", "configdir", "", wxCMD_LINE_VAL_STRING,
590 wxCMD_LINE_PARAM_OPTIONAL);
591 parser.AddSwitch("f", "fullscreen");
592 parser.AddSwitch("G", "no_opengl");
593 parser.AddSwitch("W", "config_wizard");
594 parser.AddSwitch("g", "rebuild_gl_raster_cache");
595 parser.AddSwitch("D", "rebuild_chart_db");
596 parser.AddSwitch("P", "parse_all_enc");
597 parser.AddOption("l", "loglevel");
598 parser.AddOption("u", "unit_test_1", "", wxCMD_LINE_VAL_NUMBER);
599 parser.AddSwitch("U", "unit_test_2");
600 parser.AddParam("import GPX files", wxCMD_LINE_VAL_STRING,
601 wxCMD_LINE_PARAM_OPTIONAL | wxCMD_LINE_PARAM_MULTIPLE);
602 parser.AddSwitch("s", "safe_mode");
603 parser.AddSwitch("r", "remote");
604 parser.AddSwitch("R", "raise");
605 parser.AddSwitch("q", "quit");
606 parser.AddSwitch("e", "get_rest_endpoint");
607 parser.AddOption("o", "open", "", wxCMD_LINE_VAL_STRING,
608 wxCMD_LINE_PARAM_OPTIONAL | wxCMD_LINE_PARAM_MULTIPLE);
609}
610#endif // __ANDROID__
611
613#ifdef __ANDROID__
614static void ParseLoglevel(wxCmdLineParser &parser) {
615 wxLog::SetLogLevel(wxLOG_Message);
616}
617#else
618static void ParseLoglevel(wxCmdLineParser &parser) {
619 const char *strLevel = std::getenv("OPENCPN_LOGLEVEL");
620 strLevel = strLevel ? strLevel : "info";
621 wxString wxLevel;
622 if (parser.Found("l", &wxLevel)) {
623 strLevel = wxLevel.c_str();
624 }
625 wxLogLevel level = OcpnLog::str2level(strLevel);
626 if (level == OcpnLog::LOG_BADLEVEL) {
627 fprintf(stderr, "Bad loglevel %s, using \"info\"", strLevel);
628 level = wxLOG_Info;
629 }
630 wxLog::SetLogLevel(level);
631}
632#endif // __ANDROID__
633
634#ifndef __ANDROID__
635bool MyApp::OnCmdLineHelp(wxCmdLineParser &parser) {
636 std::cout << kUsage;
637 return false;
638}
639#endif
640
641#ifndef __ANDROID__
642bool MyApp::OnCmdLineParsed(wxCmdLineParser &parser) {
643 long number;
644 wxString repo;
645 wxString plugin;
646
647 g_unit_test_2 = parser.Found("unit_test_2");
648 g_bportable = parser.Found("p");
649 g_start_fullscreen = parser.Found("fullscreen");
650 g_bdisable_opengl = parser.Found("no_opengl");
651 g_rebuild_gl_cache = parser.Found("rebuild_gl_raster_cache");
652 g_NeedDBUpdate = parser.Found("rebuild_chart_db") ? 2 : 0;
653 g_parse_all_enc = parser.Found("parse_all_enc");
654 g_config_wizard = parser.Found("config_wizard");
655 if (parser.Found("unit_test_1", &number)) {
656 g_unit_test_1 = static_cast<int>(number);
657 if (g_unit_test_1 == 0) g_unit_test_1 = -1;
658 }
659 safe_mode::set_mode(parser.Found("safe_mode"));
660 ParseLoglevel(parser);
661 wxString wxstr;
662 if (parser.Found("configdir", &wxstr)) {
663 g_configdir = wxstr.ToStdString();
664 fs::path path(g_configdir);
665 if (!fs::exists(path) || !fs::is_directory(path)) {
666 std::cerr << g_configdir << " is not an existing directory.\n";
667 return false;
668 }
669 }
670
671 bool has_start_options = false;
672 static const std::vector<std::string> kStartOptions = {
673 "unit_test_2",
674 "p",
675 "fullscreen",
676 "no_opengl",
677 "rebuild_gl_raster_cache",
678 "rebuild_chart_db",
679 "parse_all_enc",
680 "unit_test_1",
681 "safe_mode",
682 "loglevel"};
683 for (const auto &opt : kStartOptions) {
684 if (parser.Found(opt)) has_start_options = true;
685 }
686 if (has_start_options && parser.Found("remote")) {
687 std::cerr << "this option is not compatible with --remote\n";
688 return false;
689 }
690
691 bool has_remote_options = false;
692 static const std::vector<std::string> kRemoteOptions = {
693 "raise", "quit", "open", "get_rest_endpoint"};
694 for (const auto &opt : kRemoteOptions) {
695 if (parser.Found(opt)) has_remote_options = true;
696 }
697 if (has_remote_options && !parser.Found("remote")) {
698 std::cerr << "This option requires --remote\n";
699 return false;
700 }
701
702 for (size_t paramNr = 0; paramNr < parser.GetParamCount(); ++paramNr)
703 g_params.push_back(parser.GetParam(paramNr).ToStdString());
704
705 wxString optarg;
706 if (!parser.Found("remote"))
707 m_parsed_cmdline = ParsedCmdline();
708 else if (parser.Found("raise"))
709 m_parsed_cmdline = ParsedCmdline(CmdlineAction::Raise);
710 else if (parser.Found("quit"))
711 m_parsed_cmdline = ParsedCmdline(CmdlineAction::Quit);
712 else if (parser.Found("get_rest_endpoint"))
713 m_parsed_cmdline = ParsedCmdline(CmdlineAction::GetRestEndpoint);
714 else if (parser.Found("open", &optarg))
715 m_parsed_cmdline = ParsedCmdline(CmdlineAction::Open, optarg.ToStdString());
716 else if (parser.GetParamCount() == 1)
717 m_parsed_cmdline =
718 ParsedCmdline(CmdlineAction::Open, parser.GetParam(0).ToStdString());
719 else if (!has_start_options && !has_remote_options) {
720 // Neither arguments nor options
721 m_parsed_cmdline = ParsedCmdline(CmdlineAction::Raise);
722 }
723 return true;
724}
725#endif // __ANDROID__
726
727#ifdef __WXMSW__
728// Handle any exception not handled by CrashRpt
729// Most probable: Malloc/new failure
730
731bool MyApp::OnExceptionInMainLoop() {
732 wxLogWarning("Caught MainLoopException, continuing...");
733 return true;
734}
735#endif
736
737void MyApp::OnActivateApp(wxActivateEvent &event) { return; }
738
739static wxStopWatch init_sw;
740
741int MyApp::OnRun() {
742 if (m_exitcode != -2) return m_exitcode;
743 return wxAppConsole::OnRun();
744}
745
746MyApp::MyApp()
747 : m_checker(InstanceCheck::GetInstance()),
748 m_rest_server(PINCreateDialog::GetDlgCtx(), RouteCtxFactory(),
749 g_bportable),
750 m_usb_watcher(UsbWatchDaemon::GetInstance()),
751 m_exitcode(-2) {
752#ifdef __linux__
753 // Handle e. g., wayland default display -- see #1166.
754 if (!wxGetEnv("OCPN_DISABLE_X11_GDK_BACKEND", NULL)) {
755 if (wxGetEnv("WAYLAND_DISPLAY", NULL)) {
756 setenv("GDK_BACKEND", "x11", 1);
757 }
758 }
759 setenv(
760 "mesa_glthread", "false",
761 1); // Explicitly disable glthread. This may have some impact on OpenGL
762 // performance, but we know it is problematic for us. See #2889
763#endif // __linux__
764}
765
766bool MyApp::OnInit() {
767 if (!wxApp::OnInit()) return false;
768#ifdef __ANDROID__
769 androidEnableBackButton(false);
770 androidEnableOptionItems(false);
771#endif
772
774
775#if defined(__WXGTK__) && defined(ocpnUSE_GLES) && defined(__ARM_ARCH)
776 // There is a race condition between cairo which is used for text rendering
777 // by gtk and EGL which without the below code causes a bus error and the
778 // program aborts before startup
779 // this hack forces cairo to load right now by rendering some text
780
781 wxBitmap bmp(10, 10, -1);
782 wxMemoryDC dc;
783 dc.SelectObject(bmp);
784 dc.DrawText("X", 0, 0);
785#endif
786
787 // Instantiate the global OCPNPlatform class
788 g_Platform = new OCPNPlatform;
789 g_BasePlatform = g_Platform;
790#ifndef __ANDROID__
791 // We allow only one instance unless the portable option is used
792 if (!g_bportable && wxDirExists(g_Platform->GetPrivateDataDir())) {
793 m_checker.WaitUntilValid();
794 if (m_checker.IsMainInstance()) {
795 // Server is created on first call to GetInstance()
796 if (m_parsed_cmdline.action == CmdlineAction::Skip) {
797 // Server starts running when referenced.
798 [[maybe_unused]] auto &server = LocalServerApi::GetInstance();
799 } else {
800 std::cerr << "No remote opencpn found. Giving up.\n";
801 m_exitcode = 1;
802 return true;
803 }
804 } else {
805 std::unique_ptr<LocalClientApi> client;
806 try {
807 client = LocalClientApi::GetClient();
808 } catch (LocalApiException &ie) {
809 WARNING_LOG << "Ipc client exception: " << ie.str();
810 // If we get here it means that the instance_chk found another
811 // running instance. But that instance is for some reason not
812 // reachable. The safe thing to do is delete the lockfile and exit.
813 // Next start will proceed normally. This may leave a zombie OpenCPN,
814 // but at least O starts.
815 m_checker.CleanUp();
816 wxMessageBox(_("Sorry, an existing instance of OpenCPN may be too busy "
817 "to respond.\nPlease retry."),
818 "OpenCPN", wxICON_INFORMATION | wxOK);
819 m_exitcode = 2;
820 return true; // main program quiet exit.
821 }
822 if (client) {
823 auto result = client->HandleCmdline(m_parsed_cmdline.action,
824 m_parsed_cmdline.arg);
825 if (result.first) {
826 m_exitcode = 0;
827 } else {
828 wxLogDebug("Error running remote command: %s", result.second.c_str());
829 m_exitcode = 1;
830 }
831 return true;
832 }
833 }
834 }
835#endif // __ANDROID__
836
837 if (getenv("OPENCPN_FATAL_ERROR") != 0) {
838 wxLogFatalError(getenv("OPENCPN_FATAL_ERROR"));
839 }
840
841#ifndef __ANDROID__
842 // Check if last run failed, set up safe_mode.
843 if (!safe_mode::get_mode()) {
845 }
846#endif
847
848 // Perform first stage initialization
849 OCPNPlatform::Initialize_1();
850
851 // Set the name of the app as displayed to the user.
852 // This is necessary at least on OS X, for the capitalisation to be correct in
853 // the system menus.
854 MyApp::SetAppDisplayName("OpenCPN");
855
856 // Seed the random number generator
857 wxDateTime x = wxDateTime::UNow();
858 long seed = x.GetMillisecond();
859 seed *= x.GetTicks();
860 srand(seed);
861
862 // Fulup: force floating point to use dot as separation.
863 // This needs to be set early to catch numerics in config file.
864 setlocale(LC_NUMERIC, "C");
865
866 g_start_time = wxDateTime::Now();
867
868 g_loglast_time = g_start_time;
869 g_loglast_time.MakeGMT();
870 g_loglast_time.Subtract(
871 wxTimeSpan(0, 29, 0, 0)); // give 1 minute for GPS to get a fix
872
873 AnchorPointMinDist = 5.0;
874
875 // Init the private memory manager
876 malloc_max = 0;
877
878 // Record initial memory status
879 GetMemoryStatus(&g_mem_total, &g_mem_initial);
880
881 // Set up default FONT encoding, which should have been done by wxWidgets some
882 // time before this......
883 wxFont temp_font(10, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL,
884 wxFONTWEIGHT_NORMAL, FALSE, wxString(""),
885 wxFONTENCODING_SYSTEM);
886 temp_font.SetDefaultEncoding(wxFONTENCODING_SYSTEM);
887
888 // Start the Notification Manager and remove old persisted messages
889 auto &noteman = NotificationManager::GetInstance();
890 noteman.ScrubNotificationDirectory(30);
891
892 // Establish Log File location
893 if (!g_Platform->InitializeLogFile()) {
894 return false;
895 };
896
897#ifdef __WXMSW__
898
899 // Un-comment the following to establish a separate console window as a
900 // target for printf() in Windows RedirectIOToConsole();
901
902#endif
903
904 // Send init message
905 wxLogMessage("\n\n________\n");
906
907 wxDateTime now = wxDateTime::Now();
908 LOG_INFO("------- OpenCPN version %s restarted at %s -------\n", VERSION_FULL,
909 now.FormatISODate().mb_str().data());
910 wxLogLevel level = wxLog::GetLogLevel();
911 LOG_INFO("Using loglevel %s", OcpnLog::level2str(level).c_str());
912
913 wxString wxver(wxVERSION_STRING);
914 wxver.Prepend("wxWidgets version: ");
915
916 wxPlatformInfo platforminfo = wxPlatformInfo::Get();
917
918 wxString os_name;
919#ifndef __ANDROID__
920 os_name = platforminfo.GetOperatingSystemIdName();
921#else
922 os_name = platforminfo.GetOperatingSystemFamilyName();
923#endif
924
925 wxString platform = os_name + " " + platforminfo.GetArchName() + " " +
926 platforminfo.GetPortIdName();
927
928 wxLogMessage(wxver + " " + platform);
929
930 ::wxGetOsVersion(&osMajor, &osMinor);
931 wxString osVersionMsg;
932 osVersionMsg.Printf("OS Version reports as: %d.%d", osMajor, osMinor);
933 wxLogMessage(osVersionMsg);
934
935 wxLogMessage("MemoryStatus: mem_total: %d mb, mem_initial: %d mb",
936 g_mem_total / 1024, g_mem_initial / 1024);
937
938 OCPN_OSDetail *detail = g_Platform->GetOSDetail();
939 wxString msgplat;
940 wxString like0;
941 if (!detail->osd_names_like.empty())
942 like0 = detail->osd_names_like[0].c_str();
943 msgplat.Printf("OCPN_OSDetail: %s ; %s ; %s ; %s ; %s",
944 detail->osd_arch.c_str(), detail->osd_name.c_str(),
945 detail->osd_version.c_str(), detail->osd_ID.c_str(),
946 like0.mb_str());
947 wxLogMessage(msgplat);
948
949 wxString imsg = "SData_Locn is ";
950 imsg += g_Platform->GetSharedDataDir();
951 wxLogMessage(imsg);
952
953 // Initialize embedded PNG icon graphics
954 ::wxInitAllImageHandlers();
955
956#ifdef __WXQT__
957 // Now we can configure the Qt StyleSheets, if present
958 prepareAndroidStyleSheets();
959#endif
960
961 // Create some static strings
962 pInit_Chart_Dir = new wxString();
963
964 // Establish an empty ChartCroupArray
965 g_pGroupArray = new ChartGroupArray;
966
967 imsg = "PrivateDataDir is ";
968 imsg += g_Platform->GetPrivateDataDir();
969 wxLogMessage(imsg);
970
971 // Create an array string to hold repeating messages, so they don't
972 // overwhelm the log
973 pMessageOnceArray = new wxArrayString;
974
975 // Init the Route Manager
976 g_pRouteMan =
977 new Routeman(RoutePropDlg::GetDlgCtx(), RoutemanGui::GetDlgCtx());
978
979 // Init the Selectable Route Items List
980 pSelect = new Select();
981 pSelect->SetSelectPixelRadius(12);
982
983 // Init the Selectable Tide/Current Items List
984 pSelectTC = new Select();
985 // Increase the select radius for tide/current stations
986 pSelectTC->SetSelectPixelRadius(25);
987
988 // Init the Selectable AIS Target List
989 pSelectAIS = new Select();
990 pSelectAIS->SetSelectPixelRadius(12);
991
992 // Initially AIS display is always on
993 g_bShowAIS = true;
994 g_pais_query_dialog_active = NULL;
995
996 // Who am I?
997 g_hostname = ::wxGetHostName();
998 if (g_hostname.IsEmpty()) g_hostname = wxGetUserName();
999#ifdef __ANDROID__
1000 androidGetDeviceInfo();
1001 g_hostname = wxString("Android-") + g_android_Device_Model;
1002 g_hostname.Replace(" ", "-", true);
1003#endif
1004
1005 // A Portabel need a unique mDNS data hostname to share routes.
1006 if (g_bportable) {
1007 wxString p("Portable-");
1008 g_hostname = p + g_hostname;
1009 }
1010
1011 // Initialize some lists
1012 // Layers
1013 pLayerList = new LayerList;
1014 // Routes
1015 pRouteList = new RouteList;
1016
1017 // Initialize the NavObj_db
1018 auto &navobj_db = NavObj_dB::GetInstance();
1019
1020 // (Optionally) Capture the user and file(effective) ids
1021 // Some build environments may need root privileges for hardware
1022 // port I/O, as in the NMEA data input class. Set that up here.
1023
1024#ifndef __WXMSW__
1025#ifdef PROBE_PORTS__WITH_HELPER
1026 user_user_id = getuid();
1027 file_user_id = geteuid();
1028#endif
1029#endif
1030
1031 bool b_initial_load = false;
1032
1033 wxFileName config_test_file_name(g_Platform->GetConfigFileName());
1034 if (config_test_file_name.FileExists())
1035 wxLogMessage("Using existing Config_File: " +
1036 g_Platform->GetConfigFileName());
1037 else {
1038 {
1039 wxLogMessage("Creating new Config_File: " +
1040 g_Platform->GetConfigFileName());
1041
1042 b_initial_load = true;
1043
1044 if (true !=
1045 config_test_file_name.DirExists(config_test_file_name.GetPath()))
1046 if (!config_test_file_name.Mkdir(config_test_file_name.GetPath()))
1047 wxLogMessage("Cannot create config file directory for " +
1048 g_Platform->GetConfigFileName());
1049 }
1050 }
1051
1052 // Open/Create the Config Object
1053 pConfig = g_Platform->GetConfigObject();
1054 InitBaseConfig(pConfig);
1055 pConfig->LoadMyConfig();
1056
1057 // Override for some safe and nice default values if the config file was
1058 // created from scratch
1059 if (b_initial_load) g_Platform->SetDefaultOptions();
1060
1061 g_Platform->applyExpertMode(g_bUIexpert);
1062
1063 // Now initialize UI Style.
1064 g_StyleManager = new ocpnStyle::StyleManager();
1065
1066 // if(g_useMUI)
1067 // g_uiStyle = "MUI_flat";
1068
1069 g_StyleManager->SetStyle("MUI_flat");
1070 if (!g_StyleManager->IsOK()) {
1071 wxString msg = _("Failed to initialize the user interface. ");
1072 msg << _("OpenCPN cannot start. ");
1073 msg << _("The necessary configuration files were not found. ");
1074 msg << _("See the log file at ") << g_Platform->GetLogFileName()
1075 << _(" for details.") << "\n\n";
1076 msg << g_Platform->GetSharedDataDir();
1077
1078 wxMessageDialog w(NULL, msg, _("Failed to initialize the user interface. "),
1079 wxCANCEL | wxICON_ERROR);
1080 w.ShowModal();
1081 exit(EXIT_FAILURE);
1082 }
1083
1084 if (g_useMUI) {
1085 ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
1086 if (style) style->chartStatusWindowTransparent = true;
1087 }
1088
1089 // Init the WayPoint Manager
1090 pWayPointMan = NULL;
1091
1092 g_display_size_mm = wxMax(50, g_Platform->GetDisplaySizeMM());
1093 wxString msg;
1094 msg.Printf("Detected display size (horizontal): %d mm",
1095 (int)g_display_size_mm);
1096 wxLogMessage(msg);
1097
1098 // User override....
1099 if (g_config_display_size_manual &&
1100 g_config_display_size_mm.size() > g_current_monitor &&
1101 g_config_display_size_mm[g_current_monitor] > 0) {
1102 g_display_size_mm = g_config_display_size_mm[g_current_monitor];
1103 wxString msg;
1104 msg.Printf("Display size (horizontal) config override: %d mm",
1105 (int)g_display_size_mm);
1106 wxLogMessage(msg);
1107 g_Platform->SetDisplaySizeMM(g_current_monitor, g_display_size_mm);
1108 }
1109
1111
1112 if (g_btouch) {
1113 int SelectPixelRadius = 50;
1114
1115 pSelect->SetSelectPixelRadius(SelectPixelRadius);
1116 pSelectTC->SetSelectPixelRadius(wxMax(25, SelectPixelRadius));
1117 pSelectAIS->SetSelectPixelRadius(SelectPixelRadius);
1118 }
1119
1120 // Is this the first run after a clean installation?
1121 if (!n_NavMessageShown) {
1122 g_bFirstRun = true;
1123 }
1124
1125 // Now we can set the locale
1126 // using wxWidgets/gettext methodology....
1127
1128#if wxUSE_XLOCALE || !wxCHECK_VERSION(3, 0, 0)
1129
1130 // Where are the opencpn.mo files?
1131 g_Platform->SetLocaleSearchPrefixes();
1132
1133 wxString def_lang_canonical = g_Platform->GetDefaultSystemLocale();
1134
1135 imsg = "System default Language: " + def_lang_canonical;
1136 wxLogMessage(imsg);
1137
1138 wxString cflmsg = "Config file language: " + g_locale;
1139 wxLogMessage(cflmsg);
1140
1141 if (g_locale.IsEmpty()) {
1142 g_locale = def_lang_canonical;
1143 cflmsg = "Config file language empty, using system default: " + g_locale;
1144 wxLogMessage(cflmsg);
1145 }
1146
1147 // Make any adjustments necessary
1148 g_locale = g_Platform->GetAdjustedAppLocale();
1149 cflmsg = "Adjusted App language: " + g_locale;
1150 wxLogMessage(cflmsg);
1151
1152 // Set the desired locale
1153 g_Platform->ChangeLocale(g_locale, plocale_def_lang, &plocale_def_lang);
1154
1155 imsg = "Opencpn language set to: ";
1156 imsg += g_locale;
1157 wxLogMessage(imsg);
1158
1159 // French language locale is assumed to include the AZERTY keyboard
1160 // This applies to either the system language, or to OpenCPN language
1161 // selection
1162 if (g_locale == "fr_FR") g_b_assume_azerty = true;
1163#else
1164 wxLogMessage("wxLocale support not available");
1165#endif
1166
1167#ifndef __ANDROID__
1168 // Now that locale is established, possibly run the startup wizard.
1169 if (g_config_wizard || b_initial_load) {
1170 FirstUseWizImpl wiz(gFrame, pConfig);
1171 auto res = wiz.Run();
1172 if (res) {
1173 g_NeedDBUpdate = 2;
1174 }
1175 }
1176#endif
1177
1178 // Instantiate and initialize the Config Manager
1179 ConfigMgr::Get();
1180
1181 // Is this an upgrade?
1182 wxString vs = wxString("Version ") + VERSION_FULL + " Build " + VERSION_DATE;
1183 g_bUpgradeInProcess = (vs != g_config_version_string);
1184
1185 g_Platform->SetUpgradeOptions(vs, g_config_version_string);
1186
1187 // log deferred log restart message, if it exists.
1188 if (!g_Platform->GetLargeLogMessage().IsEmpty()) {
1189 wxLogMessage(g_Platform->GetLargeLogMessage());
1190 }
1191
1192 // Validate OpenGL functionality, if selected
1193#ifndef ocpnUSE_GL
1194 g_bdisable_opengl = true;
1195 ;
1196#endif
1197
1198 if (g_bdisable_opengl) g_bopengl = false;
1199
1200#if defined(__linux__) && !defined(__ANDROID__)
1201 if (g_bSoftwareGL) {
1202 setenv("LIBGL_ALWAYS_SOFTWARE", "1", 1);
1203 }
1204#endif
1205
1206 // FIXMW (dave) move to frame
1207 // g_bTransparentToolbarInOpenGLOK = isTransparentToolbarInOpenGLOK();
1208
1209 // On Windows platforms, establish a default cache managment policy
1210 // as allowing OpenCPN a percentage of available physical memory,
1211 // not to exceed 1 GB
1212 // Note that this logic implies that Windows platforms always use
1213 // the memCacheLimit policy, and never use the fallback nCacheLimit policy
1214#ifdef __WXMSW__
1215 if (0 == g_memCacheLimit) g_memCacheLimit = (int)(g_mem_total * 0.5);
1216 g_memCacheLimit =
1217 wxMin(g_memCacheLimit, 1024 * 1024); // math in kBytes, Max is 1 GB
1218#else
1219 // All other platforms will use the nCacheLimit policy
1220 // sinc on linux it is impossible to accurately measure the application memory
1221 // footprint without expensive methods such as malloc/free tracking, and such
1222
1223 g_memCacheLimit = 0;
1224 if (0 == g_nCacheLimit) // allow config file override
1225 g_nCacheLimit = CACHE_N_LIMIT_DEFAULT;
1226#endif
1227
1228 // Establish location and name of chart database
1229 ChartListFileName = newPrivateFileName(g_Platform->GetPrivateDataDir(),
1230 "chartlist.dat", "CHRTLIST.DAT");
1231
1232 // Establish location and name of AIS MMSI -> Target Name mapping
1233 AISTargetNameFileName = newPrivateFileName(g_Platform->GetPrivateDataDir(),
1234 "mmsitoname.csv", "MMSINAME.CSV");
1235
1236 // Establish guessed location of chart tree
1237 if (pInit_Chart_Dir->IsEmpty()) {
1238 wxStandardPaths &std_path = g_Platform->GetStdPaths();
1239
1240 if (!g_bportable)
1241#ifndef __ANDROID__
1242 pInit_Chart_Dir->Append(std_path.GetDocumentsDir());
1243#else
1244 pInit_Chart_Dir->Append(androidGetExtStorageDir());
1245#endif
1246 }
1247
1248 InitRestListeners();
1249
1250 // Establish the GSHHS Dataset location
1251 gDefaultWorldMapLocation = "gshhs";
1252 gDefaultWorldMapLocation.Prepend(g_Platform->GetSharedDataDir());
1253 gDefaultWorldMapLocation.Append(wxFileName::GetPathSeparator());
1254 if (gWorldMapLocation == wxEmptyString) {
1255 gWorldMapLocation = gDefaultWorldMapLocation;
1256 }
1257
1258 // Check the global Tide/Current data source array
1259 // If empty, preset default (US + ROW) data sources
1260 wxString default_tcdata0 =
1261 (g_Platform->GetSharedDataDir() + "tcdata" +
1262 wxFileName::GetPathSeparator() + "harmonics-dwf-20210110-free.tcd");
1263 wxString default_tcdata1 =
1264 (g_Platform->GetSharedDataDir() + "tcdata" +
1265 wxFileName::GetPathSeparator() + "HARMONICS_NO_US.IDX");
1266
1267 if (TideCurrentDataSet.empty()) {
1268 TideCurrentDataSet.push_back(
1269 g_Platform->NormalizePath(default_tcdata0).ToStdString());
1270 TideCurrentDataSet.push_back(
1271 g_Platform->NormalizePath(default_tcdata1).ToStdString());
1272 }
1273
1274 // Check the global AIS alarm sound file
1275 // If empty, preset default
1276 if (g_sAIS_Alert_Sound_File.IsEmpty()) {
1277 wxString default_sound = (g_Platform->GetSharedDataDir() + "sounds" +
1278 wxFileName::GetPathSeparator() + "2bells.wav");
1279 g_sAIS_Alert_Sound_File = g_Platform->NormalizePath(default_sound);
1280 }
1281
1282 gpIDXn = 0;
1283
1284 g_Platform->Initialize_2();
1285
1286 // Set up the frame initial visual parameters
1287 // Default size, resized later
1288 wxSize new_frame_size(-1, -1);
1289 int cx, cy, cw, ch;
1290 ::wxClientDisplayRect(&cx, &cy, &cw, &ch);
1291
1292 InitializeUserColors();
1293
1294 auto style = g_StyleManager->GetCurrentStyle();
1295 auto bitmap = new wxBitmap(style->GetIcon("default_pi", 32, 32));
1296 if (bitmap->IsOk())
1297 PluginLoader::GetInstance()->SetPluginDefaultIcon(bitmap);
1298 else
1299 wxLogWarning("Cannot initiate plugin default jigsaw icon.");
1300
1301 if ((g_nframewin_x > 100) && (g_nframewin_y > 100) && (g_nframewin_x <= cw) &&
1302 (g_nframewin_y <= ch))
1303 new_frame_size.Set(g_nframewin_x, g_nframewin_y);
1304 else
1305 new_frame_size.Set(cw * 7 / 10, ch * 7 / 10);
1306
1307 // Try to detect any change in physical screen configuration
1308 // This can happen when drivers are changed, for instance....
1309 // and can confuse the WUI layout perspective stored in the config file.
1310 // If detected, force a nominal window size and position....
1311 if ((g_lastClientRectx != cx) || (g_lastClientRecty != cy) ||
1312 (g_lastClientRectw != cw) || (g_lastClientRecth != ch)) {
1313 new_frame_size.Set(cw * 7 / 10, ch * 7 / 10);
1314 g_bframemax = false;
1315 }
1316
1317 g_lastClientRectx = cx;
1318 g_lastClientRecty = cy;
1319 g_lastClientRectw = cw;
1320 g_lastClientRecth = ch;
1321
1322 // Validate config file position
1323 wxPoint position(0, 0);
1324 wxSize dsize = wxGetDisplaySize();
1325
1326#ifdef __WXMAC__
1327 g_nframewin_posy = wxMax(g_nframewin_posy, 22);
1328#endif
1329
1330 if ((g_nframewin_posx < dsize.x) && (g_nframewin_posy < dsize.y))
1331 position = wxPoint(g_nframewin_posx, g_nframewin_posy);
1332
1333#ifdef __WXMSW__
1334 // Support MultiMonitor setups which can allow negative window positions.
1335 RECT frame_rect;
1336 frame_rect.left = position.x;
1337 frame_rect.top = position.y;
1338 frame_rect.right = position.x + new_frame_size.x;
1339 frame_rect.bottom = position.y + new_frame_size.y;
1340
1341 // If the requested frame window does not intersect any installed monitor,
1342 // then default to simple primary monitor positioning.
1343 if (NULL == MonitorFromRect(&frame_rect, MONITOR_DEFAULTTONULL))
1344 position = wxPoint(10, 10);
1345#endif
1346
1347#ifdef __WXOSX__
1348 // Support MultiMonitor setups which can allow negative window positions.
1349 const wxPoint ptScreen(position.x, position.y);
1350 const int displayIndex = wxDisplay::GetFromPoint(ptScreen);
1351
1352 if (displayIndex == wxNOT_FOUND) position = wxPoint(10, 30);
1353#endif
1354
1355 g_nframewin_posx = position.x;
1356 g_nframewin_posy = position.y;
1357
1358#ifdef __ANDROID__
1359 wxSize asz = getAndroidDisplayDimensions();
1360 ch = asz.y;
1361 cw = asz.x;
1362 // qDebug() << cw << ch;
1363
1364 if ((cw > 200) && (ch > 200))
1365 new_frame_size.Set(cw, ch);
1366 else
1367 new_frame_size.Set(800, 400);
1368#endif
1369
1370 // For Windows and GTK, provide the expected application Minimize/Close bar
1371 long app_style = wxDEFAULT_FRAME_STYLE;
1372 app_style |= wxWANTS_CHARS;
1373
1374 // Create the main frame window
1375
1376 // Strip the commit SHA number from the string to be shown in frame title.
1377 wxString short_version_name = wxString(PACKAGE_VERSION).BeforeFirst('+');
1378 wxString myframe_window_title = wxString("OpenCPN " + short_version_name);
1379
1380 if (g_bportable) {
1381 myframe_window_title += _(" -- [Portable(-p) executing from ");
1382 myframe_window_title += g_Platform->GetHomeDir();
1383 myframe_window_title += "]";
1384 }
1385
1386 wxString fmsg;
1387 fmsg.Printf("Creating MyFrame...size(%d, %d) position(%d, %d)",
1388 new_frame_size.x, new_frame_size.y, position.x, position.y);
1389 wxLogMessage(fmsg);
1390
1391 gFrame = new MyFrame(NULL, myframe_window_title, position, new_frame_size,
1392 app_style);
1393 wxTheApp->SetTopWindow(gFrame);
1394
1395 // Do those platform specific initialization things that need gFrame
1396 g_Platform->Initialize_3();
1397
1398 // Initialize the Plugin Manager
1399 g_pi_manager = new PlugInManager(gFrame);
1400
1401 // g_pauimgr = new wxAuiManager;
1402 g_pauimgr = new OCPN_AUIManager;
1403 g_pauidockart = new wxAuiDefaultDockArt;
1404 g_pauimgr->SetArtProvider(g_pauidockart);
1405 g_pauimgr->SetDockSizeConstraint(.9, .9);
1406
1407 // g_pauimgr->SetFlags(g_pauimgr->GetFlags() | wxAUI_MGR_LIVE_RESIZE);
1408
1409 // tell wxAuiManager to manage the frame
1410 g_pauimgr->SetManagedWindow(gFrame);
1411
1412 gFrame->CreateCanvasLayout();
1413
1414 // gFrame->RequestNewMasterToolbar( true );
1415
1416 gFrame->SetChartUpdatePeriod(); // Reasonable default
1417
1418 gFrame->Enable();
1419
1420 gFrame->GetPrimaryCanvas()->SetFocus();
1421
1422 pthumbwin = new ThumbWin(gFrame->GetPrimaryCanvas());
1423
1424 gFrame->ApplyGlobalSettings(false); // done once on init with resize
1425
1426 gFrame->SetAllToolbarScale();
1427
1428 // Show the frame
1429 gFrame->Show(TRUE);
1430 Yield(); // required for Gnome 45
1431
1432 gFrame->SetAndApplyColorScheme(global_color_scheme);
1433
1434 if (g_bframemax) gFrame->Maximize(true);
1435
1436#ifdef __ANDROID__
1437 if (g_bresponsive && (gFrame->GetPrimaryCanvas()->GetPixPerMM() > 4.0))
1438 gFrame->Maximize(true);
1439#endif
1440
1441 // Yield to pick up the OnSize() calls that result from Maximize()
1442 Yield();
1443
1444 // Build the initial chart dir array
1445 ArrayOfCDI ChartDirArray;
1446 pConfig->LoadChartDirArray(ChartDirArray);
1447
1448 // Windows installer may have left hints regarding the initial chart dir
1449 // selection
1450#ifdef __WXMSW__
1451 if (g_bFirstRun && (ChartDirArray.GetCount() == 0)) {
1452 int ndirs = 0;
1453
1454 wxRegKey RegKey(wxString("HKEY_LOCAL_MACHINE\\SOFTWARE\\OpenCPN"));
1455 if (RegKey.Exists()) {
1456 wxLogMessage(
1457 _("Retrieving initial Chart Directory set from Windows Registry"));
1458 wxString dirs;
1459 RegKey.QueryValue(wxString("ChartDirs"), dirs);
1460
1461 wxStringTokenizer tkz(dirs, ";");
1462 while (tkz.HasMoreTokens()) {
1463 wxString token = tkz.GetNextToken();
1464
1465 ChartDirInfo cdi;
1466 cdi.fullpath = token.Trim();
1467 cdi.magic_number = "";
1468
1469 ChartDirArray.Add(cdi);
1470 ndirs++;
1471 }
1472 }
1473
1474 if (g_bportable) {
1475 ChartDirInfo cdi;
1476 cdi.fullpath = "charts";
1477 cdi.fullpath.Prepend(g_Platform->GetSharedDataDir());
1478 cdi.magic_number = "";
1479 ChartDirArray.Add(cdi);
1480 ndirs++;
1481 }
1482
1483 if (ndirs) pConfig->UpdateChartDirs(ChartDirArray);
1484 }
1485#endif
1486
1487 // If the ChartDirArray is empty at this point, any existing chart database
1488 // file must be declared invalid, So it is best to simply delete it if
1489 // present.
1490 // TODO There is a possibility of recreating the dir list from the
1491 // database itself......
1492
1493 if (!ChartDirArray.GetCount())
1494 if (::wxFileExists(ChartListFileName)) ::wxRemoveFile(ChartListFileName);
1495
1496 // Try to load the current chart list Data file
1497 ChartData = new ChartDB();
1498 if (g_NeedDBUpdate == 0 &&
1499 !ChartData->LoadBinary(ChartListFileName, ChartDirArray)) {
1500 g_NeedDBUpdate = 1;
1501 }
1502
1503 // Verify any saved chart database startup index
1504 if (g_restore_dbindex >= 0) {
1505 if (ChartData->GetChartTableEntries() == 0)
1506 g_restore_dbindex = -1;
1507
1508 else if (g_restore_dbindex > (ChartData->GetChartTableEntries() - 1))
1509 g_restore_dbindex = 0;
1510 }
1511
1512 // Apply the inital Group Array structure to the chart database
1513 ChartData->ApplyGroupArray(g_pGroupArray);
1514
1515 // All set to go.....
1516
1517 // Process command line option to rebuild cache
1518#ifdef ocpnUSE_GL
1519 extern ocpnGLOptions g_GLOptions;
1520
1521 if (g_rebuild_gl_cache && g_bopengl && g_GLOptions.m_bTextureCompression &&
1522 g_GLOptions.m_bTextureCompressionCaching) {
1523 gFrame->ReloadAllVP(); // Get a nice chart background loaded
1524
1525 // Turn off the toolbar as a clear signal that the system is busy right
1526 // now.
1527 // Note: I commented this out because the toolbar never comes back for me
1528 // and is unusable until I restart opencpn without generating the cache
1529 // if( g_MainToolbar )
1530 // g_MainToolbar->Hide();
1531
1532 if (g_glTextureManager) g_glTextureManager->BuildCompressedCache();
1533 }
1534#endif
1535
1536 // FIXME (dave)
1537 // move method to frame
1538 // if (g_parse_all_enc) ParseAllENC(gFrame);
1539
1540 // establish GPS timeout value as multiple of frame timer
1541 // This will override any nonsense or unset value from the config file
1542 if ((gps_watchdog_timeout_ticks > 60) || (gps_watchdog_timeout_ticks <= 0))
1543 gps_watchdog_timeout_ticks = (GPS_TIMEOUT_SECONDS * 1000) / TIMER_GFRAME_1;
1544
1545 wxString dogmsg;
1546 dogmsg.Printf("GPS Watchdog Timeout is: %d sec.", gps_watchdog_timeout_ticks);
1547 wxLogMessage(dogmsg);
1548
1549 sat_watchdog_timeout_ticks = gps_watchdog_timeout_ticks;
1550
1551 g_priSats = 99;
1552
1553 // Most likely installations have no ownship heading information
1554 g_bVAR_Rx = false;
1555
1556 // Start up a new track if enabled in config file
1557 if (g_bTrackCarryOver) g_bDeferredStartTrack = true;
1558
1559 pAnchorWatchPoint1 = NULL;
1560 pAnchorWatchPoint2 = NULL;
1561
1562 Yield();
1563
1564 gFrame->DoChartUpdate();
1565
1566 FontMgr::Get()
1567 .ScrubList(); // Clean the font list, removing nonsensical entries
1568
1569 gFrame->ReloadAllVP(); // once more, and good to go
1570
1571 gFrame->Refresh(false);
1572 gFrame->Raise();
1573
1574 gFrame->GetPrimaryCanvas()->Enable();
1575 gFrame->GetPrimaryCanvas()->SetFocus();
1576
1577 // This little hack fixes a problem seen with some UniChrome OpenGL drivers
1578 // We need a deferred resize to get glDrawPixels() to work right.
1579 // So we set a trigger to generate a resize after 5 seconds....
1580 // See the "UniChrome" hack elsewhere
1581#ifdef ocpnUSE_GL
1582 if (!g_bdisable_opengl) {
1583 glChartCanvas *pgl =
1584 (glChartCanvas *)gFrame->GetPrimaryCanvas()->GetglCanvas();
1585 if (pgl && (pgl->GetRendererString().Find("UniChrome") != wxNOT_FOUND)) {
1586 gFrame->m_defer_size = gFrame->GetSize();
1587 gFrame->SetSize(gFrame->m_defer_size.x - 10, gFrame->m_defer_size.y);
1588 g_pauimgr->Update();
1589 gFrame->m_bdefer_resize = true;
1590 }
1591 }
1592#endif
1593
1594 // Horrible Hack (tm): Make sure the RoutePoint destructor can invoke
1595 // glDeleteTextures. Truly awful.
1596#ifdef ocpnUSE_GL
1597 if (g_bopengl)
1598 RoutePoint::delete_gl_textures = [](unsigned n, const unsigned *texts) {
1599 glDeleteTextures(n, texts);
1600 };
1601#else
1602 RoutePoint::delete_gl_textures = [](unsigned n, const unsigned *texts) {};
1603#endif
1604
1605 if (g_start_fullscreen) gFrame->ToggleFullScreen();
1606
1607#ifdef __ANDROID__
1608 // We need a resize to pick up height adjustment after building android
1609 // ActionBar
1610 gFrame->SetSize(getAndroidDisplayDimensions());
1611 androidSetFollowTool(gFrame->GetPrimaryCanvas()->m_bFollow ? 1 : 0, true);
1612#endif
1613
1614 gFrame->Raise();
1615 gFrame->GetPrimaryCanvas()->Enable();
1616 gFrame->GetPrimaryCanvas()->SetFocus();
1617
1618 // Setup Tides/Currents to settings present at last shutdown
1619 // TODO
1620 // gFrame->ShowTides( g_bShowTide );
1621 // gFrame->ShowCurrents( g_bShowCurrent );
1622
1623 // Start up the ticker....
1624 gFrame->FrameTimer1.Start(TIMER_GFRAME_1, wxTIMER_CONTINUOUS);
1625
1626 // Start up the ViewPort Rotation angle Averaging Timer....
1627 gFrame->FrameCOGTimer.Start(2000, wxTIMER_CONTINUOUS);
1628
1629 // Start up the Ten Hz timer....
1630 gFrame->FrameTenHzTimer.Start(100, wxTIMER_CONTINUOUS);
1631
1632 // wxLogMessage( wxString::Format("OpenCPN Initialized in %ld ms.",
1633 // init_sw.Time() ) );
1634
1635 OCPNPlatform::Initialize_4();
1636
1637#ifdef __ANDROID__
1638 androidHideBusyIcon();
1639#endif
1640 wxLogMessage(
1641 wxString::Format(_("OpenCPN Initialized in %ld ms."), init_sw.Time()));
1642
1643 wxMilliSleep(500);
1644
1645#ifdef __ANDROID__
1646 // We defer the startup message to here to allow the app frame to be
1647 // contructed, thus avoiding a dialog with NULL parent which might not work
1648 // on some devices.
1649 if (!n_NavMessageShown || (vs != g_config_version_string) ||
1650 (g_AndroidVersionCode != androidGetVersionCode())) {
1651 // qDebug() << "Showing NavWarning";
1652 wxMilliSleep(500);
1653
1654 if (!ShowNavWarning()) {
1655 qDebug() << "Closing due to NavWarning Cancel";
1656 gFrame->Close();
1657 androidTerminate();
1658 return true;
1659 }
1660
1661 n_NavMessageShown = 1;
1662 }
1663
1664 // Finished with upgrade checking, so persist the currect Version Code
1665 g_AndroidVersionCode = androidGetVersionCode();
1666 qDebug() << "Persisting Version Code: " << g_AndroidVersionCode;
1667#else
1668 // Send the Welcome/warning message if it has never been sent before,
1669 // or if the version string has changed at all
1670 // We defer until here to allow for localization of the message
1671 if (!n_NavMessageShown || (vs != g_config_version_string)) {
1672 if (!ShowNavWarning()) return false;
1673 n_NavMessageShown = 1;
1674 pConfig->Flush();
1675 }
1676#endif
1677
1678 // As an a.e. Raspberry does not have a hardwareclock we will have some
1679 // problems with date/time setting
1680 g_bHasHwClock = true; // by default most computers do have a hwClock
1681#if defined(__UNIX__) && !defined(__ANDROID__)
1682 struct stat buffer;
1683 g_bHasHwClock =
1684 ((stat("/dev/rtc", &buffer) == 0) || (stat("/dev/rtc0", &buffer) == 0) ||
1685 (stat("/dev/misc/rtc", &buffer) == 0));
1686#endif
1687
1688 g_config_version_string = vs;
1689
1690 // The user accepted the "not for navigation" nag, so persist it here...
1691 pConfig->UpdateSettings();
1692
1693 // Start delayed initialization chain after some milliseconds
1694 gFrame->InitTimer.Start(5, wxTIMER_CONTINUOUS);
1695
1696 g_pauimgr->Update();
1697
1698 for (auto *cp : TheConnectionParams()) {
1699 if (cp->bEnabled) {
1700 if (cp->GetDSPort().Contains("Serial")) {
1701 std::string port(cp->Port.ToStdString());
1702 CheckSerialAccess(gFrame, port);
1703 }
1704 }
1705 }
1706 CheckDongleAccess(gFrame);
1707
1708 // Initialize the CommBridge
1709 m_comm_bridge.Initialize();
1710
1711 std::vector<std::string> ipv4_addrs = get_local_ipv4_addresses();
1712
1713 // If network connection is available, start the server and mDNS client
1714 if (ipv4_addrs.size()) {
1715 std::string ipAddr = ipv4_addrs[0];
1716
1717 wxString data_dir = g_Platform->GetPrivateDataDir();
1718 if (data_dir.Last() != wxFileName::GetPathSeparator())
1719 data_dir.Append(wxFileName::GetPathSeparator());
1720
1721 make_certificate(ipAddr, data_dir.ToStdString());
1722
1723 m_rest_server.StartServer(fs::path(data_dir.ToStdString()));
1724 StartMDNSService(g_hostname.ToStdString(), "opencpn-object-control-service",
1725 8000);
1726 }
1727 return TRUE;
1728}
1729
1730int MyApp::OnExit() {
1731 wxLogMessage("opencpn::MyApp starting exit.");
1732 m_checker.OnExit();
1733 m_usb_watcher.Stop();
1734 // Send current nav status data to log file // pjotrc 2010.02.09
1735
1736 wxDateTime lognow = wxDateTime::Now();
1737 lognow.MakeGMT();
1738 wxString day = lognow.FormatISODate();
1739 wxString utc = lognow.FormatISOTime();
1740 wxString navmsg = "LOGBOOK: ";
1741 navmsg += day;
1742 navmsg += " ";
1743 navmsg += utc;
1744 navmsg += " UTC ";
1745
1746 if (bGPSValid) {
1747 wxString data;
1748 data.Printf("OFF: Lat %10.5f Lon %10.5f ", gLat, gLon);
1749 navmsg += data;
1750
1751 wxString cog;
1752 if (std::isnan(gCog))
1753 cog.Printf("COG ----- ");
1754 else
1755 cog.Printf("COG %10.5f ", gCog);
1756
1757 wxString sog;
1758 if (std::isnan(gSog))
1759 sog.Printf("SOG ----- ");
1760 else
1761 sog.Printf("SOG %6.2f " + getUsrSpeedUnit(), toUsrSpeed(gSog));
1762
1763 navmsg += cog;
1764 navmsg += sog;
1765
1766 } else {
1767 wxString data;
1768 data.Printf("OFF: Lat %10.5f Lon %10.5f", gLat, gLon);
1769 navmsg += data;
1770 }
1771 wxLogMessage(navmsg);
1772 g_loglast_time = lognow;
1773
1774 if (ptcmgr) delete ptcmgr;
1775
1776 for (Track *track : g_TrackList) {
1777 delete track;
1778 }
1779 g_TrackList.clear();
1780
1781 delete pConfig;
1782 delete pSelect;
1783 delete pSelectTC;
1784 delete pSelectAIS;
1785
1786 delete ps52plib;
1787 delete g_SencThreadManager;
1788
1789 if (g_pGroupArray) {
1790 for (unsigned int igroup = 0; igroup < g_pGroupArray->GetCount();
1791 igroup++) {
1792 delete g_pGroupArray->Item(igroup);
1793 }
1794
1795 g_pGroupArray->Clear();
1796 delete g_pGroupArray;
1797 }
1798
1799 wxLogMessage("opencpn::MyApp exiting cleanly...\n");
1800 wxLog::FlushActive();
1801
1802 g_Platform->CloseLogFile();
1803
1804 delete pInit_Chart_Dir;
1805
1806 for (Track *track : g_TrackList) {
1807 delete track;
1808 }
1809 g_TrackList.clear();
1810
1811 delete g_pRouteMan;
1812 delete pWayPointMan;
1813
1814 delete pMessageOnceArray;
1815
1816 DeInitializeUserColors();
1817
1818 delete pLayerList;
1819
1820 delete m_pRegistrarMan;
1821 CSVDeaccess(NULL);
1822
1823 delete g_StyleManager;
1824
1825#ifdef __WXMSW__
1826#ifdef USE_GLU_TESS
1827#ifdef USE_GLU_DLL
1828 if (s_glu_dll_ready) {
1829 FreeLibrary(s_hGLU_DLL);
1830 } // free the glu32.dll
1831#endif
1832#endif
1833#endif
1834
1835 // Restore any changed system colors
1836
1837#ifdef __WXMSW__
1838 void RestoreSystemColors(void);
1839 RestoreSystemColors();
1840#endif
1841
1842#ifdef __MSVC__LEAK
1843 DeInitAllocCheck();
1844#endif
1845
1846#if wxUSE_XLOCALE || !wxCHECK_VERSION(3, 0, 0)
1847 if (plocale_def_lang) delete plocale_def_lang;
1848#endif
1849
1850 FontMgr::Shutdown();
1851
1852 g_Platform->OnExit_2();
1854 delete g_Platform;
1855
1856 return TRUE;
1857}
1858
1859#ifdef LINUX_CRASHRPT
1860void MyApp::OnFatalException() { g_crashprint.Report(); }
1861#endif
1862
1863//----------------------------------------------------------------------------------------------------------
1864// Application-wide CPL Error handler
1865//----------------------------------------------------------------------------------------------------------
1866void MyCPLErrorHandler(CPLErr eErrClass, int nError, const char *pszErrorMsg)
1867
1868{
1869 char msg[256];
1870
1871 if (eErrClass == CE_Debug)
1872 snprintf(msg, 255, "CPL: %s", pszErrorMsg);
1873 else if (eErrClass == CE_Warning)
1874 snprintf(msg, 255, "CPL Warning %d: %s", nError, pszErrorMsg);
1875 else
1876 snprintf(msg, 255, "CPL ERROR %d: %s", nError, pszErrorMsg);
1877
1878 wxString str(msg, wxConvUTF8);
1879 wxLogMessage(str);
1880}
class About
Class AboutFrameImpl.
Class AisDecoder and helpers.
Global state for AIS decoder.
Class AISTargetAlertDialog and helpers.
Class AISTargetListDialog.
Class AISTargetQueryDialog.
Chart canvas configuration state
ChartDB * ChartData
Global instance.
Definition chartdb.cpp:72
Charts database management
ChartGroupArray * g_pGroupArray
Global instance.
Definition chartdbs.cpp:54
Generic Chart canvas base.
Extends AboutFrame, providing implementation for various event handlers and additional methods.
wxString & GetPrivateDataDir()
Return dir path for opencpn.log, etc., respecting -c cli option.
EventVar reverse_route
Notified with a string GUID when user wants to reverse a route.
EventVar activate_route
Notified with a string GUID when user wants to activate a route.
Handles the AIS information GUI and sound alerts.
ChartCanvas - Main chart display and interaction component.
Definition chcanv.h:151
Manages the chart database and provides access to chart data.
Definition chartdb.h:94
bool LoadBinary(const wxString &filename, ArrayOfCDI &dir_array_check)
Load the chart database from a binary file.
Definition chartdb.cpp:231
void Notify() override
Notify all listeners, no data supplied.
void ScrubList()
Cleans up stale font entries after a locale change.
Definition FontMgr.cpp:564
static void SeedRandom()
Seed the random generator used by GetUUID().
Common interface for all instance checkers.
virtual bool IsMainInstance()=0
Return true if this process is the primary opencpn instance.
virtual void CleanUp()
Remove all persistent instance state, including possible lock file and defunct opencpn processes.
virtual void OnExit()
Do whatever needed before wxWidget's checks triggers.
virtual void WaitUntilValid()
Wait until this object can be used for example for Dbus connection.
static LocalServerApi & GetInstance()
Main application frame.
Definition ocpn_frame.h:138
Provides platform-specific support utilities for OpenCPN.
void SetDisplaySizeMM(size_t monitor, double size)
Set the width of the monitor in millimeters.
double GetDisplaySizeMM()
Get the width of the screen in millimeters.
void Init(const KeyProvider &kp, const std::function< void(ObservedEvt &ev)> &action)
Initiate an object yet not listening.
Definition observable.h:295
bool LoadAllPlugIns(bool enabled_plugins, bool keep_orphans=false)
Update catalog with imported metadata and load all plugin library files.
bool StartServer(const fs::path &certificate_location) override
Start the server thread.
Represents a waypoint or mark within the navigation system.
Definition route_point.h:70
static std::function< void(unsigned, const unsigned *)> delete_gl_textures
Horrible Hack (tm).
Definition route_point.h:50
Represents a navigational route in the navigation system.
Definition route.h:98
bool m_bRtIsSelected
Flag indicating whether this route is currently selected in the UI.
Definition route.h:202
wxString m_RouteNameString
User-assigned name for the route.
Definition route.h:246
EventVar on_routes_update
Notified when list of routes is updated (no data in event)
Definition routeman.h:265
bool ActivateRoute(Route *pRouteToActivate, RoutePoint *pStartPoint=NULL)
Activates a route for navigation.
Definition routeman.cpp:263
Window for displaying chart thumbnails.
Definition thumbwin.h:59
Represents a track, which is a series of connected track points.
Definition track.h:114
Listen to hardware events and notifies SystemEvents when new devices are plugged in.
OpenGL chart rendering canvas.
Handles crash reporting in wxWidgets applications.
Definition crashprint.h:24
Class cm93chart and helpers – CM93 chart state.
Global variables reflecting command line options and arguments.
The CommBridge class and helpers.
Primary navigation console display for route and vessel tracking.
Config file user configuration interface.
std::vector< size_t > g_config_display_size_mm
g_config_display_size_mm: Size of pysical screen in millimeters.
double g_display_size_mm
Physical display width (mm)
Global variables stored in configuration file.
Dump debug info on crash.
Miscellaneous globals primarely used by gui layer.
The local API has a server side handling commands and a client part issuing commands.
Enhanced logging interface on top of wx/log.h.
mDNS lookup wrappers.
Start/stop mdns service routines.
Multiplexer class and helpers.
void check_last_start()
Check if the last start failed, possibly invoke user dialog and set safe mode state.
void clear_check()
Mark last run as successful.
Definition safe_mode.cpp:39
Class NavObj_dB.
Class NotificationManager.
General observable implementation with several specializations.
PLugin remote repositories installation and Uninstall/list operations.
bool CheckDongleAccess(wxWindow *parent)
Runs checks and if required dialogs to make dongle accessible.
bool CheckSerialAccess(wxWindow *parent, const std::string device)
Run checks and possible dialogs to ensure device is accessible.
Access checks for comm devices and dongle.