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