53#include <wx/msw/msvcrt.h>
65#if defined(__WXMSW__) && defined(__MSVC__LEAK)
66#include "Stackwalker.h"
75#include <wx/apptrait.h>
76#include <wx/arrimpl.cpp>
77#include <wx/artprov.h>
78#include <wx/aui/aui.h>
79#include <wx/aui/dockart.h>
80#include <wx/clrpicker.h>
81#include <wx/cmdline.h>
85#include <wx/display.h>
90#include <wx/jsonreader.h>
91#include <wx/listctrl.h>
93#include <wx/printdlg.h>
95#include <wx/progdlg.h>
96#include <wx/settings.h>
97#include <wx/stdpaths.h>
98#include <wx/tokenzr.h>
100#include "o_sound/o_sound.h"
120#include "model/nav_object_database.h"
132#include "ais_info_gui.h"
144#include "dialog_alert.h"
147#include "gdal/cpl_csv.h"
161#include "route_ctx_factory.h"
168#include "safe_mode_gui.h"
169#include "std_filesystem.h"
175#include "user_colors.h"
188void RedirectIOToConsole();
200#include "androidUTIL.h"
205using namespace std::literals::chrono_literals;
207const char *
const kUsage =
210 opencpn [-p] [-f] [-G] [-g] [-P] [-l <str>] [-u <num>] [-U] [-s] [GPX file ...]
211 opencpn --remote [-R] | -q] | -e] |-o <str>]
213Options for starting opencpn
215 -c, --configdir=<dirpath> Use alternative configuration directory.
216 -p, --portable Run in portable mode.
217 -f, --fullscreen Switch to full screen mode on start.
218 -G, --no_opengl Disable OpenGL video acceleration. This setting will
220 -g, --rebuild_gl_raster_cache Rebuild OpenGL raster cache on start.
221 -D, --rebuild_chart_db Rescan chart directories and rebuild the chart database
222 -P, --parse_all_enc Convert all S-57 charts to OpenCPN's internal format on start.
223 -l, --loglevel=<str> Amount of logging: error, warning, message, info, debug or trace
224 -u, --unit_test_1=<num> Display a slideshow of <num> charts and then exit.
225 Zero or negative <num> specifies no limit.
227 -s, --safe_mode Run without plugins, opengl and other "dangerous" stuff
228 -W, --config_wizard Start with initial configuration wizard
230Options manipulating already started opencpn
231 -r, --remote Execute commands on already running instance
232 -R, --raise Make running OpenCPN visible if hidden
233 -q, --quit Terminate already running opencpn
234 -e, --get_rest_endpoint Print rest server endpoint and exit.
235 -o, --open=<GPX file> Open file in running opencpn
238 GPX file GPX-formatted file with waypoints or routes.
242wxDEFINE_EVENT(EVT_N2K_129029, wxCommandEvent);
243wxDEFINE_EVENT(EVT_N2K_129026, wxCommandEvent);
245wxDEFINE_EVENT(EVT_N0183_RMC, wxCommandEvent);
246wxDEFINE_EVENT(EVT_N0183_HDT, wxCommandEvent);
247wxDEFINE_EVENT(EVT_N0183_HDG, wxCommandEvent);
248wxDEFINE_EVENT(EVT_N0183_HDM, wxCommandEvent);
249wxDEFINE_EVENT(EVT_N0183_VTG, wxCommandEvent);
250wxDEFINE_EVENT(EVT_N0183_GSV, wxCommandEvent);
251wxDEFINE_EVENT(EVT_N0183_GGA, wxCommandEvent);
252wxDEFINE_EVENT(EVT_N0183_GLL, wxCommandEvent);
253wxDEFINE_EVENT(EVT_N0183_AIVDO, wxCommandEvent);
263WX_DEFINE_OBJARRAY(ArrayOfCDI);
265static int user_user_id;
266static int file_user_id;
268static int g_mem_total, g_mem_initial;
270static unsigned int malloc_max;
272static int osMajor, osMinor;
274static bool g_bHasHwClock;
276#if wxUSE_XLOCALE || !wxCHECK_VERSION(3, 0, 0)
278wxLocale *plocale_def_lang = 0;
286extern sigjmp_buf env;
291DEFINE_GUID(GARMIN_DETECT_GUID, 0x2c9c45c2L, 0x8e7d, 0x4c08, 0xa1, 0x2d, 0x81,
292 0x6b, 0xba, 0xe7, 0x22, 0xc0);
296static const long long lNaN = 0xfff8000000000000;
297#define NAN (*(double *)&lNaN)
301void appendOSDirSlash(wxString *pString);
303void InitializeUserColors();
304void DeInitializeUserColors();
305void SetSystemColors(ColorScheme cs);
307static bool LoadAllPlugIns(
bool load_enabled) {
308 g_Platform->ShowBusySpinner();
309 bool b = PluginLoader::GetInstance()->
LoadAllPlugIns(load_enabled);
310 g_Platform->HideBusySpinner();
318#if defined(__WXGTK__) || defined(__WXQT__)
319#include "bitmaps/opencpn.xpm"
322wxString newPrivateFileName(wxString,
const char *name,
323 [[maybe_unused]]
const char *windowsName) {
324 wxString fname = wxString::FromUTF8(name);
325 wxString filePathAndName;
328 if (filePathAndName.Last() != wxFileName::GetPathSeparator())
329 filePathAndName.Append(wxFileName::GetPathSeparator());
332 wxString fwname = wxString::FromUTF8(windowsName);
333 filePathAndName.Append(fwname);
335 filePathAndName.Append(fname);
338 return filePathAndName;
344 : wxFrame(
nullptr, wxID_ANY,
"Loading...", wxDefaultPosition,
345 wxSize(2000, 2000), wxSTAY_ON_TOP) {
347 SetBackgroundColour(wxColour(0, 0, 0));
349 wxPanel *panel =
new wxPanel(
this, wxID_ANY);
352 panel->SetBackgroundColour(wxColour(0, 0, 0));
355 wxBoxSizer *sizer =
new wxBoxSizer(wxVERTICAL);
356 sizer->Add(panel, 1, wxEXPAND);
363bool ShowNavWarning() {
366OpenCPN is distributed in the hope that it will be useful, \
367but WITHOUT ANY WARRANTY; without even the implied \
368warranty of MERCHANTABILITY or FITNESS FOR A \
369PARTICULAR PURPOSE.\n\n\
370See the GNU General Public License for more details.\n\n\
371OpenCPN must only be used in conjunction with approved \
372paper charts and traditional methods of navigation.\n\n\
373DO NOT rely upon OpenCPN for safety of life or property.\n\n\
374Please click \"Agree\" and proceed, or \"Cancel\" to quit.\n"));
376 wxString vs = wxString::Format(
" .. Version %s", VERSION_FULL);
379 androidShowDisclaimer(_(
"OpenCPN for Android") + vs, msg);
382 msg.Replace(
"\n",
"<br>");
384 std::stringstream html;
385 html <<
"<html><body><p>";
386 html << msg.ToStdString();
387 html <<
"</p></body></html>";
389 std::string title = _(
"Welcome to OpenCPN").ToStdString();
390 std::string action = _(
"Agree").ToStdString();
392 info_dlg.SetInitialSize();
393 info_dlg.AddHtmlContent(html);
394 int agreed = info_dlg.ShowModal();
395 return agreed == wxID_OK;
399bool DoNavMessage(wxString &new_version_string) {
404 if (!n_NavMessageShown || (new_version_string != g_config_version_string) ||
405 (g_AndroidVersionCode != androidGetVersionCode())) {
409 if (!ShowNavWarning()) {
410 qDebug() <<
"Closing due to NavWarning Cancel";
416 n_NavMessageShown = 1;
420 g_AndroidVersionCode = androidGetVersionCode();
421 qDebug() <<
"Persisting Version Code: " << g_AndroidVersionCode;
426 if (!n_NavMessageShown || (new_version_string != g_config_version_string)) {
427 if (!ShowNavWarning())
return false;
428 n_NavMessageShown = 1;
441BEGIN_EVENT_TABLE(
MyApp, wxApp)
442EVT_ACTIVATE_APP(MyApp::OnActivateApp)
445static
void ActivateRoute(const std::
string &guid) {
448 wxLogMessage(
"Cannot activate guid: no such route");
459 point = route->GetPoint(2);
466static void ReverseRoute(
const std::string &guid) {
469 wxLogMessage(
"Cannot activate guid: no such route");
476void MyApp::InitRestListeners() {
477 auto activate_route = [&](wxCommandEvent ev) {
478 auto guid = ev.GetString().ToStdString();
482 auto reverse_route = [&](wxCommandEvent ev) {
483 auto guid = ev.GetString().ToStdString();
489void MyApp::OnUnhandledException() {
496 }
catch (std::exception &e) {
498 what.Printf(
"standard exception with message \"%s\"", e.what());
500 what.Printf(
"standard exception of type \"%s\" with message \"%s\"",
501 typeid(e).name(), e.what());
504 what =
"unknown exception";
506 wxMessageOutputBest().Printf(
507 "Unhandled %s; terminating %s.\n", what,
508 wxIsMainThread() ?
"the application" :
"the thread in which it happened");
511bool MyApp::OpenFile(
const std::string &path) {
513 auto result = nav_objects.load_file(path.c_str());
515 std::string s(_(
"Cannot load route or waypoint file: "));
516 s += std::string(
"\"") + path +
"\"";
517 wxMessageBox(s,
"OpenCPN", wxICON_WARNING | wxOK);
523 nav_objects.LoadAllGPXObjects(!nav_objects.IsOpenCPN(), wpt_dups,
true);
527 LLBBox box = nav_objects.GetBBox();
528 if (box.GetValid()) {
529 gFrame->CenterView(gFrame->GetPrimaryCanvas(), box);
535void MyApp::OnInitCmdLine(wxCmdLineParser &parser) {
538 parser.AddSwitch(
"h",
"help",
"", wxCMD_LINE_OPTION_HELP);
539 parser.AddSwitch(
"p",
"portable");
540 parser.AddOption(
"c",
"configdir",
"", wxCMD_LINE_VAL_STRING,
541 wxCMD_LINE_PARAM_OPTIONAL);
542 parser.AddSwitch(
"f",
"fullscreen");
543 parser.AddSwitch(
"G",
"no_opengl");
544 parser.AddSwitch(
"W",
"config_wizard");
545 parser.AddSwitch(
"g",
"rebuild_gl_raster_cache");
546 parser.AddSwitch(
"D",
"rebuild_chart_db");
547 parser.AddSwitch(
"P",
"parse_all_enc");
548 parser.AddOption(
"l",
"loglevel");
549 parser.AddOption(
"u",
"unit_test_1",
"", wxCMD_LINE_VAL_NUMBER);
550 parser.AddSwitch(
"U",
"unit_test_2");
551 parser.AddParam(
"import GPX files", wxCMD_LINE_VAL_STRING,
552 wxCMD_LINE_PARAM_OPTIONAL | wxCMD_LINE_PARAM_MULTIPLE);
553 parser.AddSwitch(
"s",
"safe_mode");
554 parser.AddSwitch(
"r",
"remote");
555 parser.AddSwitch(
"R",
"raise");
556 parser.AddSwitch(
"q",
"quit");
557 parser.AddSwitch(
"e",
"get_rest_endpoint");
558 parser.AddOption(
"o",
"open",
"", wxCMD_LINE_VAL_STRING,
559 wxCMD_LINE_PARAM_OPTIONAL | wxCMD_LINE_PARAM_MULTIPLE);
565static void ParseLoglevel(wxCmdLineParser &parser) {
566 wxLog::SetLogLevel(wxLOG_Message);
569static void ParseLoglevel(wxCmdLineParser &parser) {
570 const char *strLevel = std::getenv(
"OPENCPN_LOGLEVEL");
571 strLevel = strLevel ? strLevel :
"info";
573 if (parser.Found(
"l", &wxLevel)) {
574 strLevel = wxLevel.c_str();
576 wxLogLevel level = OcpnLog::str2level(strLevel);
577 if (level == OcpnLog::LOG_BADLEVEL) {
578 fprintf(stderr,
"Bad loglevel %s, using \"info\"", strLevel);
581 wxLog::SetLogLevel(level);
586bool MyApp::OnCmdLineHelp(wxCmdLineParser &parser) {
593bool MyApp::OnCmdLineParsed(wxCmdLineParser &parser) {
598 g_unit_test_2 = parser.Found(
"unit_test_2");
599 g_bportable = parser.Found(
"p");
600 g_start_fullscreen = parser.Found(
"fullscreen");
601 g_bdisable_opengl = parser.Found(
"no_opengl");
602 g_rebuild_gl_cache = parser.Found(
"rebuild_gl_raster_cache");
604 g_parse_all_enc = parser.Found(
"parse_all_enc");
605 g_config_wizard = parser.Found(
"config_wizard");
606 if (parser.Found(
"unit_test_1", &number)) {
607 g_unit_test_1 =
static_cast<int>(number);
608 if (g_unit_test_1 == 0) g_unit_test_1 = -1;
610 safe_mode::set_mode(parser.Found(
"safe_mode"));
611 ParseLoglevel(parser);
613 if (parser.Found(
"configdir", &wxstr)) {
614 g_configdir = wxstr.ToStdString();
615 fs::path path(g_configdir);
616 if (!fs::exists(path) || !fs::is_directory(path)) {
617 std::cerr << g_configdir <<
" is not an existing directory.\n";
622 bool has_start_options =
false;
623 static const std::vector<std::string> kStartOptions = {
628 "rebuild_gl_raster_cache",
634 for (
const auto &opt : kStartOptions) {
635 if (parser.Found(opt)) has_start_options =
true;
637 if (has_start_options && parser.Found(
"remote")) {
638 std::cerr <<
"this option is not compatible with --remote\n";
642 bool has_remote_options =
false;
643 static const std::vector<std::string> kRemoteOptions = {
644 "raise",
"quit",
"open",
"get_rest_endpoint"};
645 for (
const auto &opt : kRemoteOptions) {
646 if (parser.Found(opt)) has_remote_options =
true;
648 if (has_remote_options && !parser.Found(
"remote")) {
649 std::cerr <<
"This option requires --remote\n";
653 for (
size_t paramNr = 0; paramNr < parser.GetParamCount(); ++paramNr)
654 g_params.push_back(parser.GetParam(paramNr).ToStdString());
657 if (!parser.Found(
"remote"))
658 m_parsed_cmdline = ParsedCmdline();
659 else if (parser.Found(
"raise"))
660 m_parsed_cmdline = ParsedCmdline(CmdlineAction::Raise);
661 else if (parser.Found(
"quit"))
662 m_parsed_cmdline = ParsedCmdline(CmdlineAction::Quit);
663 else if (parser.Found(
"get_rest_endpoint"))
664 m_parsed_cmdline = ParsedCmdline(CmdlineAction::GetRestEndpoint);
665 else if (parser.Found(
"open", &optarg))
666 m_parsed_cmdline = ParsedCmdline(CmdlineAction::Open, optarg.ToStdString());
667 else if (parser.GetParamCount() == 1)
669 ParsedCmdline(CmdlineAction::Open, parser.GetParam(0).ToStdString());
670 else if (!has_start_options && !has_remote_options) {
672 m_parsed_cmdline = ParsedCmdline(CmdlineAction::Raise);
682bool MyApp::OnExceptionInMainLoop() {
683 wxLogWarning(
"Caught MainLoopException, continuing...");
688void MyApp::OnActivateApp(wxActivateEvent &event) {
return; }
690static wxStopWatch init_sw;
693 if (m_exitcode != -2)
return m_exitcode;
694 return wxAppConsole::OnRun();
705 if (!wxGetEnv(
"OCPN_DISABLE_X11_GDK_BACKEND", NULL)) {
706 if (wxGetEnv(
"WAYLAND_DISPLAY", NULL)) {
707 setenv(
"GDK_BACKEND",
"x11", 1);
711 "mesa_glthread",
"false",
720 if (!wxApp::OnInit())
return false;
723 androidEnableBackButton(
false);
724 androidEnableOptionItems(
false);
729#if defined(__WXGTK__) && defined(ocpnUSE_GLES) && defined(__ARM_ARCH)
735 wxBitmap bmp(10, 10, -1);
737 dc.SelectObject(bmp);
738 dc.DrawText(
"X", 0, 0);
753 if (m_parsed_cmdline.action == CmdlineAction::Skip) {
757 std::cerr <<
"No remote opencpn found. Giving up.\n";
762 std::unique_ptr<LocalClientApi> client;
764 client = LocalClientApi::GetClient();
766 WARNING_LOG <<
"Ipc client exception: " << ie.str();
773 wxMessageBox(_(
"Sorry, an existing instance of OpenCPN may be too busy "
774 "to respond.\nPlease retry."),
775 "OpenCPN", wxICON_INFORMATION | wxOK);
780 auto result = client->HandleCmdline(m_parsed_cmdline.action,
781 m_parsed_cmdline.arg);
785 wxLogDebug(
"Error running remote command: %s", result.second.c_str());
794 if (getenv(
"OPENCPN_FATAL_ERROR") != 0) {
795 wxLogFatalError(getenv(
"OPENCPN_FATAL_ERROR"));
800 if (!safe_mode::get_mode()) {
806 OCPNPlatform::Initialize_1();
811 MyApp::SetAppDisplayName(
"OpenCPN");
814 wxDateTime x = wxDateTime::UNow();
815 long seed = x.GetMillisecond();
816 seed *= x.GetTicks();
821 setlocale(LC_NUMERIC,
"C");
823 g_start_time = wxDateTime::Now();
825 g_loglast_time = g_start_time;
826 g_loglast_time.MakeGMT();
827 g_loglast_time.Subtract(
828 wxTimeSpan(0, 29, 0, 0));
830 AnchorPointMinDist = 5.0;
836 platform::GetMemoryStatus(&g_mem_total, &g_mem_initial);
840 wxFont temp_font(10, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL,
841 wxFONTWEIGHT_NORMAL, FALSE, wxString(
""),
842 wxFONTENCODING_SYSTEM);
843 temp_font.SetDefaultEncoding(wxFONTENCODING_SYSTEM);
846 auto ¬eman = NotificationManager::GetInstance();
847 noteman.ScrubNotificationDirectory(30);
850 if (!g_Platform->InitializeLogFile()) {
862 wxLogMessage(
"\n\n________\n");
864 wxDateTime now = wxDateTime::Now();
865 LOG_INFO(
"------- OpenCPN version %s restarted at %s -------\n", VERSION_FULL,
866 now.FormatISODate().mb_str().data());
867 wxLogLevel level = wxLog::GetLogLevel();
868 LOG_INFO(
"Using loglevel %s", OcpnLog::level2str(level).c_str());
870 wxString wxver(wxVERSION_STRING);
871 wxver.Prepend(
"wxWidgets version: ");
873 wxPlatformInfo platforminfo = wxPlatformInfo::Get();
877 os_name = platforminfo.GetOperatingSystemIdName();
879 os_name = platforminfo.GetOperatingSystemFamilyName();
882 wxString platform = os_name +
" " + platforminfo.GetArchName() +
" " +
883 platforminfo.GetPortIdName();
885 wxLogMessage(wxver +
" " + platform);
887 ::wxGetOsVersion(&osMajor, &osMinor);
888 wxString osVersionMsg;
889 osVersionMsg.Printf(
"OS Version reports as: %d.%d", osMajor, osMinor);
890 wxLogMessage(osVersionMsg);
892 wxLogMessage(
"MemoryStatus: mem_total: %d mb, mem_initial: %d mb",
893 g_mem_total / 1024, g_mem_initial / 1024);
898 if (!detail->osd_names_like.empty())
899 like0 = detail->osd_names_like[0].c_str();
900 msgplat.Printf(
"OCPN_OSDetail: %s ; %s ; %s ; %s ; %s",
901 detail->osd_arch.c_str(), detail->osd_name.c_str(),
902 detail->osd_version.c_str(), detail->osd_ID.c_str(),
904 wxLogMessage(msgplat);
906 wxString imsg =
"SData_Locn is ";
907 imsg += g_Platform->GetSharedDataDir();
911 ::wxInitAllImageHandlers();
915 prepareAndroidStyleSheets();
919 pInit_Chart_Dir =
new wxString();
924 imsg =
"PrivateDataDir is ";
930 navutil::InitGlobals();
934 new Routeman(RoutePropDlg::GetDlgCtx(), RoutemanGui::GetDlgCtx());
938 pSelect->SetSelectPixelRadius(12);
951 g_pais_query_dialog_active = NULL;
954 g_hostname = ::wxGetHostName();
955 if (g_hostname.IsEmpty()) g_hostname = wxGetUserName();
957 androidGetDeviceInfo();
958 g_hostname = wxString(
"Android-") + g_android_Device_Model;
959 g_hostname.Replace(
" ",
"-",
true);
964 wxString p(
"Portable-");
965 g_hostname = p + g_hostname;
970 pLayerList =
new LayerList;
975 auto &navobj_db = NavObj_dB::GetInstance();
982#ifdef PROBE_PORTS__WITH_HELPER
983 user_user_id = getuid();
984 file_user_id = geteuid();
988 bool b_initial_load =
false;
990 wxFileName config_test_file_name(g_Platform->GetConfigFileName());
991 if (config_test_file_name.FileExists())
992 wxLogMessage(
"Using existing Config_File: " +
993 g_Platform->GetConfigFileName());
996 wxLogMessage(
"Creating new Config_File: " +
997 g_Platform->GetConfigFileName());
999 b_initial_load =
true;
1002 config_test_file_name.DirExists(config_test_file_name.GetPath()))
1003 if (!config_test_file_name.Mkdir(config_test_file_name.GetPath()))
1004 wxLogMessage(
"Cannot create config file directory for " +
1005 g_Platform->GetConfigFileName());
1010 pConfig = g_Platform->GetConfigObject();
1014 if (g_kiosk_startup) {
1016 g_wallpaper->ShowFullScreen(
true);
1017 g_wallpaper->Show();
1022 if (b_initial_load) g_Platform->SetDefaultOptions();
1024 g_Platform->applyExpertMode(g_bUIexpert);
1029 g_StyleManager->SetStyle(
"MUI_flat");
1030 if (!g_StyleManager->IsOK()) {
1031 wxString msg = _(
"Failed to initialize the user interface. ");
1032 msg << _(
"OpenCPN cannot start. ");
1033 msg << _(
"The necessary configuration files were not found. ");
1034 msg << _(
"See the log file at ") << g_Platform->GetLogFileName()
1035 << _(
" for details.") <<
"\n\n";
1036 msg << g_Platform->GetSharedDataDir();
1038 wxMessageDialog w(NULL, msg, _(
"Failed to initialize the user interface. "),
1039 wxCANCEL | wxICON_ERROR);
1046 if (style) style->chartStatusWindowTransparent =
true;
1050 pWayPointMan = NULL;
1054 msg.Printf(
"Detected display size (horizontal): %d mm",
1059 if (g_config_display_size_manual &&
1064 msg.Printf(
"Display size (horizontal) config override: %d mm",
1073 int SelectPixelRadius = 50;
1075 pSelect->SetSelectPixelRadius(SelectPixelRadius);
1076 pSelectTC->SetSelectPixelRadius(wxMax(25, SelectPixelRadius));
1077 pSelectAIS->SetSelectPixelRadius(SelectPixelRadius);
1081 if (!n_NavMessageShown) {
1088#if wxUSE_XLOCALE || !wxCHECK_VERSION(3, 0, 0)
1091 g_Platform->SetLocaleSearchPrefixes();
1093 wxString def_lang_canonical = g_Platform->GetDefaultSystemLocale();
1095 imsg =
"System default Language: " + def_lang_canonical;
1098 wxString cflmsg =
"Config file language: " + g_locale;
1099 wxLogMessage(cflmsg);
1101 if (g_locale.IsEmpty()) {
1102 g_locale = def_lang_canonical;
1103 cflmsg =
"Config file language empty, using system default: " + g_locale;
1104 wxLogMessage(cflmsg);
1108 g_locale = g_Platform->GetAdjustedAppLocale();
1109 cflmsg =
"Adjusted App language: " + g_locale;
1110 wxLogMessage(cflmsg);
1113 g_Platform->ChangeLocale(g_locale, plocale_def_lang, &plocale_def_lang);
1115 imsg =
"Opencpn language set to: ";
1122 if (g_locale ==
"fr_FR") g_b_assume_azerty =
true;
1124 wxLogMessage(
"wxLocale support not available");
1129 if (g_config_wizard || b_initial_load) {
1131 auto res = wiz.Run();
1142 wxString vs = wxString(
"Version ") + VERSION_FULL +
" Build " + VERSION_DATE;
1143 g_bUpgradeInProcess = (vs != g_config_version_string);
1145 g_Platform->SetUpgradeOptions(vs, g_config_version_string);
1148 if (!g_Platform->GetLargeLogMessage().IsEmpty()) {
1149 wxLogMessage(g_Platform->GetLargeLogMessage());
1154 g_bdisable_opengl =
true;
1157 if (g_bdisable_opengl) g_bopengl =
false;
1159#if defined(__linux__) && !defined(__ANDROID__)
1160 if (g_bSoftwareGL) {
1161 setenv(
"LIBGL_ALWAYS_SOFTWARE",
"1", 1);
1174 if (0 == g_memCacheLimit) g_memCacheLimit = (int)(g_mem_total * 0.5);
1176 wxMin(g_memCacheLimit, 1024 * 1024);
1182 g_memCacheLimit = 0;
1183 if (0 == g_nCacheLimit)
1184 g_nCacheLimit = CACHE_N_LIMIT_DEFAULT;
1189 "chartlist.dat",
"CHRTLIST.DAT");
1193 "mmsitoname.csv",
"MMSINAME.CSV");
1196 if (pInit_Chart_Dir->IsEmpty()) {
1197 wxStandardPaths &std_path = g_Platform->GetStdPaths();
1201 pInit_Chart_Dir->Append(std_path.GetDocumentsDir());
1203 pInit_Chart_Dir->Append(androidGetExtStorageDir());
1207 InitRestListeners();
1210 gDefaultWorldMapLocation =
"gshhs";
1211 gDefaultWorldMapLocation.Prepend(g_Platform->GetSharedDataDir());
1212 gDefaultWorldMapLocation.Append(wxFileName::GetPathSeparator());
1213 if (gWorldMapLocation == wxEmptyString) {
1214 gWorldMapLocation = gDefaultWorldMapLocation;
1219 wxString default_tcdata0 =
1220 (g_Platform->GetSharedDataDir() +
"tcdata" +
1221 wxFileName::GetPathSeparator() +
"harmonics-dwf-20210110-free.tcd");
1222 wxString default_tcdata1 =
1223 (g_Platform->GetSharedDataDir() +
"tcdata" +
1224 wxFileName::GetPathSeparator() +
"HARMONICS_NO_US.IDX");
1226 if (TideCurrentDataSet.empty()) {
1227 TideCurrentDataSet.push_back(
1228 g_Platform->NormalizePath(default_tcdata0).ToStdString());
1229 TideCurrentDataSet.push_back(
1230 g_Platform->NormalizePath(default_tcdata1).ToStdString());
1235 if (g_sAIS_Alert_Sound_File.IsEmpty()) {
1236 wxString default_sound = (g_Platform->GetSharedDataDir() +
"sounds" +
1237 wxFileName::GetPathSeparator() +
"2bells.wav");
1238 g_sAIS_Alert_Sound_File = g_Platform->NormalizePath(default_sound);
1243 g_Platform->Initialize_2();
1245 LoadChartDatabase();
1249 g_kiosk_startup =
false;
1253 if (!g_kiosk_startup) {
1255 SetTopWindow(gFrame);
1262 OCPNPlatform::Initialize_4();
1265 androidHideBusyIcon();
1268 wxString::Format(_(
"OpenCPN Initialized in %ld ms."), init_sw.Time()));
1272 if (!g_kiosk_startup) {
1273 if (!DoNavMessage(vs)) {
1280 g_bHasHwClock =
true;
1281#if defined(__UNIX__) && !defined(__ANDROID__)
1284 ((stat(
"/dev/rtc", &buffer) == 0) || (stat(
"/dev/rtc0", &buffer) == 0) ||
1285 (stat(
"/dev/misc/rtc", &buffer) == 0));
1288 g_config_version_string = vs;
1293 for (
auto *cp : TheConnectionParams()) {
1295 if (cp->GetDSPort().Contains(
"Serial")) {
1296 std::string port(cp->Port.ToStdString());
1303 std::vector<std::string> ipv4_addrs = get_local_ipv4_addresses();
1306 if (ipv4_addrs.size()) {
1307 std::string ipAddr = ipv4_addrs[0];
1310 if (data_dir.Last() != wxFileName::GetPathSeparator())
1311 data_dir.Append(wxFileName::GetPathSeparator());
1313 make_certificate(ipAddr, data_dir.ToStdString());
1315 m_rest_server.
StartServer(fs::path(data_dir.ToStdString()));
1316 StartMDNSService(g_hostname.ToStdString(),
"opencpn-object-control-service",
1320 if (!g_kiosk_startup) {
1324 wxLogMessage(
"InitTimer start");
1325 gFrame->InitTimer.Start(10, wxTIMER_CONTINUOUS);
1335 CallAfter([
this]() {
1337 g_wallpaper->Show(
false);
1339 gFrame->ShowFullScreen(
true);
1340 g_bFullscreen =
true;
1343 g_wallpaper->Destroy();
1344 g_wallpaper =
nullptr;
1346 SetTopWindow(gFrame);
1350 wxString(
"Version ") + VERSION_FULL +
" Build " + VERSION_DATE;
1351 if (!DoNavMessage(vs)) {
1357 wxLogMessage(
"InitTimer start");
1358 gFrame->InitTimer.Start(10, wxTIMER_CONTINUOUS);
1363void MyApp::BuildMainFrame() {
1366 wxSize new_frame_size(-1, -1);
1368 ::wxClientDisplayRect(&cx, &cy, &cw, &ch);
1370 user_colors::Initialize();
1372 if ((g_nframewin_x > 100) && (g_nframewin_y > 100) && (g_nframewin_x <= cw) &&
1373 (g_nframewin_y <= ch))
1374 new_frame_size.Set(g_nframewin_x, g_nframewin_y);
1376 new_frame_size.Set(cw * 7 / 10, ch * 7 / 10);
1382 if ((g_lastClientRectx != cx) || (g_lastClientRecty != cy) ||
1383 (g_lastClientRectw != cw) || (g_lastClientRecth != ch)) {
1384 new_frame_size.Set(cw * 7 / 10, ch * 7 / 10);
1385 g_bframemax =
false;
1388 g_lastClientRectx = cx;
1389 g_lastClientRecty = cy;
1390 g_lastClientRectw = cw;
1391 g_lastClientRecth = ch;
1394 wxPoint position(0, 0);
1395 wxSize dsize = wxGetDisplaySize();
1398 g_nframewin_posy = wxMax(g_nframewin_posy, 22);
1401 if ((g_nframewin_posx < dsize.x) && (g_nframewin_posy < dsize.y))
1402 position = wxPoint(g_nframewin_posx, g_nframewin_posy);
1407 frame_rect.left = position.x;
1408 frame_rect.top = position.y;
1409 frame_rect.right = position.x + new_frame_size.x;
1410 frame_rect.bottom = position.y + new_frame_size.y;
1414 if (NULL == MonitorFromRect(&frame_rect, MONITOR_DEFAULTTONULL))
1415 position = wxPoint(10, 10);
1420 const wxPoint ptScreen(position.x, position.y);
1421 const int displayIndex = wxDisplay::GetFromPoint(ptScreen);
1423 if (displayIndex == wxNOT_FOUND) position = wxPoint(10, 30);
1426 g_nframewin_posx = position.x;
1427 g_nframewin_posy = position.y;
1430 wxSize asz = getAndroidDisplayDimensions();
1435 if ((cw > 200) && (ch > 200))
1436 new_frame_size.Set(cw, ch);
1438 new_frame_size.Set(800, 400);
1444 wxString short_version_name = wxString(PACKAGE_VERSION).BeforeFirst(
'+');
1445 wxString myframe_window_title = wxString(
"OpenCPN " + short_version_name);
1448 myframe_window_title += _(
" -- [Portable(-p) executing from ");
1449 myframe_window_title += g_Platform->GetHomeDir();
1450 myframe_window_title +=
"]";
1454 fmsg.Printf(
"Creating MyFrame...size(%d, %d) position(%d, %d)",
1455 new_frame_size.x, new_frame_size.y, position.x, position.y);
1459 auto dockart =
new wxAuiDefaultDockArt;
1462 gFrame =
new MyFrame(myframe_window_title, position, new_frame_size,
1463 m_rest_server, dockart,
1464 [&](
const std::string &path) {
return OpenFile(path); });
1469 g_pauimgr->SetDockSizeConstraint(.9, .9);
1475 g_Platform->Initialize_3();
1477 gFrame->CreateCanvasLayout();
1481 gFrame->SetChartUpdatePeriod();
1485 gFrame->ApplyGlobalSettings(
false);
1486 gFrame->SetAllToolbarScale();
1487 gFrame->SetAndApplyColorScheme(global_color_scheme);
1488 if (g_bframemax) gFrame->Maximize(
true);
1491 if (g_bresponsive && (gFrame->GetPrimaryCanvas()->
GetPixPerMM() > 4.0))
1492 gFrame->Maximize(
true);
1501 if (g_rebuild_gl_cache && g_bopengl && g_GLOptions.m_bTextureCompression &&
1502 g_GLOptions.m_bTextureCompressionCaching) {
1503 gFrame->ReloadAllVP();
1522 if ((gps_watchdog_timeout_ticks > 60) || (gps_watchdog_timeout_ticks <= 0))
1526 dogmsg.Printf(
"GPS Watchdog Timeout is: %d sec.", gps_watchdog_timeout_ticks);
1527 wxLogMessage(dogmsg);
1529 sat_watchdog_timeout_ticks = gps_watchdog_timeout_ticks;
1537 if (g_bTrackCarryOver) g_bDeferredStartTrack =
true;
1542 gFrame->DoChartUpdate();
1545 for (
auto *cp : TheConnectionParams()) {
1548 cp->b_IsSetup = TRUE;
1554 auto style = g_StyleManager->GetCurrentStyle();
1555 auto bitmap =
new wxBitmap(style->GetIcon(
"default_pi", 32, 32));
1557 PluginLoader::GetInstance()->SetPluginDefaultIcon(bitmap);
1559 wxLogWarning(
"Cannot initiate plugin default jigsaw icon.");
1561 AbstractPlatform::ShowBusySpinner();
1563 AbstractPlatform::HideBusySpinner();
1567 wxString perspective;
1569 pConfig->Read(
"AUIPerspective", &perspective);
1576 bool bno_load =
false;
1578 wxArrayString name_array;
1579 wxStringTokenizer st(perspective,
"|;");
1580 while (st.HasMoreTokens()) {
1581 wxString s1 = st.GetNextToken();
1582 if (s1.StartsWith(
"name=")) {
1583 wxString sc = s1.AfterFirst(
'=');
1588 wxAuiPaneInfoArray pane_array_val =
g_pauimgr->GetAllPanes();
1589 for (
unsigned int i = 0; i < pane_array_val.GetCount(); i++) {
1590 wxAuiPaneInfo pane = pane_array_val.Item(i);
1594 if (name_array.Index(pane.name) == wxNOT_FOUND) {
1600 if (!bno_load)
g_pauimgr->LoadPerspective(perspective,
false);
1605 for (
unsigned int i = 0; i <
g_canvasArray.GetCount(); i++) {
1608 wxSize frameSize = GetClientSize();
1609 wxSize minSize =
g_pauimgr->GetPane(cc).min_size;
1610 int width = wxMax(minSize.x, frameSize.x / 10);
1611 g_pauimgr->GetPane(cc).MinSize(frameSize.x * 1 / 5, frameSize.y);
1633 if (!g_bdisable_opengl) {
1636 if (pgl && (pgl->GetRendererString().Find(
"UniChrome") != wxNOT_FOUND)) {
1637 gFrame->m_defer_size = gFrame->GetSize();
1638 gFrame->SetSize(gFrame->m_defer_size.x - 10, gFrame->m_defer_size.y);
1640 gFrame->m_bdefer_resize =
true;
1650 glDeleteTextures(n, texts);
1659 gFrame->SetSize(getAndroidDisplayDimensions());
1660 androidSetFollowTool(gFrame->GetPrimaryCanvas()->m_bFollow ? 1 : 0, true);
1672void MyApp::LoadChartDatabase() {
1674 ArrayOfCDI ChartDirArray;
1675 pConfig->LoadChartDirArray(ChartDirArray);
1680 if (g_bFirstRun && (ChartDirArray.GetCount() == 0)) {
1683 wxRegKey RegKey(wxString(
"HKEY_LOCAL_MACHINE\\SOFTWARE\\OpenCPN"));
1684 if (RegKey.Exists()) {
1686 _(
"Retrieving initial Chart Directory set from Windows Registry"));
1688 RegKey.QueryValue(wxString(
"ChartDirs"), dirs);
1690 wxStringTokenizer tkz(dirs,
";");
1691 while (tkz.HasMoreTokens()) {
1692 wxString token = tkz.GetNextToken();
1695 cdi.fullpath = token.Trim();
1696 cdi.magic_number =
"";
1698 ChartDirArray.Add(cdi);
1705 cdi.fullpath =
"charts";
1706 cdi.fullpath.Prepend(g_Platform->GetSharedDataDir());
1707 cdi.magic_number =
"";
1708 ChartDirArray.Add(cdi);
1712 if (ndirs)
pConfig->UpdateChartDirs(ChartDirArray);
1722 if (!ChartDirArray.GetCount())
1723 if (::wxFileExists(ChartListFileName)) ::wxRemoveFile(ChartListFileName);
1730 g_restore_dbindex = 0;
1734 if (g_restore_dbindex >= 0) {
1735 if (
ChartData->GetChartTableEntries() == 0)
1736 g_restore_dbindex = -1;
1738 else if (g_restore_dbindex > (
ChartData->GetChartTableEntries() - 1))
1739 g_restore_dbindex = 0;
1747 wxLogMessage(
"opencpn::MyApp starting exit.");
1749 m_usb_watcher.Stop();
1752 wxDateTime lognow = wxDateTime::Now();
1754 wxString day = lognow.FormatISODate();
1755 wxString utc = lognow.FormatISOTime();
1756 wxString navmsg =
"LOGBOOK: ";
1764 data.Printf(
"OFF: Lat %10.5f Lon %10.5f ",
gLat,
gLon);
1768 if (std::isnan(
gCog))
1769 cog.Printf(
"COG ----- ");
1771 cog.Printf(
"COG %10.5f ",
gCog);
1774 if (std::isnan(
gSog))
1775 sog.Printf(
"SOG ----- ");
1777 sog.Printf(
"SOG %6.2f " + getUsrSpeedUnit(), toUsrSpeed(
gSog));
1784 data.Printf(
"OFF: Lat %10.5f Lon %10.5f",
gLat,
gLon);
1787 wxLogMessage(navmsg);
1788 g_loglast_time = lognow;
1806 for (
unsigned int igroup = 0; igroup <
g_pGroupArray->GetCount();
1815 wxLogMessage(
"opencpn::MyApp exiting cleanly...\n");
1816 wxLog::FlushActive();
1818 g_Platform->CloseLogFile();
1820 delete pInit_Chart_Dir;
1828 delete pWayPointMan;
1830 navutil::DeinitGlobals();
1832 user_colors::DeInitialize();
1839 delete g_StyleManager;
1844 void RestoreSystemColors();
1845 RestoreSystemColors();
1852#if wxUSE_XLOCALE || !wxCHECK_VERSION(3, 0, 0)
1856 FontMgr::Shutdown();
1858 g_Platform->OnExit_2();
1865#ifdef LINUX_CRASHRPT
1866void MyApp::OnFatalException() { g_crashprint.Report(); }
1872void MyCPLErrorHandler(CPLErr eErrClass,
int nError,
const char *pszErrorMsg)
1877 if (eErrClass == CE_Debug)
1878 snprintf(msg, 255,
"CPL: %s", pszErrorMsg);
1879 else if (eErrClass == CE_Warning)
1880 snprintf(msg, 255,
"CPL Warning %d: %s", nError, pszErrorMsg);
1882 snprintf(msg, 255,
"CPL ERROR %d: %s", nError, pszErrorMsg);
1884 wxString str(msg, wxConvUTF8);
Select * pSelectAIS
Global instance.
wxString AISTargetNameFileName
Global instance.
Class AisDecoder and helpers.
Global state for AIS decoder.
Class AISTargetAlertDialog and helpers.
Class AISTargetListDialog.
Class AISTargetQueryDialog.
Chart canvas configuration state
Purpose: TLS Certificate support.
ChartDB * ChartData
Global instance.
Charts database management
ChartGroupArray * g_pGroupArray
Global instance.
arrayofCanvasPtr g_canvasArray
Global instance.
Generic Chart canvas base.
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.
A modal message dialog with confirmation button and cancel button.
ChartCanvas - Main chart display and interaction component.
double GetPixPerMM()
Get the number of logical pixels per millimeter on the screen.
Manages the chart database and provides access to chart data.
bool LoadBinary(const wxString &filename, ArrayOfCDI &dir_array_check)
Load the chart database from a binary file.
void Notify() override
Notify all listeners, no data supplied.
static void SeedRandom()
Seed the random generator used by GetUUID().
EventVar on_routes_update
Notified when list of routes is updated (no data in event)
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()
void Init(const KeyProvider &kp, const std::function< void(ObservedEvt &ev)> &action)
Initiate an object yet not listening.
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.
static std::function< void(unsigned, const unsigned *)> delete_gl_textures
Horrible Hack (tm).
Represents a navigational route in the navigation system.
bool m_bRtIsSelected
Flag indicating whether this route is currently selected in the UI.
wxString m_RouteNameString
User-assigned name for the route.
bool ActivateRoute(Route *pRouteToActivate, RoutePoint *pStartPoint=NULL)
Activates a route for navigation.
Window for displaying chart thumbnails.
Represents a track, which is a series of connected track points.
Listen to hardware events and notifies SystemEvents when new devices are plugged in.
OpenGL chart rendering canvas.
Handles crash reporting in wxWidgets applications.
Class cm93chart and helpers – CM93 chart state.
Global variables reflecting command line options and arguments.
CommBridge class and helpers.
void MakeLoopbackDriver()
Create and register the loopback driver.
void MakeCommDriver(const ConnectionParams *params)
Create and register a driver for given connection.
Communication drivers factory and support.
NMEA Data Multiplexer Object.
Variables maintained by comm stack, read-only access for others.
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.
OpenGL chart rendering canvas.
glTextureManager * g_glTextureManager
Global instance.
Misc GUI event vars, a singleton.
size_t g_current_monitor
Current monitor displaying main application frame.
int g_NeedDBUpdate
0 - No update needed, 1 - Update needed because there is no chart database, inform user 2 - Start upd...
Miscellaneous globals primarely used by gui layer, not persisted in configuration file.
Instance check interface.
The local API has a server side handling commands and a client part issuing commands.
Enhanced logging interface on top of wx/log.h.
MacOS hardware probing functions.
Waypoint properties maintenance dialog.
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.
MySQL based storage for routes, tracks, etc.
MyConfig * pConfig
Global instance.
Navigation Utility Functions without GUI dependencies.
User notifications manager.
s57RegistrarMgr * m_pRegistrarMan
Global instance.
General observable implementation with several specializations.
OCPN_AUIManager * g_pauimgr
Global instance.
bool bGPSValid
Indicate whether the Global Navigation Satellite System (GNSS) has a valid position.
double gLat
Vessel's current latitude in decimal degrees.
double gCog
Course over ground in degrees (0-359.99).
double gSog
Speed over ground in knots.
double gLon
Vessel's current longitude in decimal degrees.
Position, course, speed, etc.
Plugin remote repositories installation and Uninstall/list operations.
PlugInManager * g_pi_manager
Global instance.
RoutePoint * pAnchorWatchPoint2
Global instance.
Routeman * g_pRouteMan
Global instance.
RouteList * pRouteList
Global instance.
RoutePoint * pAnchorWatchPoint1
Global instance.
RouteManagerDialog * pRouteManagerDialog
Global instance.
S57 object query result window.
Select * pSelect
Global instance.
Select * pSelectTC
Global instance.
Selected route, segment, waypoint, etc.
SENCThreadManager * g_SencThreadManager
Global instance.
TCMgr * ptcmgr
Global instance.
Tide and Current Manager @TODO Add original author copyright.
ThumbWin * pthumbwin
Global instance.
std::vector< Track * > g_TrackList
Global instance.
Recorded track abstraction.
Access checks for comm devices and dongle.