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->Show();
1021 if (b_initial_load) g_Platform->SetDefaultOptions();
1023 g_Platform->applyExpertMode(g_bUIexpert);
1028 g_StyleManager->SetStyle(
"MUI_flat");
1029 if (!g_StyleManager->IsOK()) {
1030 wxString msg = _(
"Failed to initialize the user interface. ");
1031 msg << _(
"OpenCPN cannot start. ");
1032 msg << _(
"The necessary configuration files were not found. ");
1033 msg << _(
"See the log file at ") << g_Platform->GetLogFileName()
1034 << _(
" for details.") <<
"\n\n";
1035 msg << g_Platform->GetSharedDataDir();
1037 wxMessageDialog w(NULL, msg, _(
"Failed to initialize the user interface. "),
1038 wxCANCEL | wxICON_ERROR);
1045 if (style) style->chartStatusWindowTransparent =
true;
1049 pWayPointMan = NULL;
1053 msg.Printf(
"Detected display size (horizontal): %d mm",
1058 if (g_config_display_size_manual &&
1063 msg.Printf(
"Display size (horizontal) config override: %d mm",
1072 int SelectPixelRadius = 50;
1074 pSelect->SetSelectPixelRadius(SelectPixelRadius);
1075 pSelectTC->SetSelectPixelRadius(wxMax(25, SelectPixelRadius));
1076 pSelectAIS->SetSelectPixelRadius(SelectPixelRadius);
1080 if (!n_NavMessageShown) {
1087#if wxUSE_XLOCALE || !wxCHECK_VERSION(3, 0, 0)
1090 g_Platform->SetLocaleSearchPrefixes();
1092 wxString def_lang_canonical = g_Platform->GetDefaultSystemLocale();
1094 imsg =
"System default Language: " + def_lang_canonical;
1097 wxString cflmsg =
"Config file language: " + g_locale;
1098 wxLogMessage(cflmsg);
1100 if (g_locale.IsEmpty()) {
1101 g_locale = def_lang_canonical;
1102 cflmsg =
"Config file language empty, using system default: " + g_locale;
1103 wxLogMessage(cflmsg);
1107 g_locale = g_Platform->GetAdjustedAppLocale();
1108 cflmsg =
"Adjusted App language: " + g_locale;
1109 wxLogMessage(cflmsg);
1112 g_Platform->ChangeLocale(g_locale, plocale_def_lang, &plocale_def_lang);
1114 imsg =
"Opencpn language set to: ";
1121 if (g_locale ==
"fr_FR") g_b_assume_azerty =
true;
1123 wxLogMessage(
"wxLocale support not available");
1128 if (g_config_wizard || b_initial_load) {
1130 auto res = wiz.Run();
1141 wxString vs = wxString(
"Version ") + VERSION_FULL +
" Build " + VERSION_DATE;
1142 g_bUpgradeInProcess = (vs != g_config_version_string);
1144 g_Platform->SetUpgradeOptions(vs, g_config_version_string);
1147 if (!g_Platform->GetLargeLogMessage().IsEmpty()) {
1148 wxLogMessage(g_Platform->GetLargeLogMessage());
1153 g_bdisable_opengl =
true;
1156 if (g_bdisable_opengl) g_bopengl =
false;
1158#if defined(__linux__) && !defined(__ANDROID__)
1159 if (g_bSoftwareGL) {
1160 setenv(
"LIBGL_ALWAYS_SOFTWARE",
"1", 1);
1173 if (0 == g_memCacheLimit) g_memCacheLimit = (int)(g_mem_total * 0.5);
1175 wxMin(g_memCacheLimit, 1024 * 1024);
1181 g_memCacheLimit = 0;
1182 if (0 == g_nCacheLimit)
1183 g_nCacheLimit = CACHE_N_LIMIT_DEFAULT;
1188 "chartlist.dat",
"CHRTLIST.DAT");
1192 "mmsitoname.csv",
"MMSINAME.CSV");
1195 if (pInit_Chart_Dir->IsEmpty()) {
1196 wxStandardPaths &std_path = g_Platform->GetStdPaths();
1200 pInit_Chart_Dir->Append(std_path.GetDocumentsDir());
1202 pInit_Chart_Dir->Append(androidGetExtStorageDir());
1206 InitRestListeners();
1209 gDefaultWorldMapLocation =
"gshhs";
1210 gDefaultWorldMapLocation.Prepend(g_Platform->GetSharedDataDir());
1211 gDefaultWorldMapLocation.Append(wxFileName::GetPathSeparator());
1212 if (gWorldMapLocation == wxEmptyString) {
1213 gWorldMapLocation = gDefaultWorldMapLocation;
1218 wxString default_tcdata0 =
1219 (g_Platform->GetSharedDataDir() +
"tcdata" +
1220 wxFileName::GetPathSeparator() +
"harmonics-dwf-20210110-free.tcd");
1221 wxString default_tcdata1 =
1222 (g_Platform->GetSharedDataDir() +
"tcdata" +
1223 wxFileName::GetPathSeparator() +
"HARMONICS_NO_US.IDX");
1224 wxString default_tcdata2 =
1225 (g_Platform->GetSharedDataDir() +
"tcdata" +
1226 wxFileName::GetPathSeparator() +
"ticon-europe-global.tcd");
1228 if (TideCurrentDataSet.empty()) {
1229 TideCurrentDataSet.push_back(
1230 g_Platform->NormalizePath(default_tcdata0).ToStdString());
1231 TideCurrentDataSet.push_back(
1232 g_Platform->NormalizePath(default_tcdata1).ToStdString());
1233 TideCurrentDataSet.push_back(
1234 g_Platform->NormalizePath(default_tcdata2).ToStdString());
1239 if (g_sAIS_Alert_Sound_File.IsEmpty()) {
1240 wxString default_sound = (g_Platform->GetSharedDataDir() +
"sounds" +
1241 wxFileName::GetPathSeparator() +
"2bells.wav");
1242 g_sAIS_Alert_Sound_File = g_Platform->NormalizePath(default_sound);
1247 g_Platform->Initialize_2();
1249 LoadChartDatabase();
1253 g_kiosk_startup =
false;
1257 if (!g_kiosk_startup) {
1259 SetTopWindow(gFrame);
1266 OCPNPlatform::Initialize_4();
1269 androidHideBusyIcon();
1272 wxString::Format(_(
"OpenCPN Initialized in %ld ms."), init_sw.Time()));
1276 if (!g_kiosk_startup) {
1277 if (!DoNavMessage(vs)) {
1284 g_bHasHwClock =
true;
1285#if defined(__UNIX__) && !defined(__ANDROID__)
1288 ((stat(
"/dev/rtc", &buffer) == 0) || (stat(
"/dev/rtc0", &buffer) == 0) ||
1289 (stat(
"/dev/misc/rtc", &buffer) == 0));
1292 g_config_version_string = vs;
1297 for (
auto *cp : TheConnectionParams()) {
1299 if (cp->GetDSPort().Contains(
"Serial")) {
1300 std::string port(cp->Port.ToStdString());
1307 std::vector<std::string> ipv4_addrs = get_local_ipv4_addresses();
1310 if (ipv4_addrs.size()) {
1311 std::string ipAddr = ipv4_addrs[0];
1314 if (data_dir.Last() != wxFileName::GetPathSeparator())
1315 data_dir.Append(wxFileName::GetPathSeparator());
1317 make_certificate(ipAddr, data_dir.ToStdString());
1319 m_rest_server.
StartServer(fs::path(data_dir.ToStdString()));
1320 StartMDNSService(g_hostname.ToStdString(),
"opencpn-object-control-service",
1324 if (!g_kiosk_startup) {
1328 wxLogMessage(
"InitTimer start");
1329 gFrame->InitTimer.Start(10, wxTIMER_CONTINUOUS);
1339 CallAfter([
this]() {
1341 g_wallpaper->Show(
false);
1343 g_bFullscreen =
true;
1346 g_wallpaper->Destroy();
1347 g_wallpaper =
nullptr;
1349 SetTopWindow(gFrame);
1353 wxString(
"Version ") + VERSION_FULL +
" Build " + VERSION_DATE;
1354 if (!DoNavMessage(vs)) {
1360 wxLogMessage(
"InitTimer start");
1361 gFrame->InitTimer.Start(10, wxTIMER_CONTINUOUS);
1366void MyApp::BuildMainFrame() {
1369 wxSize new_frame_size(-1, -1);
1371 ::wxClientDisplayRect(&cx, &cy, &cw, &ch);
1373 user_colors::Initialize();
1375 if ((g_nframewin_x > 100) && (g_nframewin_y > 100) && (g_nframewin_x <= cw) &&
1376 (g_nframewin_y <= ch))
1377 new_frame_size.Set(g_nframewin_x, g_nframewin_y);
1379 new_frame_size.Set(cw * 7 / 10, ch * 7 / 10);
1385 if ((g_lastClientRectx != cx) || (g_lastClientRecty != cy) ||
1386 (g_lastClientRectw != cw) || (g_lastClientRecth != ch)) {
1387 new_frame_size.Set(cw * 7 / 10, ch * 7 / 10);
1388 g_bframemax =
false;
1391 g_lastClientRectx = cx;
1392 g_lastClientRecty = cy;
1393 g_lastClientRectw = cw;
1394 g_lastClientRecth = ch;
1397 wxPoint position(0, 0);
1398 wxSize dsize = wxGetDisplaySize();
1401 g_nframewin_posy = wxMax(g_nframewin_posy, 22);
1404 if ((g_nframewin_posx < dsize.x) && (g_nframewin_posy < dsize.y))
1405 position = wxPoint(g_nframewin_posx, g_nframewin_posy);
1410 frame_rect.left = position.x;
1411 frame_rect.top = position.y;
1412 frame_rect.right = position.x + new_frame_size.x;
1413 frame_rect.bottom = position.y + new_frame_size.y;
1417 if (NULL == MonitorFromRect(&frame_rect, MONITOR_DEFAULTTONULL))
1418 position = wxPoint(10, 10);
1423 const wxPoint ptScreen(position.x, position.y);
1424 const int displayIndex = wxDisplay::GetFromPoint(ptScreen);
1426 if (displayIndex == wxNOT_FOUND) position = wxPoint(10, 30);
1429 g_nframewin_posx = position.x;
1430 g_nframewin_posy = position.y;
1433 wxSize asz = getAndroidDisplayDimensions();
1438 if ((cw > 200) && (ch > 200))
1439 new_frame_size.Set(cw, ch);
1441 new_frame_size.Set(800, 400);
1447 wxString short_version_name = wxString(PACKAGE_VERSION).BeforeFirst(
'+');
1448 wxString myframe_window_title = wxString(
"OpenCPN " + short_version_name);
1451 myframe_window_title += _(
" -- [Portable(-p) executing from ");
1452 myframe_window_title += g_Platform->GetHomeDir();
1453 myframe_window_title +=
"]";
1457 fmsg.Printf(
"Creating MyFrame...size(%d, %d) position(%d, %d)",
1458 new_frame_size.x, new_frame_size.y, position.x, position.y);
1462 auto dockart =
new wxAuiDefaultDockArt;
1465 gFrame =
new MyFrame(myframe_window_title, position, new_frame_size,
1466 m_rest_server, dockart,
1467 [&](
const std::string &path) {
return OpenFile(path); });
1472 g_pauimgr->SetDockSizeConstraint(.9, .9);
1478 g_Platform->Initialize_3();
1480 gFrame->CreateCanvasLayout();
1482 gFrame->SetGPSCompassScale();
1486 gFrame->SetChartUpdatePeriod();
1490 gFrame->ApplyGlobalSettings(
false);
1491 gFrame->SetAllToolbarScale();
1492 gFrame->SetAndApplyColorScheme(global_color_scheme);
1493 if (g_bframemax) gFrame->Maximize(
true);
1496 if (g_bresponsive && (gFrame->GetPrimaryCanvas()->
GetPixPerMM() > 4.0))
1497 gFrame->Maximize(
true);
1506 if (g_rebuild_gl_cache && g_bopengl && g_GLOptions.m_bTextureCompression &&
1507 g_GLOptions.m_bTextureCompressionCaching) {
1508 gFrame->ReloadAllVP();
1527 if ((gps_watchdog_timeout_ticks > 60) || (gps_watchdog_timeout_ticks <= 0))
1531 dogmsg.Printf(
"GPS Watchdog Timeout is: %d sec.", gps_watchdog_timeout_ticks);
1532 wxLogMessage(dogmsg);
1534 sat_watchdog_timeout_ticks = gps_watchdog_timeout_ticks;
1542 if (g_bTrackCarryOver) g_bDeferredStartTrack =
true;
1547 gFrame->DoChartUpdate();
1549 CommBridge::GetInstance();
1552 for (
auto *cp : TheConnectionParams()) {
1555 cp->b_IsSetup = TRUE;
1561 auto style = g_StyleManager->GetCurrentStyle();
1562 auto bitmap =
new wxBitmap(style->GetIcon(
"default_pi", 32, 32));
1564 PluginLoader::GetInstance()->SetPluginDefaultIcon(bitmap);
1566 wxLogWarning(
"Cannot initiate plugin default jigsaw icon.");
1568 AbstractPlatform::ShowBusySpinner();
1570 AbstractPlatform::HideBusySpinner();
1574 wxString perspective;
1576 pConfig->Read(
"AUIPerspective", &perspective);
1583 bool bno_load =
false;
1585 wxArrayString name_array;
1586 wxStringTokenizer st(perspective,
"|;");
1587 while (st.HasMoreTokens()) {
1588 wxString s1 = st.GetNextToken();
1589 if (s1.StartsWith(
"name=")) {
1590 wxString sc = s1.AfterFirst(
'=');
1595 wxAuiPaneInfoArray pane_array_val =
g_pauimgr->GetAllPanes();
1596 for (
unsigned int i = 0; i < pane_array_val.GetCount(); i++) {
1597 wxAuiPaneInfo pane = pane_array_val.Item(i);
1601 if (name_array.Index(pane.name) == wxNOT_FOUND) {
1607 if (!bno_load)
g_pauimgr->LoadPerspective(perspective,
false);
1612 for (
unsigned int i = 0; i <
g_canvasArray.GetCount(); i++) {
1615 wxSize frameSize = GetClientSize();
1616 wxSize minSize =
g_pauimgr->GetPane(cc).min_size;
1617 int width = wxMax(minSize.x, frameSize.x / 10);
1618 g_pauimgr->GetPane(cc).MinSize(frameSize.x * 1 / 5, frameSize.y);
1640 if (!g_bdisable_opengl) {
1643 if (pgl && (pgl->GetRendererString().Find(
"UniChrome") != wxNOT_FOUND)) {
1644 gFrame->m_defer_size = gFrame->GetSize();
1645 gFrame->SetSize(gFrame->m_defer_size.x - 10, gFrame->m_defer_size.y);
1647 gFrame->m_bdefer_resize =
true;
1657 glDeleteTextures(n, texts);
1666 gFrame->SetSize(getAndroidDisplayDimensions());
1667 androidSetFollowTool(gFrame->GetPrimaryCanvas()->m_bFollow ? 1 : 0, true);
1679void MyApp::LoadChartDatabase() {
1681 ArrayOfCDI ChartDirArray;
1682 pConfig->LoadChartDirArray(ChartDirArray);
1687 if (g_bFirstRun && (ChartDirArray.GetCount() == 0)) {
1690 wxRegKey RegKey(wxString(
"HKEY_LOCAL_MACHINE\\SOFTWARE\\OpenCPN"));
1691 if (RegKey.Exists()) {
1693 _(
"Retrieving initial Chart Directory set from Windows Registry"));
1695 RegKey.QueryValue(wxString(
"ChartDirs"), dirs);
1697 wxStringTokenizer tkz(dirs,
";");
1698 while (tkz.HasMoreTokens()) {
1699 wxString token = tkz.GetNextToken();
1702 cdi.fullpath = token.Trim();
1703 cdi.magic_number =
"";
1705 ChartDirArray.Add(cdi);
1712 cdi.fullpath =
"charts";
1713 cdi.fullpath.Prepend(g_Platform->GetSharedDataDir());
1714 cdi.magic_number =
"";
1715 ChartDirArray.Add(cdi);
1719 if (ndirs)
pConfig->UpdateChartDirs(ChartDirArray);
1729 if (!ChartDirArray.GetCount())
1730 if (::wxFileExists(ChartListFileName)) ::wxRemoveFile(ChartListFileName);
1737 g_restore_dbindex = 0;
1741 if (g_restore_dbindex >= 0) {
1742 if (
ChartData->GetChartTableEntries() == 0)
1743 g_restore_dbindex = -1;
1745 else if (g_restore_dbindex > (
ChartData->GetChartTableEntries() - 1))
1746 g_restore_dbindex = 0;
1754 wxLogMessage(
"opencpn::MyApp starting exit.");
1756 m_usb_watcher.Stop();
1759 wxDateTime lognow = wxDateTime::Now();
1761 wxString day = lognow.FormatISODate();
1762 wxString utc = lognow.FormatISOTime();
1763 wxString navmsg =
"LOGBOOK: ";
1771 data.Printf(
"OFF: Lat %10.5f Lon %10.5f ",
gLat,
gLon);
1775 if (std::isnan(
gCog))
1776 cog.Printf(
"COG ----- ");
1778 cog.Printf(
"COG %10.5f ",
gCog);
1781 if (std::isnan(
gSog))
1782 sog.Printf(
"SOG ----- ");
1784 sog.Printf(
"SOG %6.2f " + getUsrSpeedUnit(), toUsrSpeed(
gSog));
1791 data.Printf(
"OFF: Lat %10.5f Lon %10.5f",
gLat,
gLon);
1794 wxLogMessage(navmsg);
1795 g_loglast_time = lognow;
1813 for (
unsigned int igroup = 0; igroup <
g_pGroupArray->GetCount();
1822 wxLogMessage(
"opencpn::MyApp exiting cleanly...\n");
1823 wxLog::FlushActive();
1825 g_Platform->CloseLogFile();
1827 delete pInit_Chart_Dir;
1835 delete pWayPointMan;
1837 navutil::DeinitGlobals();
1839 user_colors::DeInitialize();
1846 delete g_StyleManager;
1851 void RestoreSystemColors();
1852 RestoreSystemColors();
1859#if wxUSE_XLOCALE || !wxCHECK_VERSION(3, 0, 0)
1863 FontMgr::Shutdown();
1865 g_Platform->OnExit_2();
1872#ifdef LINUX_CRASHRPT
1873void MyApp::OnFatalException() { g_crashprint.Report(); }
1879void MyCPLErrorHandler(CPLErr eErrClass,
int nError,
const char *pszErrorMsg)
1884 if (eErrClass == CE_Debug)
1885 snprintf(msg, 255,
"CPL: %s", pszErrorMsg);
1886 else if (eErrClass == CE_Warning)
1887 snprintf(msg, 255,
"CPL Warning %d: %s", nError, pszErrorMsg);
1889 snprintf(msg, 255,
"CPL ERROR %d: %s", nError, pszErrorMsg);
1891 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.