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"
119#include "model/nav_object_database.h"
131#include "ais_info_gui.h"
143#include "dialog_alert.h"
146#include "gdal/cpl_csv.h"
160#include "route_ctx_factory.h"
167#include "safe_mode_gui.h"
168#include "std_filesystem.h"
186void RedirectIOToConsole();
198#include "androidUTIL.h"
203using namespace std::literals::chrono_literals;
205const char *
const kUsage =
208 opencpn [-p] [-f] [-G] [-g] [-P] [-l <str>] [-u <num>] [-U] [-s] [GPX file ...]
209 opencpn --remote [-R] | -q] | -e] |-o <str>]
211Options for starting opencpn
213 -c, --configdir=<dirpath> Use alternative configuration directory.
214 -p, --portable Run in portable mode.
215 -f, --fullscreen Switch to full screen mode on start.
216 -G, --no_opengl Disable OpenGL video acceleration. This setting will
218 -g, --rebuild_gl_raster_cache Rebuild OpenGL raster cache on start.
219 -D, --rebuild_chart_db Rescan chart directories and rebuild the chart database
220 -P, --parse_all_enc Convert all S-57 charts to OpenCPN's internal format on start.
221 -l, --loglevel=<str> Amount of logging: error, warning, message, info, debug or trace
222 -u, --unit_test_1=<num> Display a slideshow of <num> charts and then exit.
223 Zero or negative <num> specifies no limit.
225 -s, --safe_mode Run without plugins, opengl and other "dangerous" stuff
226 -W, --config_wizard Start with initial configuration wizard
228Options manipulating already started opencpn
229 -r, --remote Execute commands on already running instance
230 -R, --raise Make running OpenCPN visible if hidden
231 -q, --quit Terminate already running opencpn
232 -e, --get_rest_endpoint Print rest server endpoint and exit.
233 -o, --open=<GPX file> Open file in running opencpn
236 GPX file GPX-formatted file with waypoints or routes.
240wxDEFINE_EVENT(EVT_N2K_129029, wxCommandEvent);
241wxDEFINE_EVENT(EVT_N2K_129026, wxCommandEvent);
243wxDEFINE_EVENT(EVT_N0183_RMC, wxCommandEvent);
244wxDEFINE_EVENT(EVT_N0183_HDT, wxCommandEvent);
245wxDEFINE_EVENT(EVT_N0183_HDG, wxCommandEvent);
246wxDEFINE_EVENT(EVT_N0183_HDM, wxCommandEvent);
247wxDEFINE_EVENT(EVT_N0183_VTG, wxCommandEvent);
248wxDEFINE_EVENT(EVT_N0183_GSV, wxCommandEvent);
249wxDEFINE_EVENT(EVT_N0183_GGA, wxCommandEvent);
250wxDEFINE_EVENT(EVT_N0183_GLL, wxCommandEvent);
251wxDEFINE_EVENT(EVT_N0183_AIVDO, wxCommandEvent);
261WX_DEFINE_OBJARRAY(ArrayOfCDI);
263static int user_user_id;
264static int file_user_id;
266static int g_mem_total, g_mem_initial;
268static unsigned int malloc_max;
270static int osMajor, osMinor;
272static bool g_bHasHwClock;
274#if wxUSE_XLOCALE || !wxCHECK_VERSION(3, 0, 0)
276wxLocale *plocale_def_lang = 0;
284extern sigjmp_buf env;
289DEFINE_GUID(GARMIN_DETECT_GUID, 0x2c9c45c2L, 0x8e7d, 0x4c08, 0xa1, 0x2d, 0x81,
290 0x6b, 0xba, 0xe7, 0x22, 0xc0);
294static const long long lNaN = 0xfff8000000000000;
295#define NAN (*(double *)&lNaN)
299void appendOSDirSlash(wxString *pString);
301void InitializeUserColors();
302void DeInitializeUserColors();
303void SetSystemColors(ColorScheme cs);
305static bool LoadAllPlugIns(
bool load_enabled) {
306 g_Platform->ShowBusySpinner();
307 bool b = PluginLoader::GetInstance()->
LoadAllPlugIns(load_enabled);
308 g_Platform->HideBusySpinner();
316#if defined(__WXGTK__) || defined(__WXQT__)
317#include "bitmaps/opencpn.xpm"
320wxString newPrivateFileName(wxString,
const char *name,
321 [[maybe_unused]]
const char *windowsName) {
322 wxString fname = wxString::FromUTF8(name);
323 wxString filePathAndName;
326 if (filePathAndName.Last() != wxFileName::GetPathSeparator())
327 filePathAndName.Append(wxFileName::GetPathSeparator());
330 wxString fwname = wxString::FromUTF8(windowsName);
331 filePathAndName.Append(fwname);
333 filePathAndName.Append(fname);
336 return filePathAndName;
342 : wxFrame(
nullptr, wxID_ANY,
"Loading...", wxDefaultPosition,
343 wxSize(900, 600), wxSTAY_ON_TOP) {
345 SetBackgroundColour(wxColour(0, 0, 0));
349 new wxStaticText(
this, wxID_ANY,
"", wxDefaultPosition, wxDefaultSize,
350 wxALIGN_CENTRE_HORIZONTAL);
351 text->SetForegroundColour(wxColour(255, 255, 255));
353 wxBoxSizer *sizer =
new wxBoxSizer(wxVERTICAL);
354 sizer->Add(text, 1, wxALIGN_CENTER);
355 SetSizerAndFit(sizer);
361bool ShowNavWarning() {
364OpenCPN is distributed in the hope that it will be useful, \
365but WITHOUT ANY WARRANTY; without even the implied \
366warranty of MERCHANTABILITY or FITNESS FOR A \
367PARTICULAR PURPOSE.\n\n\
368See the GNU General Public License for more details.\n\n\
369OpenCPN must only be used in conjunction with approved \
370paper charts and traditional methods of navigation.\n\n\
371DO NOT rely upon OpenCPN for safety of life or property.\n\n\
372Please click \"Agree\" and proceed, or \"Cancel\" to quit.\n"));
374 wxString vs = wxString::Format(
" .. Version %s", VERSION_FULL);
377 androidShowDisclaimer(_(
"OpenCPN for Android") + vs, msg);
380 msg.Replace(
"\n",
"<br>");
382 std::stringstream html;
383 html <<
"<html><body><p>";
384 html << msg.ToStdString();
385 html <<
"</p></body></html>";
387 std::string title = _(
"Welcome to OpenCPN").ToStdString();
388 std::string action = _(
"Agree").ToStdString();
390 info_dlg.SetInitialSize();
391 info_dlg.AddHtmlContent(html);
392 int agreed = info_dlg.ShowModal();
393 return agreed == wxID_OK;
397bool DoNavMessage(wxString &new_version_string) {
402 if (!n_NavMessageShown || (new_version_string != g_config_version_string) ||
403 (g_AndroidVersionCode != androidGetVersionCode())) {
407 if (!ShowNavWarning()) {
408 qDebug() <<
"Closing due to NavWarning Cancel";
414 n_NavMessageShown = 1;
418 g_AndroidVersionCode = androidGetVersionCode();
419 qDebug() <<
"Persisting Version Code: " << g_AndroidVersionCode;
424 if (!n_NavMessageShown || (new_version_string != g_config_version_string)) {
425 if (!ShowNavWarning())
return false;
426 n_NavMessageShown = 1;
439BEGIN_EVENT_TABLE(
MyApp, wxApp)
440EVT_ACTIVATE_APP(MyApp::OnActivateApp)
443static
void ActivateRoute(const std::
string &guid) {
446 wxLogMessage(
"Cannot activate guid: no such route");
457 point = route->GetPoint(2);
464static void ReverseRoute(
const std::string &guid) {
467 wxLogMessage(
"Cannot activate guid: no such route");
474void MyApp::InitRestListeners() {
475 auto activate_route = [&](wxCommandEvent ev) {
476 auto guid = ev.GetString().ToStdString();
480 auto reverse_route = [&](wxCommandEvent ev) {
481 auto guid = ev.GetString().ToStdString();
487void MyApp::OnUnhandledException() {
494 }
catch (std::exception &e) {
496 what.Printf(
"standard exception with message \"%s\"", e.what());
498 what.Printf(
"standard exception of type \"%s\" with message \"%s\"",
499 typeid(e).name(), e.what());
502 what =
"unknown exception";
504 wxMessageOutputBest().Printf(
505 "Unhandled %s; terminating %s.\n", what,
506 wxIsMainThread() ?
"the application" :
"the thread in which it happened");
509bool MyApp::OpenFile(
const std::string &path) {
511 auto result = nav_objects.load_file(path.c_str());
513 std::string s(_(
"Cannot load route or waypoint file: "));
514 s += std::string(
"\"") + path +
"\"";
515 wxMessageBox(s,
"OpenCPN", wxICON_WARNING | wxOK);
521 nav_objects.LoadAllGPXObjects(!nav_objects.IsOpenCPN(), wpt_dups,
true);
523 if (pRouteManagerDialog && pRouteManagerDialog->IsShown())
524 pRouteManagerDialog->UpdateLists();
525 LLBBox box = nav_objects.GetBBox();
526 if (box.GetValid()) {
527 gFrame->CenterView(gFrame->GetPrimaryCanvas(), box);
533void MyApp::OnInitCmdLine(wxCmdLineParser &parser) {
536 parser.AddSwitch(
"h",
"help",
"", wxCMD_LINE_OPTION_HELP);
537 parser.AddSwitch(
"p",
"portable");
538 parser.AddOption(
"c",
"configdir",
"", wxCMD_LINE_VAL_STRING,
539 wxCMD_LINE_PARAM_OPTIONAL);
540 parser.AddSwitch(
"f",
"fullscreen");
541 parser.AddSwitch(
"G",
"no_opengl");
542 parser.AddSwitch(
"W",
"config_wizard");
543 parser.AddSwitch(
"g",
"rebuild_gl_raster_cache");
544 parser.AddSwitch(
"D",
"rebuild_chart_db");
545 parser.AddSwitch(
"P",
"parse_all_enc");
546 parser.AddOption(
"l",
"loglevel");
547 parser.AddOption(
"u",
"unit_test_1",
"", wxCMD_LINE_VAL_NUMBER);
548 parser.AddSwitch(
"U",
"unit_test_2");
549 parser.AddParam(
"import GPX files", wxCMD_LINE_VAL_STRING,
550 wxCMD_LINE_PARAM_OPTIONAL | wxCMD_LINE_PARAM_MULTIPLE);
551 parser.AddSwitch(
"s",
"safe_mode");
552 parser.AddSwitch(
"r",
"remote");
553 parser.AddSwitch(
"R",
"raise");
554 parser.AddSwitch(
"q",
"quit");
555 parser.AddSwitch(
"e",
"get_rest_endpoint");
556 parser.AddOption(
"o",
"open",
"", wxCMD_LINE_VAL_STRING,
557 wxCMD_LINE_PARAM_OPTIONAL | wxCMD_LINE_PARAM_MULTIPLE);
563static void ParseLoglevel(wxCmdLineParser &parser) {
564 wxLog::SetLogLevel(wxLOG_Message);
567static void ParseLoglevel(wxCmdLineParser &parser) {
568 const char *strLevel = std::getenv(
"OPENCPN_LOGLEVEL");
569 strLevel = strLevel ? strLevel :
"info";
571 if (parser.Found(
"l", &wxLevel)) {
572 strLevel = wxLevel.c_str();
574 wxLogLevel level = OcpnLog::str2level(strLevel);
575 if (level == OcpnLog::LOG_BADLEVEL) {
576 fprintf(stderr,
"Bad loglevel %s, using \"info\"", strLevel);
579 wxLog::SetLogLevel(level);
584bool MyApp::OnCmdLineHelp(wxCmdLineParser &parser) {
591bool MyApp::OnCmdLineParsed(wxCmdLineParser &parser) {
596 g_unit_test_2 = parser.Found(
"unit_test_2");
597 g_bportable = parser.Found(
"p");
598 g_start_fullscreen = parser.Found(
"fullscreen");
599 g_bdisable_opengl = parser.Found(
"no_opengl");
600 g_rebuild_gl_cache = parser.Found(
"rebuild_gl_raster_cache");
602 g_parse_all_enc = parser.Found(
"parse_all_enc");
603 g_config_wizard = parser.Found(
"config_wizard");
604 if (parser.Found(
"unit_test_1", &number)) {
605 g_unit_test_1 =
static_cast<int>(number);
606 if (g_unit_test_1 == 0) g_unit_test_1 = -1;
608 safe_mode::set_mode(parser.Found(
"safe_mode"));
609 ParseLoglevel(parser);
611 if (parser.Found(
"configdir", &wxstr)) {
612 g_configdir = wxstr.ToStdString();
613 fs::path path(g_configdir);
614 if (!fs::exists(path) || !fs::is_directory(path)) {
615 std::cerr << g_configdir <<
" is not an existing directory.\n";
620 bool has_start_options =
false;
621 static const std::vector<std::string> kStartOptions = {
626 "rebuild_gl_raster_cache",
632 for (
const auto &opt : kStartOptions) {
633 if (parser.Found(opt)) has_start_options =
true;
635 if (has_start_options && parser.Found(
"remote")) {
636 std::cerr <<
"this option is not compatible with --remote\n";
640 bool has_remote_options =
false;
641 static const std::vector<std::string> kRemoteOptions = {
642 "raise",
"quit",
"open",
"get_rest_endpoint"};
643 for (
const auto &opt : kRemoteOptions) {
644 if (parser.Found(opt)) has_remote_options =
true;
646 if (has_remote_options && !parser.Found(
"remote")) {
647 std::cerr <<
"This option requires --remote\n";
651 for (
size_t paramNr = 0; paramNr < parser.GetParamCount(); ++paramNr)
652 g_params.push_back(parser.GetParam(paramNr).ToStdString());
655 if (!parser.Found(
"remote"))
656 m_parsed_cmdline = ParsedCmdline();
657 else if (parser.Found(
"raise"))
658 m_parsed_cmdline = ParsedCmdline(CmdlineAction::Raise);
659 else if (parser.Found(
"quit"))
660 m_parsed_cmdline = ParsedCmdline(CmdlineAction::Quit);
661 else if (parser.Found(
"get_rest_endpoint"))
662 m_parsed_cmdline = ParsedCmdline(CmdlineAction::GetRestEndpoint);
663 else if (parser.Found(
"open", &optarg))
664 m_parsed_cmdline = ParsedCmdline(CmdlineAction::Open, optarg.ToStdString());
665 else if (parser.GetParamCount() == 1)
667 ParsedCmdline(CmdlineAction::Open, parser.GetParam(0).ToStdString());
668 else if (!has_start_options && !has_remote_options) {
670 m_parsed_cmdline = ParsedCmdline(CmdlineAction::Raise);
680bool MyApp::OnExceptionInMainLoop() {
681 wxLogWarning(
"Caught MainLoopException, continuing...");
686void MyApp::OnActivateApp(wxActivateEvent &event) {
return; }
688static wxStopWatch init_sw;
691 if (m_exitcode != -2)
return m_exitcode;
692 return wxAppConsole::OnRun();
703 if (!wxGetEnv(
"OCPN_DISABLE_X11_GDK_BACKEND", NULL)) {
704 if (wxGetEnv(
"WAYLAND_DISPLAY", NULL)) {
705 setenv(
"GDK_BACKEND",
"x11", 1);
709 "mesa_glthread",
"false",
718 if (!wxApp::OnInit())
return false;
721 androidEnableBackButton(
false);
722 androidEnableOptionItems(
false);
727#if defined(__WXGTK__) && defined(ocpnUSE_GLES) && defined(__ARM_ARCH)
733 wxBitmap bmp(10, 10, -1);
735 dc.SelectObject(bmp);
736 dc.DrawText(
"X", 0, 0);
748 if (m_parsed_cmdline.action == CmdlineAction::Skip) {
752 std::cerr <<
"No remote opencpn found. Giving up.\n";
757 std::unique_ptr<LocalClientApi> client;
759 client = LocalClientApi::GetClient();
761 WARNING_LOG <<
"Ipc client exception: " << ie.str();
768 wxMessageBox(_(
"Sorry, an existing instance of OpenCPN may be too busy "
769 "to respond.\nPlease retry."),
770 "OpenCPN", wxICON_INFORMATION | wxOK);
775 auto result = client->HandleCmdline(m_parsed_cmdline.action,
776 m_parsed_cmdline.arg);
780 wxLogDebug(
"Error running remote command: %s", result.second.c_str());
789 if (getenv(
"OPENCPN_FATAL_ERROR") != 0) {
790 wxLogFatalError(getenv(
"OPENCPN_FATAL_ERROR"));
795 if (!safe_mode::get_mode()) {
801 OCPNPlatform::Initialize_1();
806 MyApp::SetAppDisplayName(
"OpenCPN");
809 wxDateTime x = wxDateTime::UNow();
810 long seed = x.GetMillisecond();
811 seed *= x.GetTicks();
816 setlocale(LC_NUMERIC,
"C");
818 g_start_time = wxDateTime::Now();
820 g_loglast_time = g_start_time;
821 g_loglast_time.MakeGMT();
822 g_loglast_time.Subtract(
823 wxTimeSpan(0, 29, 0, 0));
825 AnchorPointMinDist = 5.0;
831 platform::GetMemoryStatus(&g_mem_total, &g_mem_initial);
835 wxFont temp_font(10, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL,
836 wxFONTWEIGHT_NORMAL, FALSE, wxString(
""),
837 wxFONTENCODING_SYSTEM);
838 temp_font.SetDefaultEncoding(wxFONTENCODING_SYSTEM);
841 auto ¬eman = NotificationManager::GetInstance();
842 noteman.ScrubNotificationDirectory(30);
845 if (!g_Platform->InitializeLogFile()) {
857 wxLogMessage(
"\n\n________\n");
859 wxDateTime now = wxDateTime::Now();
860 LOG_INFO(
"------- OpenCPN version %s restarted at %s -------\n", VERSION_FULL,
861 now.FormatISODate().mb_str().data());
862 wxLogLevel level = wxLog::GetLogLevel();
863 LOG_INFO(
"Using loglevel %s", OcpnLog::level2str(level).c_str());
865 wxString wxver(wxVERSION_STRING);
866 wxver.Prepend(
"wxWidgets version: ");
868 wxPlatformInfo platforminfo = wxPlatformInfo::Get();
872 os_name = platforminfo.GetOperatingSystemIdName();
874 os_name = platforminfo.GetOperatingSystemFamilyName();
877 wxString platform = os_name +
" " + platforminfo.GetArchName() +
" " +
878 platforminfo.GetPortIdName();
880 wxLogMessage(wxver +
" " + platform);
882 ::wxGetOsVersion(&osMajor, &osMinor);
883 wxString osVersionMsg;
884 osVersionMsg.Printf(
"OS Version reports as: %d.%d", osMajor, osMinor);
885 wxLogMessage(osVersionMsg);
887 wxLogMessage(
"MemoryStatus: mem_total: %d mb, mem_initial: %d mb",
888 g_mem_total / 1024, g_mem_initial / 1024);
893 if (!detail->osd_names_like.empty())
894 like0 = detail->osd_names_like[0].c_str();
895 msgplat.Printf(
"OCPN_OSDetail: %s ; %s ; %s ; %s ; %s",
896 detail->osd_arch.c_str(), detail->osd_name.c_str(),
897 detail->osd_version.c_str(), detail->osd_ID.c_str(),
899 wxLogMessage(msgplat);
901 wxString imsg =
"SData_Locn is ";
902 imsg += g_Platform->GetSharedDataDir();
906 ::wxInitAllImageHandlers();
910 prepareAndroidStyleSheets();
914 pInit_Chart_Dir =
new wxString();
919 imsg =
"PrivateDataDir is ";
925 navutil::InitGlobals();
929 new Routeman(RoutePropDlg::GetDlgCtx(), RoutemanGui::GetDlgCtx());
933 pSelect->SetSelectPixelRadius(12);
946 g_pais_query_dialog_active = NULL;
949 g_hostname = ::wxGetHostName();
950 if (g_hostname.IsEmpty()) g_hostname = wxGetUserName();
952 androidGetDeviceInfo();
953 g_hostname = wxString(
"Android-") + g_android_Device_Model;
954 g_hostname.Replace(
" ",
"-",
true);
959 wxString p(
"Portable-");
960 g_hostname = p + g_hostname;
965 pLayerList =
new LayerList;
970 auto &navobj_db = NavObj_dB::GetInstance();
977#ifdef PROBE_PORTS__WITH_HELPER
978 user_user_id = getuid();
979 file_user_id = geteuid();
983 bool b_initial_load =
false;
985 wxFileName config_test_file_name(g_Platform->GetConfigFileName());
986 if (config_test_file_name.FileExists())
987 wxLogMessage(
"Using existing Config_File: " +
988 g_Platform->GetConfigFileName());
991 wxLogMessage(
"Creating new Config_File: " +
992 g_Platform->GetConfigFileName());
994 b_initial_load =
true;
997 config_test_file_name.DirExists(config_test_file_name.GetPath()))
998 if (!config_test_file_name.Mkdir(config_test_file_name.GetPath()))
999 wxLogMessage(
"Cannot create config file directory for " +
1000 g_Platform->GetConfigFileName());
1005 pConfig = g_Platform->GetConfigObject();
1006 InitBaseConfig(pConfig);
1007 pConfig->LoadMyConfig();
1009 if (g_kiosk_startup) {
1011 g_wallpaper->ShowFullScreen(
true);
1012 g_wallpaper->Show();
1017 if (b_initial_load) g_Platform->SetDefaultOptions();
1019 g_Platform->applyExpertMode(g_bUIexpert);
1024 g_StyleManager->SetStyle(
"MUI_flat");
1025 if (!g_StyleManager->IsOK()) {
1026 wxString msg = _(
"Failed to initialize the user interface. ");
1027 msg << _(
"OpenCPN cannot start. ");
1028 msg << _(
"The necessary configuration files were not found. ");
1029 msg << _(
"See the log file at ") << g_Platform->GetLogFileName()
1030 << _(
" for details.") <<
"\n\n";
1031 msg << g_Platform->GetSharedDataDir();
1033 wxMessageDialog w(NULL, msg, _(
"Failed to initialize the user interface. "),
1034 wxCANCEL | wxICON_ERROR);
1041 if (style) style->chartStatusWindowTransparent =
true;
1045 pWayPointMan = NULL;
1049 msg.Printf(
"Detected display size (horizontal): %d mm",
1054 if (g_config_display_size_manual &&
1059 msg.Printf(
"Display size (horizontal) config override: %d mm",
1068 int SelectPixelRadius = 50;
1070 pSelect->SetSelectPixelRadius(SelectPixelRadius);
1071 pSelectTC->SetSelectPixelRadius(wxMax(25, SelectPixelRadius));
1072 pSelectAIS->SetSelectPixelRadius(SelectPixelRadius);
1076 if (!n_NavMessageShown) {
1083#if wxUSE_XLOCALE || !wxCHECK_VERSION(3, 0, 0)
1086 g_Platform->SetLocaleSearchPrefixes();
1088 wxString def_lang_canonical = g_Platform->GetDefaultSystemLocale();
1090 imsg =
"System default Language: " + def_lang_canonical;
1093 wxString cflmsg =
"Config file language: " + g_locale;
1094 wxLogMessage(cflmsg);
1096 if (g_locale.IsEmpty()) {
1097 g_locale = def_lang_canonical;
1098 cflmsg =
"Config file language empty, using system default: " + g_locale;
1099 wxLogMessage(cflmsg);
1103 g_locale = g_Platform->GetAdjustedAppLocale();
1104 cflmsg =
"Adjusted App language: " + g_locale;
1105 wxLogMessage(cflmsg);
1108 g_Platform->ChangeLocale(g_locale, plocale_def_lang, &plocale_def_lang);
1110 imsg =
"Opencpn language set to: ";
1117 if (g_locale ==
"fr_FR") g_b_assume_azerty =
true;
1119 wxLogMessage(
"wxLocale support not available");
1124 if (g_config_wizard || b_initial_load) {
1126 auto res = wiz.Run();
1137 wxString vs = wxString(
"Version ") + VERSION_FULL +
" Build " + VERSION_DATE;
1138 g_bUpgradeInProcess = (vs != g_config_version_string);
1140 g_Platform->SetUpgradeOptions(vs, g_config_version_string);
1143 if (!g_Platform->GetLargeLogMessage().IsEmpty()) {
1144 wxLogMessage(g_Platform->GetLargeLogMessage());
1149 g_bdisable_opengl =
true;
1152 if (g_bdisable_opengl) g_bopengl =
false;
1154#if defined(__linux__) && !defined(__ANDROID__)
1155 if (g_bSoftwareGL) {
1156 setenv(
"LIBGL_ALWAYS_SOFTWARE",
"1", 1);
1169 if (0 == g_memCacheLimit) g_memCacheLimit = (int)(g_mem_total * 0.5);
1171 wxMin(g_memCacheLimit, 1024 * 1024);
1177 g_memCacheLimit = 0;
1178 if (0 == g_nCacheLimit)
1179 g_nCacheLimit = CACHE_N_LIMIT_DEFAULT;
1184 "chartlist.dat",
"CHRTLIST.DAT");
1188 "mmsitoname.csv",
"MMSINAME.CSV");
1191 if (pInit_Chart_Dir->IsEmpty()) {
1192 wxStandardPaths &std_path = g_Platform->GetStdPaths();
1196 pInit_Chart_Dir->Append(std_path.GetDocumentsDir());
1198 pInit_Chart_Dir->Append(androidGetExtStorageDir());
1202 InitRestListeners();
1205 gDefaultWorldMapLocation =
"gshhs";
1206 gDefaultWorldMapLocation.Prepend(g_Platform->GetSharedDataDir());
1207 gDefaultWorldMapLocation.Append(wxFileName::GetPathSeparator());
1208 if (gWorldMapLocation == wxEmptyString) {
1209 gWorldMapLocation = gDefaultWorldMapLocation;
1214 wxString default_tcdata0 =
1215 (g_Platform->GetSharedDataDir() +
"tcdata" +
1216 wxFileName::GetPathSeparator() +
"harmonics-dwf-20210110-free.tcd");
1217 wxString default_tcdata1 =
1218 (g_Platform->GetSharedDataDir() +
"tcdata" +
1219 wxFileName::GetPathSeparator() +
"HARMONICS_NO_US.IDX");
1221 if (TideCurrentDataSet.empty()) {
1222 TideCurrentDataSet.push_back(
1223 g_Platform->NormalizePath(default_tcdata0).ToStdString());
1224 TideCurrentDataSet.push_back(
1225 g_Platform->NormalizePath(default_tcdata1).ToStdString());
1230 if (g_sAIS_Alert_Sound_File.IsEmpty()) {
1231 wxString default_sound = (g_Platform->GetSharedDataDir() +
"sounds" +
1232 wxFileName::GetPathSeparator() +
"2bells.wav");
1233 g_sAIS_Alert_Sound_File = g_Platform->NormalizePath(default_sound);
1238 g_Platform->Initialize_2();
1240 LoadChartDatabase();
1244 g_kiosk_startup =
false;
1248 if (!g_kiosk_startup) {
1250 SetTopWindow(gFrame);
1255 gFrame->InitTimer.Start(50, wxTIMER_CONTINUOUS);
1261 OCPNPlatform::Initialize_4();
1264 androidHideBusyIcon();
1267 wxString::Format(_(
"OpenCPN Initialized in %ld ms."), init_sw.Time()));
1271 if (!g_kiosk_startup) {
1272 if (!DoNavMessage(vs)) {
1279 g_bHasHwClock =
true;
1280#if defined(__UNIX__) && !defined(__ANDROID__)
1283 ((stat(
"/dev/rtc", &buffer) == 0) || (stat(
"/dev/rtc0", &buffer) == 0) ||
1284 (stat(
"/dev/misc/rtc", &buffer) == 0));
1287 g_config_version_string = vs;
1290 pConfig->UpdateSettings();
1292 for (
auto *cp : TheConnectionParams()) {
1294 if (cp->GetDSPort().Contains(
"Serial")) {
1295 std::string port(cp->Port.ToStdString());
1303 m_comm_bridge.Initialize();
1305 std::vector<std::string> ipv4_addrs = get_local_ipv4_addresses();
1308 if (ipv4_addrs.size()) {
1309 std::string ipAddr = ipv4_addrs[0];
1312 if (data_dir.Last() != wxFileName::GetPathSeparator())
1313 data_dir.Append(wxFileName::GetPathSeparator());
1315 make_certificate(ipAddr, data_dir.ToStdString());
1317 m_rest_server.
StartServer(fs::path(data_dir.ToStdString()));
1318 StartMDNSService(g_hostname.ToStdString(),
"opencpn-object-control-service",
1329 g_wallpaper->Show(
false);
1332 gFrame->ShowFullScreen(
true);
1333 g_bFullscreen =
true;
1336 g_wallpaper->Destroy();
1337 g_wallpaper =
nullptr;
1339 SetTopWindow(gFrame);
1342 wxString vs = wxString(
"Version ") + VERSION_FULL +
" Build " + VERSION_DATE;
1343 if (!DoNavMessage(vs)) {
1348 gFrame->InitTimer.Start(50, wxTIMER_CONTINUOUS);
1354 wxSize new_frame_size(-1, -1);
1356 ::wxClientDisplayRect(&cx, &cy, &cw, &ch);
1358 InitializeUserColors();
1360 if ((g_nframewin_x > 100) && (g_nframewin_y > 100) && (g_nframewin_x <= cw) &&
1361 (g_nframewin_y <= ch))
1362 new_frame_size.Set(g_nframewin_x, g_nframewin_y);
1364 new_frame_size.Set(cw * 7 / 10, ch * 7 / 10);
1370 if ((g_lastClientRectx != cx) || (g_lastClientRecty != cy) ||
1371 (g_lastClientRectw != cw) || (g_lastClientRecth != ch)) {
1372 new_frame_size.Set(cw * 7 / 10, ch * 7 / 10);
1373 g_bframemax =
false;
1376 g_lastClientRectx = cx;
1377 g_lastClientRecty = cy;
1378 g_lastClientRectw = cw;
1379 g_lastClientRecth = ch;
1382 wxPoint position(0, 0);
1383 wxSize dsize = wxGetDisplaySize();
1386 g_nframewin_posy = wxMax(g_nframewin_posy, 22);
1389 if ((g_nframewin_posx < dsize.x) && (g_nframewin_posy < dsize.y))
1390 position = wxPoint(g_nframewin_posx, g_nframewin_posy);
1395 frame_rect.left = position.x;
1396 frame_rect.top = position.y;
1397 frame_rect.right = position.x + new_frame_size.x;
1398 frame_rect.bottom = position.y + new_frame_size.y;
1402 if (NULL == MonitorFromRect(&frame_rect, MONITOR_DEFAULTTONULL))
1403 position = wxPoint(10, 10);
1408 const wxPoint ptScreen(position.x, position.y);
1409 const int displayIndex = wxDisplay::GetFromPoint(ptScreen);
1411 if (displayIndex == wxNOT_FOUND) position = wxPoint(10, 30);
1414 g_nframewin_posx = position.x;
1415 g_nframewin_posy = position.y;
1418 wxSize asz = getAndroidDisplayDimensions();
1423 if ((cw > 200) && (ch > 200))
1424 new_frame_size.Set(cw, ch);
1426 new_frame_size.Set(800, 400);
1430 long app_style = wxDEFAULT_FRAME_STYLE;
1431 app_style |= wxWANTS_CHARS;
1436 wxString short_version_name = wxString(PACKAGE_VERSION).BeforeFirst(
'+');
1437 wxString myframe_window_title = wxString(
"OpenCPN " + short_version_name);
1440 myframe_window_title += _(
" -- [Portable(-p) executing from ");
1441 myframe_window_title += g_Platform->GetHomeDir();
1442 myframe_window_title +=
"]";
1446 fmsg.Printf(
"Creating MyFrame...size(%d, %d) position(%d, %d)",
1447 new_frame_size.x, new_frame_size.y, position.x, position.y);
1451 auto dockart =
new wxAuiDefaultDockArt;
1454 gFrame =
new MyFrame(NULL, myframe_window_title, position, new_frame_size,
1455 app_style, dockart);
1460 g_pauimgr->SetDockSizeConstraint(.9, .9);
1466 g_Platform->Initialize_3();
1468 gFrame->CreateCanvasLayout();
1472 gFrame->SetChartUpdatePeriod();
1476 gFrame->ApplyGlobalSettings(
false);
1477 gFrame->SetAllToolbarScale();
1478 gFrame->SetAndApplyColorScheme(global_color_scheme);
1479 if (g_bframemax) gFrame->Maximize(
true);
1482 if (g_bresponsive && (gFrame->GetPrimaryCanvas()->
GetPixPerMM() > 4.0))
1483 gFrame->Maximize(
true);
1492 if (g_rebuild_gl_cache && g_bopengl && g_GLOptions.m_bTextureCompression &&
1493 g_GLOptions.m_bTextureCompressionCaching) {
1494 gFrame->ReloadAllVP();
1513 if ((gps_watchdog_timeout_ticks > 60) || (gps_watchdog_timeout_ticks <= 0))
1514 gps_watchdog_timeout_ticks = (GPS_TIMEOUT_SECONDS * 1000) / TIMER_GFRAME_1;
1517 dogmsg.Printf(
"GPS Watchdog Timeout is: %d sec.", gps_watchdog_timeout_ticks);
1518 wxLogMessage(dogmsg);
1520 sat_watchdog_timeout_ticks = gps_watchdog_timeout_ticks;
1528 if (g_bTrackCarryOver) g_bDeferredStartTrack =
true;
1533 gFrame->DoChartUpdate();
1536 for (
auto *cp : TheConnectionParams()) {
1539 cp->b_IsSetup = TRUE;
1545 auto style = g_StyleManager->GetCurrentStyle();
1546 auto bitmap =
new wxBitmap(style->GetIcon(
"default_pi", 32, 32));
1548 PluginLoader::GetInstance()->SetPluginDefaultIcon(bitmap);
1550 wxLogWarning(
"Cannot initiate plugin default jigsaw icon.");
1560 wxString perspective;
1561 pConfig->SetPath(
"/AUI");
1562 pConfig->Read(
"AUIPerspective", &perspective);
1569 bool bno_load =
false;
1571 wxArrayString name_array;
1572 wxStringTokenizer st(perspective,
"|;");
1573 while (st.HasMoreTokens()) {
1574 wxString s1 = st.GetNextToken();
1575 if (s1.StartsWith(
"name=")) {
1576 wxString sc = s1.AfterFirst(
'=');
1581 wxAuiPaneInfoArray pane_array_val =
g_pauimgr->GetAllPanes();
1582 for (
unsigned int i = 0; i < pane_array_val.GetCount(); i++) {
1583 wxAuiPaneInfo pane = pane_array_val.Item(i);
1587 if (name_array.Index(pane.name) == wxNOT_FOUND) {
1593 if (!bno_load)
g_pauimgr->LoadPerspective(perspective,
false);
1598 for (
unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
1601 wxSize frameSize = GetClientSize();
1602 wxSize minSize =
g_pauimgr->GetPane(cc).min_size;
1603 int width = wxMax(minSize.x, frameSize.x / 10);
1604 g_pauimgr->GetPane(cc).MinSize(frameSize.x * 1 / 5, frameSize.y);
1626 if (!g_bdisable_opengl) {
1629 if (pgl && (pgl->GetRendererString().Find(
"UniChrome") != wxNOT_FOUND)) {
1630 gFrame->m_defer_size = gFrame->GetSize();
1631 gFrame->SetSize(gFrame->m_defer_size.x - 10, gFrame->m_defer_size.y);
1633 gFrame->m_bdefer_resize =
true;
1643 glDeleteTextures(n, texts);
1652 gFrame->SetSize(getAndroidDisplayDimensions());
1653 androidSetFollowTool(gFrame->GetPrimaryCanvas()->m_bFollow ? 1 : 0,
true);
1665void MyApp::LoadChartDatabase() {
1667 ArrayOfCDI ChartDirArray;
1668 pConfig->LoadChartDirArray(ChartDirArray);
1673 if (g_bFirstRun && (ChartDirArray.GetCount() == 0)) {
1676 wxRegKey RegKey(wxString(
"HKEY_LOCAL_MACHINE\\SOFTWARE\\OpenCPN"));
1677 if (RegKey.Exists()) {
1679 _(
"Retrieving initial Chart Directory set from Windows Registry"));
1681 RegKey.QueryValue(wxString(
"ChartDirs"), dirs);
1683 wxStringTokenizer tkz(dirs,
";");
1684 while (tkz.HasMoreTokens()) {
1685 wxString token = tkz.GetNextToken();
1688 cdi.fullpath = token.Trim();
1689 cdi.magic_number =
"";
1691 ChartDirArray.Add(cdi);
1698 cdi.fullpath =
"charts";
1699 cdi.fullpath.Prepend(g_Platform->GetSharedDataDir());
1700 cdi.magic_number =
"";
1701 ChartDirArray.Add(cdi);
1705 if (ndirs) pConfig->UpdateChartDirs(ChartDirArray);
1715 if (!ChartDirArray.GetCount())
1716 if (::wxFileExists(ChartListFileName)) ::wxRemoveFile(ChartListFileName);
1726 if (g_restore_dbindex >= 0) {
1727 if (
ChartData->GetChartTableEntries() == 0)
1728 g_restore_dbindex = -1;
1730 else if (g_restore_dbindex > (
ChartData->GetChartTableEntries() - 1))
1731 g_restore_dbindex = 0;
1739 wxLogMessage(
"opencpn::MyApp starting exit.");
1741 m_usb_watcher.Stop();
1744 wxDateTime lognow = wxDateTime::Now();
1746 wxString day = lognow.FormatISODate();
1747 wxString utc = lognow.FormatISOTime();
1748 wxString navmsg =
"LOGBOOK: ";
1756 data.Printf(
"OFF: Lat %10.5f Lon %10.5f ",
gLat,
gLon);
1760 if (std::isnan(
gCog))
1761 cog.Printf(
"COG ----- ");
1763 cog.Printf(
"COG %10.5f ",
gCog);
1766 if (std::isnan(
gSog))
1767 sog.Printf(
"SOG ----- ");
1769 sog.Printf(
"SOG %6.2f " + getUsrSpeedUnit(), toUsrSpeed(
gSog));
1776 data.Printf(
"OFF: Lat %10.5f Lon %10.5f",
gLat,
gLon);
1779 wxLogMessage(navmsg);
1780 g_loglast_time = lognow;
1798 for (
unsigned int igroup = 0; igroup <
g_pGroupArray->GetCount();
1807 wxLogMessage(
"opencpn::MyApp exiting cleanly...\n");
1808 wxLog::FlushActive();
1810 g_Platform->CloseLogFile();
1812 delete pInit_Chart_Dir;
1820 delete pWayPointMan;
1822 navutil::DeinitGlobals();
1824 DeInitializeUserColors();
1831 delete g_StyleManager;
1836 void RestoreSystemColors();
1837 RestoreSystemColors();
1844#if wxUSE_XLOCALE || !wxCHECK_VERSION(3, 0, 0)
1848 FontMgr::Shutdown();
1850 g_Platform->OnExit_2();
1857#ifdef LINUX_CRASHRPT
1858void MyApp::OnFatalException() { g_crashprint.Report(); }
1864void MyCPLErrorHandler(CPLErr eErrClass,
int nError,
const char *pszErrorMsg)
1869 if (eErrClass == CE_Debug)
1870 snprintf(msg, 255,
"CPL: %s", pszErrorMsg);
1871 else if (eErrClass == CE_Warning)
1872 snprintf(msg, 255,
"CPL Warning %d: %s", nError, pszErrorMsg);
1874 snprintf(msg, 255,
"CPL ERROR %d: %s", nError, pszErrorMsg);
1876 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.
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().
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.
EventVar on_routes_update
Notified when list of routes is updated (no data in event)
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.
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.
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.
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.