OpenCPN Partial API docs
Loading...
Searching...
No Matches
ocpn_platform.cpp
Go to the documentation of this file.
1/**************************************************************************
2 * Copyright (C) 2015 by David S. Register *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, see <https://www.gnu.org/licenses/>. *
16 **************************************************************************/
17
24#include <cstdlib>
25#include <string>
26#include <vector>
27
28#ifndef __WXMSW__
29#include <signal.h>
30#include <setjmp.h>
31#endif
32
33#include "gl_headers.h" // Must be included before anything using GL stuff
34
35#ifdef __WXMSW__
36#undef IPV6STRICT // mingw FTBS fix: missing struct ip_mreq
37#include <windows.h>
38#include <winioctl.h>
39#include <initguid.h>
40#include "setupapi.h" // presently stored in opencpn/src
41#endif
42
43#include <wx/wxprec.h>
44
45#ifndef WX_PRECOMP
46#include <wx/wx.h>
47#endif // precompiled headers
48
49#include <wx/app.h>
50#include <wx/apptrait.h>
51#include <wx/stdpaths.h>
52#include <wx/filename.h>
53#include <wx/tokenzr.h>
54#include <wx/textfile.h>
55#include <wx/jsonval.h>
56#include <wx/jsonreader.h>
57
58#include "config.h"
59
60#include "model/ais_decoder.h"
62#include "model/base_platform.h"
63#include "model/cmdline.h"
64#include "model/config_vars.h"
65#include "model/conn_params.h"
66#include "model/cutil.h"
67#include "model/gui_vars.h"
68#include "model/idents.h"
69#include "model/logger.h"
70#include "model/ocpn_utils.h"
71#include "model/plugin_cache.h"
72#include "model/plugin_paths.h"
73#include "model/select.h"
74
75#include "about_frame_impl.h"
76#include "about.h"
77#include "displays.h"
78#include "font_mgr.h"
79#include "gui_lib.h"
80#include "navutil.h"
81#include "ocpn_platform.h"
82#include "options.h"
83#include "s52s57.h"
84#include "snd_config.h"
85#include "std_filesystem.h"
86#include "styles.h"
87#include "top_frame.h"
88
89#ifdef __ANDROID__
90#include "androidUTIL.h"
91#endif
92
93#ifdef ocpnUSE_GL
94#include "gl_chart_canvas.h"
95#endif
96
97// Include CrashRpt Header
98#ifdef OCPN_USE_CRASHREPORT
99#include "CrashRpt.h"
100#endif
101#ifdef __MSVC__
102#include <new.h>
103#endif
104
105#ifdef LINUX_CRASHRPT
106#include "crashprint.h"
107#endif
108
109#ifdef __WXOSX__
110#include "model/macutils.h"
111#endif
112
114
116
117class MyApp;
118DECLARE_APP(MyApp)
119
120void appendOSDirSlash(wxString *pString);
121
122#ifndef __WXMSW__
123struct sigaction sa_all;
124struct sigaction sa_all_old;
125sigjmp_buf env; // the context saved by sigsetjmp();
126#endif
127
128#ifdef ocpnUSE_GL
129extern ocpnGLOptions g_GLOptions;
130#endif
131#if wxUSE_XLOCALE || !wxCHECK_VERSION(3, 0, 0)
132extern wxLocale *plocale_def_lang;
133#endif
134
135#ifdef __ANDROID__
136extern PlatSpec android_plat_spc;
137#endif
138
139#ifdef ocpnUSE_GL
140OCPN_GLCaps *GL_Caps = nullptr;
141#endif
142
143static const char *const DEFAULT_XDG_DATA_DIRS =
144 "~/.local/share:/usr/local/share:/usr/share";
145
146#ifdef __WXMSW__
147static const char PATH_SEP = ';';
148#else
149static const char PATH_SEP = ':';
150#endif
151
152#ifdef __ANDROID__
153constexpr int kStatFieldCount = 2;
154#else
155constexpr int kStatFieldCount = 5;
156#endif
157
158static bool checkIfFlatpacked() {
159 wxString id;
160 if (!wxGetEnv("FLATPAK_ID", &id)) {
161 return false;
162 }
163 return id == "org.opencpn.OpenCPN";
164}
165
166OCPNPlatform::OCPNPlatform() {
167 m_pt_per_pixel = 0; // cached value
168 m_bdisableWindowsDisplayEnum = false;
169 m_monitorWidth = m_monitorHeight = 0;
171 for (size_t i = 0; i < g_monitor_info.size(); i++) {
172 m_displaySizeMMOverride.push_back(0);
173 }
174 m_pluginDataPath = "";
175}
176
177OCPNPlatform::~OCPNPlatform() {
178#ifdef ocpnUSE_GL
179 if (GL_Caps) {
180 delete GL_Caps;
181 }
182#endif
183}
184
185//--------------------------------------------------------------------------
186// Per-Platform Initialization support
187//--------------------------------------------------------------------------
188#ifdef __WXMSW__
189int MyNewHandler(size_t size) {
190 // Pass to wxWidgets Main Loop handler
191 throw std::bad_alloc();
192
193 return 0;
194}
195#endif
196
197//-----------------------------------------------------------------------
198// Signal Handlers
199//-----------------------------------------------------------------------
200#ifndef __WXMSW__
201
202// These are the signals possibly expected
203// SIGUSR1
204// Raised externally to cause orderly termination of application
205// Intended to act just like pushing the "EXIT" button
206
207// SIGSEGV
208// Some undefined segfault......
209
210int s_inhup;
211
212void catch_signals(int signo) {
213 switch (signo) {
214 case SIGUSR1:
215 quitflag++; // signal to the timer loop
216 break;
217
218 case SIGSEGV:
219 siglongjmp(env, 1); // jump back to the setjmp() point
220 break;
221
222 case SIGHUP:
223 if (!s_inhup) {
224 s_inhup++; // incase SIGHUP is closely followed by SIGTERM
225 top_frame::Get()->FastClose();
226 }
227 break;
228
229 case SIGTERM:
230 if (!s_inhup) {
231 s_inhup++; // incase SIGHUP is closely followed by SIGTERM
232 top_frame::Get()->FastClose();
233 }
234
235 break;
236
237 default:
238 break;
239 }
240}
241#endif
242
243#ifdef OCPN_USE_CRASHREPORT
244// Define the crash callback
245int CALLBACK CrashCallback(CR_CRASH_CALLBACK_INFO *pInfo) {
246 wxLog::GetActiveTarget()->Flush(); // Flush log file
247 return CR_CB_DODEFAULT;
248}
249#endif
250
251// Called from MyApp() immediately upon entry to MyApp::OnInit()
252void OCPNPlatform::Initialize_1() {
253#ifdef OCPN_USE_CRASHREPORT
254#ifndef _DEBUG
255 // Install Windows crash reporting
256
257 CR_INSTALL_INFO info;
258 memset(&info, 0, sizeof(CR_INSTALL_INFO));
259 info.cb = sizeof(CR_INSTALL_INFO);
260 info.pszAppName = L"OpenCPN";
261
262 info.pszAppVersion = wxString(VERSION_FULL).c_str();
263
264 int type = MiniDumpNormal;
265
266 // This results in the inclusion of global variables
267 type |= MiniDumpWithDataSegs;
268
269 // If this flag is specified, the contents of every readable and writeable
270 // private memory page will be included into the minidump. type |=
271 // MiniDumpWithPrivateReadWriteMemory;
272
273 // If this flag is specified, MiniDumpWriteDump function will scan the stack
274 // memory of every thread looking for pointers that point to other readable
275 // memory pages in the process' address space. type |=
276 // MiniDumpWithIndirectlyReferencedMemory;
277
278 info.uMiniDumpType = (MINIDUMP_TYPE)type;
279
280 // Install all available exception handlers....
281 info.dwFlags = CR_INST_ALL_POSSIBLE_HANDLERS;
282
283 // Except memory allocation failures
284 info.dwFlags &= ~CR_INST_NEW_OPERATOR_ERROR_HANDLER;
285
286 // Allow user to attach files
287 info.dwFlags |= CR_INST_ALLOW_ATTACH_MORE_FILES;
288
289 // Allow user to add more info
290 info.dwFlags |= CR_INST_SHOW_ADDITIONAL_INFO_FIELDS;
291
292 // URL for sending error reports over HTTP.
293
294 if (g_bEmailCrashReport) {
295 info.pszUrl = L"https://bigdumboat.com/crashrpt/ocpn_crashrpt.php";
296 info.uPriorities[CR_HTTP] = 3; // First try send report over HTTP
297 } else {
298 info.dwFlags |= CR_INST_DONT_SEND_REPORT;
299 info.uPriorities[CR_HTTP] = CR_NEGATIVE_PRIORITY; // don't send at all
300 }
301
302 info.uPriorities[CR_SMTP] =
303 CR_NEGATIVE_PRIORITY; // Second try send report over SMTP
304 info.uPriorities[CR_SMAPI] =
305 CR_NEGATIVE_PRIORITY; // Third try send report over Simple MAPI
306
307 wxStandardPaths &crash_std_path = g_Platform->GetStdPaths();
308
309 wxString crash_rpt_save_locn = crash_std_path.GetConfigDir();
310 if (g_bportable) {
311 wxFileName exec_path_crash(crash_std_path.GetExecutablePath());
312 crash_rpt_save_locn =
313 exec_path_crash.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
314 }
315
316 wxString locn = crash_rpt_save_locn + "\\CrashReports";
317
318 if (!wxDirExists(locn)) wxMkdir(locn);
319
320 if (wxDirExists(locn)) {
321 wxCharBuffer buf = locn.ToUTF8();
322 wchar_t wlocn[256];
323 if (buf && (locn.Length() < sizeof(wlocn))) {
324 MultiByteToWideChar(0, 0, buf.data(), -1, wlocn, sizeof(wlocn) - 1);
325 info.pszErrorReportSaveDir = (LPCWSTR)wlocn;
326 }
327 }
328
329 // Provide privacy policy URL
330 wxFileName exec_path_crash(crash_std_path.GetExecutablePath());
331 wxString policy_file =
332 exec_path_crash.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
333 policy_file += "PrivacyPolicy.txt";
334 policy_file.Prepend("file:");
335
336 info.pszPrivacyPolicyURL = policy_file.c_str();
337 ;
338
339 int nResult = crInstall(&info);
340 if (nResult != 0) {
341 TCHAR buff[256];
342 crGetLastErrorMsg(buff, 256);
343 // MessageBox(NULL, buff, "crInstall error, Crash Reporting disabled.",
344 // MB_OK);
345 }
346
347 if (nResult == 0) { // Complete the installation
348 // Establish the crash callback function
349 crSetCrashCallback(CrashCallback, NULL);
350
351 // Take screenshot of the app window at the moment of crash
352 crAddScreenshot2(CR_AS_PROCESS_WINDOWS | CR_AS_USE_JPEG_FORMAT, 95);
353
354 // Mark some files to add to the crash report
355 wxString home_data_crash = crash_std_path.GetConfigDir();
356 if (g_bportable) {
357 wxFileName f(crash_std_path.GetExecutablePath());
358 home_data_crash = f.GetPath();
359 }
360 appendOSDirSlash(&home_data_crash);
361
362 wxString config_crash = "opencpn.ini";
363 config_crash.Prepend(home_data_crash);
364 crAddFile2(config_crash.c_str(), NULL, NULL,
365 CR_AF_MISSING_FILE_OK | CR_AF_ALLOW_DELETE);
366
367 wxString log_crash = "opencpn.log";
368 log_crash.Prepend(home_data_crash);
369 crAddFile2(log_crash.c_str(), NULL, NULL,
370 CR_AF_MISSING_FILE_OK | CR_AF_ALLOW_DELETE);
371 }
372#endif
373#endif
374
375#ifdef LINUX_CRASHRPT
376#if wxUSE_ON_FATAL_EXCEPTION
377 // fatal exceptions handling
378 wxHandleFatalExceptions(true);
379#endif
380#endif
381
382#ifdef __MSVC__
383 // Invoke my own handler for failures of malloc/new
384 _set_new_handler(MyNewHandler);
385 // configure malloc to call the New failure handler on failure
386 _set_new_mode(1);
387#endif
388
389#if 0
390#ifdef __WXMSW__
391 // On MSW, force the entire process to run on one CPU core only
392 // This resolves some difficulty with wxThread syncronization
393 //Gets the current process handle
394 HANDLE hProc = GetCurrentProcess();
395 DWORD procMask;
396 DWORD sysMask;
397 HANDLE hDup;
398 DuplicateHandle( hProc, hProc, hProc, &hDup, 0, FALSE, DUPLICATE_SAME_ACCESS );
399
400 //Gets the current process affinity mask
401 GetProcessAffinityMask( hDup, &procMask, &sysMask );
402
403 // Take a simple approach, and assume up to 4 processors
404 DWORD newMask;
405 if( ( procMask & 1 ) == 1 ) newMask = 1;
406 else
407 if( ( procMask & 2 ) == 2 ) newMask = 2;
408 else
409 if( ( procMask & 4 ) == 4 ) newMask = 4;
410 else
411 if( ( procMask & 8 ) == 8 ) newMask = 8;
412
413 //Set te affinity mask for the process
414 BOOL res = SetProcessAffinityMask( hDup, (DWORD_PTR) newMask );
415
416 if( res == 0 ) {
417 //Error setting affinity mask!!
418 }
419#endif
420#endif
421
422#ifdef __MSVC__
423
424 // Handle any Floating Point Exceptions which may leak thru from other
425 // processes. The exception filter is in cutil.c
426 // Seems to only happen for W98
427
428 wxPlatformInfo Platform;
429 if (Platform.GetOperatingSystemId() == wxOS_WINDOWS_9X)
430 SetUnhandledExceptionFilter(&MyUnhandledExceptionFilter);
431#endif
432
433#ifdef __WXMSW__
434 // _CrtSetBreakAlloc(25503);
435#endif
436
437#ifndef __WXMSW__
438 // Setup Linux SIGNAL handling, for external program control
439
440 // Build the sigaction structure
441 sa_all.sa_handler = catch_signals; // point to my handler
442 sigemptyset(&sa_all.sa_mask); // make the blocking set
443 // empty, so that all
444 // other signals will be
445 // unblocked during my handler
446 sa_all.sa_flags = 0;
447
448 sigaction(SIGUSR1, NULL,
449 &sa_all_old); // save existing action for this signal
450
451 // Register my request for some signals
452 sigaction(SIGUSR1, &sa_all, NULL);
453 sigaction(SIGUSR1, NULL,
454 &sa_all_old); // inspect existing action for this signal
455
456 sigaction(SIGTERM, &sa_all, NULL);
457 sigaction(SIGTERM, NULL, &sa_all_old);
458
459 sigaction(SIGHUP, &sa_all, NULL);
460 sigaction(SIGHUP, NULL, &sa_all_old);
461
462#endif
463
464#ifdef __ANDROID__
465 qDebug() << "Initialize_1()";
466 // #ifdef NOASSERT
467 wxDisableAsserts(); // No asserts at all in Release mode
468// #endif
469#endif
470}
471
472// Called from MyApp() immediately before creation of MyFrame()
473// Config is known to be loaded and stable
474// Log is available
475void OCPNPlatform::Initialize_2() {
476#ifdef __ANDROID__
477 wxLogMessage(androidGetDeviceInfo());
478
479 // Create some directories in App private directory
480 // Mainly required for Android 11+, but useable on all versions.
481 wxChar sep = wxFileName::GetPathSeparator();
482
483 wxString ChartDir = GetPrivateDataDir();
484 if (ChartDir.Last() != sep) ChartDir.Append(sep);
485 ChartDir.Append("Charts");
486 if (!::wxDirExists(ChartDir)) {
487 ::wxMkdir(ChartDir);
488 }
489
490 wxString GRIBDir = GetPrivateDataDir();
491 if (GRIBDir.Last() != sep) GRIBDir.Append(sep);
492 GRIBDir.Append("GRIBS");
493 if (!::wxDirExists(GRIBDir)) {
494 ::wxMkdir(GRIBDir);
495 }
496
497 wxString VDRDir = GetPrivateDataDir();
498 if (VDRDir.Last() != sep) VDRDir.Append(sep);
499 VDRDir.Append("VDR");
500 if (!::wxDirExists(VDRDir)) {
501 ::wxMkdir(VDRDir);
502 }
503
504 // Set the default Import/Export directory for A11+
505 if (g_Android_SDK_Version >= 30) {
506 if (!g_gpx_path.StartsWith(androidGetDocumentsDirectory())) {
507 g_gpx_path = androidGetDocumentsDirectory();
508 }
509 }
510
511 if (!g_TCData_Dir.Length()) g_TCData_Dir = GetPrivateDataDir();
512
513#endif
514
515 // Set a global toolbar scale factor
516 g_toolbar_scalefactor = GetToolbarScaleFactor(g_GUIScaleFactor);
517 auto configdir = wxFileName(GetPrivateDataDir());
518 if (!configdir.DirExists()) {
519 if (!configdir.Mkdir()) {
520 auto msg = std::string("Cannot create config directory: ");
521 wxLogWarning(msg + configdir.GetFullPath());
522 }
523 }
524}
525
526// Called from MyApp()::OnInit() just after gFrame is created, so gFrame is
527// available
528void OCPNPlatform::Initialize_3() {
529 bool bcapable = IsGLCapable();
530
531#ifdef ocpnARM // Boot arm* platforms (meaning rPI) without OpenGL on first run
532 // bcapable = false;
533#endif
534
535 bool bAndroid = false;
536#ifdef __ANDROID__
537 bAndroid = true;
538#endif
539
540 if (!bcapable)
541 g_bopengl = false;
542 else {
543 // g_bopengl = true;
544 g_bdisable_opengl = false;
545 pConfig->UpdateSettings();
546 }
547
548 // Try to automatically switch to guaranteed usable GL mode on an OCPN upgrade
549 // or fresh install
550
551#ifdef ocpnUSE_GL
552 if ((g_bFirstRun || g_bUpgradeInProcess || bAndroid) && bcapable) {
553 g_bopengl = true;
554
555 // Set up visually nice options
556 g_GLOptions.m_bUseAcceleratedPanning = true;
557 g_GLOptions.m_bTextureCompression = true;
558 g_GLOptions.m_bTextureCompressionCaching = true;
559
560 g_GLOptions.m_iTextureDimension = 512;
561 g_GLOptions.m_iTextureMemorySize = 64;
562
563 g_GLOptions.m_GLPolygonSmoothing = true;
564 g_GLOptions.m_GLLineSmoothing = true;
565 }
566#endif
567
568 // Force a few items for Android, to ensure that UI is useable if config got
569 // scrambled
570 if (bAndroid) {
571 g_btouch = true;
572 }
573
574 if (g_bFirstRun || g_bUpgradeInProcess) {
575 if (!g_bRollover) // Not explicit set before
576 g_bRollover = g_btouch ? false : true;
577 }
578
579 g_FlushNavobjChangesTimeout = 300; // Seconds, so 5 minutes
580}
581
582// Called from MyApp() just before end of MyApp::OnInit()
583void OCPNPlatform::Initialize_4() {
584#ifdef __ANDROID__
585 if (pSelect) pSelect->SetSelectPixelRadius(wxMax(25, 6.0 * getAndroidDPmm()));
586 if (pSelectTC)
587 pSelectTC->SetSelectPixelRadius(wxMax(25, 6.0 * getAndroidDPmm()));
588 if (pSelectAIS)
589 pSelectAIS->SetSelectPixelRadius(wxMax(25, 6.0 * getAndroidDPmm()));
590#endif
591
592#ifdef __WXMAC__
593 // A bit of a hack for Mojave MacOS 10.14.
594 // Force the user to actively select "Display" tab to ensure initial rendering
595 // of canvas layout select button.
596 options_lastPage = 1;
597#endif
598}
599
600void OCPNPlatform::OnExit_1() {}
601
602void OCPNPlatform::OnExit_2() {
603#ifdef OCPN_USE_CRASHREPORT
604#ifndef _DEBUG
605 // Uninstall Windows crash reporting
606// crUninstall();
607#endif
608#endif
609}
610
611#ifdef ocpnUSE_GL
612
613bool HasGLExt(wxJSONValue &glinfo, const std::string ext) {
614 if (!glinfo.HasMember("GL_EXTENSIONS")) {
615 return false;
616 }
617 for (int i = 0; i < glinfo["GL_EXTENSIONS"].Size(); i++) {
618 if (glinfo["GL_EXTENSIONS"][i].AsString() == ext) {
619 return true;
620 }
621 }
622 return false;
623}
624
625bool OCPNPlatform::BuildGLCaps(void *pbuf) {
626#ifndef __ANDROID__
627 fs::path ep(GetExePath().ToStdString());
628#ifndef __WXMSW__
629 std::string gl_util_exe = "opencpn-glutil";
630#else
631 std::string gl_util_exe = "opencpn-glutil.exe";
632#endif
633 fs::path gl_util_path = ep.parent_path().append(gl_util_exe);
634
635 if (!fs::exists(gl_util_path)) {
636 gl_util_path =
637 ep.parent_path().parent_path().append("libexec").append(gl_util_exe);
638 }
639 if (!fs::exists(gl_util_path)) {
640 // TODO: What to do if the utility is not found (Which it is not for
641 // developer builds that are not installed)?
642 wxLogMessage("OpenGL test utility not found at %s.", gl_util_path.c_str());
643 return false;
644 }
645
646 std::string gl_json = fs::path(GetPrivateDataDir().ToStdString())
647 .append("gl_caps.json")
648 .string();
649
650 wxString cmd = wxString::Format("\"%s\" opengl-info \"%s\"",
651 gl_util_path.c_str(), gl_json.c_str());
652
653 wxLogMessage("Starting OpenGL test utility: %s", cmd);
654
655 wxArrayString output;
656 if (long res = wxExecute(cmd, output); res != 0) {
657 wxLogMessage("OpenGL test utility failed with exit code %d", res);
658 for (const auto &l : output) {
659 wxLogMessage(l);
660 }
661 return false;
662 }
663
664 wxFileInputStream fis(gl_json);
665 wxJSONReader reader;
666 wxJSONValue root;
667 reader.Parse(fis, &root);
668 if (reader.GetErrorCount() > 0) {
669 wxLogMessage("Failed to parse JSON output from OpenGL test utility.");
670 for (const auto &l : reader.GetErrors()) {
671 wxLogMessage(l);
672 }
673 return false;
674 }
675
676 OCPN_GLCaps *pcaps = (OCPN_GLCaps *)pbuf;
677
678 if (root.HasMember("GL_RENDERER")) {
679 pcaps->Renderer = root["GL_RENDERER"].AsString();
680 } else {
681 wxLogMessage("GL_RENDERER not found.");
682 return false;
683 }
684 if (root.HasMember("GL_VERSION")) {
685 pcaps->Version = root["GL_VERSION"].AsString();
686 } else {
687 wxLogMessage("GL_VERSION not found.");
688 return false;
689 }
690 if (root.HasMember("GL_SHADING_LANGUAGE_VERSION")) {
691 pcaps->GLSL_Version = root["GL_SHADING_LANGUAGE_VERSION"].AsString();
692 } else {
693 wxLogMessage("GL_SHADING_LANGUAGE_VERSION not found.");
694 return false;
695 }
696 if (root.HasMember("GL_USABLE")) {
697 if (!root["GL_USABLE"].AsBool()) {
698 wxLogMessage("OpenGL test utility reports that OpenGL is not usable.");
699 return false;
700 }
701 } else {
702 wxLogMessage("GL_USABLE not found.");
703 return false;
704 }
705 pcaps->dGLSL_Version = 0;
706 pcaps->dGLSL_Version = ::atof(pcaps->GLSL_Version.c_str());
707 if (pcaps->dGLSL_Version < 1.2) {
708 wxString msg;
709 msg.Printf("GLCaps Probe: OpenGL-> GLSL Version reported: ");
710 msg += wxString(pcaps->GLSL_Version.c_str());
711 msg += "\n OpenGL disabled due to insufficient OpenGL capabilities";
712 wxLogMessage(msg);
713 pcaps->bCanDoGLSL = false;
714 return false;
715 }
716 pcaps->bCanDoGLSL = true;
717 if (HasGLExt(root, "GL_ARB_texture_non_power_of_two")) {
718 pcaps->TextureRectangleFormat = GL_TEXTURE_2D;
719 } else if (HasGLExt(root, "GL_OES_texture_npot")) {
720 pcaps->TextureRectangleFormat = GL_TEXTURE_2D;
721 } else if (HasGLExt(root, "GL_ARB_texture_rectangle")) {
722 pcaps->TextureRectangleFormat = GL_TEXTURE_RECTANGLE_ARB;
723 }
724
725 pcaps->bOldIntel = false;
726
727 pcaps->bCanDoFBO = HasGLExt(root, "GL_EXT_framebuffer_object");
728 if (!pcaps->TextureRectangleFormat) {
729 pcaps->bCanDoFBO = false;
730 }
731
732 pcaps->bCanDoVBO = HasGLExt(
733 root, "GL_ARB_vertex_buffer_object"); // TODO: Or the old way where we
734 // enable it without querying the
735 // extension is right?
736 top_frame::Get()->Show();
737 return true;
738#else
739 // The original codepath doing direct probing in the main OpenCPN process, now
740 // only for Android Investigate OpenGL capabilities
741 top_frame::Get()->Show();
742 glTestCanvas *tcanvas = new glTestCanvas(wxTheApp->GetTopWindow());
743 tcanvas->Show();
744 wxYield();
745 wxGLContext *pctx = new wxGLContext(tcanvas);
746 tcanvas->SetCurrent(*pctx);
747
748 OCPN_GLCaps *pcaps = (OCPN_GLCaps *)pbuf;
749
750 char *str = (char *)glGetString(GL_RENDERER);
751 if (str == NULL) { // No GL at all...
752 wxLogMessage("GL_RENDERER not found.");
753 delete tcanvas;
754 delete pctx;
755 return false;
756 }
757 pcaps->Renderer = std::string(str);
758
759 char *stv = (char *)glGetString(GL_VERSION);
760 if (stv == NULL) { // No GL Version...
761 wxLogMessage("GL_VERSION not found");
762 delete tcanvas;
763 delete pctx;
764 return false;
765 }
766 pcaps->Version = std::string(stv);
767
768 char *stsv = (char *)glGetString(GL_SHADING_LANGUAGE_VERSION);
769 if (stsv == NULL) { // No GLSL...
770 wxLogMessage("GL_SHADING_LANGUAGE_VERSION not found");
771 delete tcanvas;
772 delete pctx;
773 return false;
774 }
775 pcaps->GLSL_Version = std::string(stsv);
776
777 pcaps->dGLSL_Version = 0;
778 pcaps->dGLSL_Version = ::atof(pcaps->GLSL_Version.c_str());
779
780 if (pcaps->dGLSL_Version < 1.2) {
781 wxString msg;
782 msg.Printf("GLCaps Probe: OpenGL-> GLSL Version reported: ");
783 msg += wxString(pcaps->GLSL_Version.c_str());
784 msg += "\n OpenGL disabled due to insufficient OpenGL capabilities";
785 wxLogMessage(msg);
786 pcaps->bCanDoGLSL = false;
787 delete tcanvas;
788 delete pctx;
789 return false;
790 }
791
792 pcaps->bCanDoGLSL = true;
793
794 if (QueryExtension("GL_ARB_texture_non_power_of_two"))
795 pcaps->TextureRectangleFormat = GL_TEXTURE_2D;
796 else if (QueryExtension("GL_OES_texture_npot"))
797 pcaps->TextureRectangleFormat = GL_TEXTURE_2D;
798 else if (QueryExtension("GL_ARB_texture_rectangle"))
799 pcaps->TextureRectangleFormat = GL_TEXTURE_RECTANGLE_ARB;
800
801 pcaps->bOldIntel = false;
802
803 // Can we use VBO?
804 pcaps->bCanDoVBO = true;
805
806#if defined(__WXMSW__) || defined(__WXOSX__)
807 if (pcaps->bOldIntel) pcaps->bCanDoVBO = false;
808#endif
809
810#ifdef __ANDROID__
811 pcaps->bCanDoVBO = false;
812#endif
813
814 // Can we use FBO?
815 pcaps->bCanDoFBO = true;
816
817#ifndef __ANDROID__
818 // We need NPOT to support FBO rendering
819 if (!pcaps->TextureRectangleFormat) pcaps->bCanDoFBO = false;
820
821 // We require certain extensions to support FBO rendering
822 if (!QueryExtension("GL_EXT_framebuffer_object")) pcaps->bCanDoFBO = false;
823#endif
824
825 delete tcanvas;
826 delete pctx;
827 return true;
828#endif
829}
830#endif
831
832bool OCPNPlatform::IsGLCapable() {
833#ifdef ocpnUSE_GL
834
835#ifdef __ANDROID__
836 return true;
837#elif defined(CLI)
838 return false;
839#else
840
841 if (g_bdisable_opengl) return false;
842
843 wxLogMessage("Starting OpenGL test...");
844 wxLog::FlushActive();
845
846 if (!GL_Caps) {
847 GL_Caps = new OCPN_GLCaps();
848 bool bcaps = BuildGLCaps(GL_Caps);
849
850 wxLogMessage("OpenGL test complete.");
851 if (!bcaps) {
852 wxLogMessage("BuildGLCaps fails.");
853 wxLog::FlushActive();
854 return false;
855 }
856 }
857
858 // and so we decide....
859
860 // Require a modern GLSL implementation
861 if (!GL_Caps->bCanDoGLSL) {
862 return false;
863 }
864
865 // We insist on FBO support, since otherwise DC mode is always faster on
866 // canvas panning..
867 if (!GL_Caps->bCanDoFBO) {
868 return false;
869 }
870
871 // OpenGL is OK for OCPN
872 wxLogMessage("OpenGL determined CAPABLE.");
873 wxLog::FlushActive();
874
875 g_bdisable_opengl = false;
876 // g_bopengl = true;
877
878 // Update and flush the config file
879 pConfig->UpdateSettings();
880
881 return true;
882#endif
883#else
884 return false;
885#endif
886}
887
888void OCPNPlatform::SetLocaleSearchPrefixes() {
889#if wxUSE_XLOCALE
890// Add a new prefixes for search order.
891#if defined(__WINDOWS__)
892
893 // Legacy and system plugin location
894 wxString locale_location = GetSharedDataDir();
895 locale_location += "share\\locale";
896 wxLocale::AddCatalogLookupPathPrefix(locale_location);
897 wxString imsg = "Adding catalog lookup path: ";
898 imsg += locale_location;
899 wxLogMessage(imsg);
900
901 // Managed plugin location
902 wxFileName usrShare(GetWinPluginBaseDir() + wxFileName::GetPathSeparator());
903 usrShare.RemoveLastDir();
904 locale_location = usrShare.GetFullPath() + ("share\\locale");
905 wxLocale::AddCatalogLookupPathPrefix(locale_location);
906 imsg = "Adding catalog lookup path: ";
907 imsg += locale_location;
908 wxLogMessage(imsg);
909
910#elif defined(__ANDROID__)
911
912 wxString locale_location = GetSharedDataDir() + "locale";
913 wxLocale::AddCatalogLookupPathPrefix(locale_location);
914
915#elif defined(__UNIX__) && !defined(__WINE__)
916
917 // On Unix, wxWidgets defaults to installation prefix of its own, usually
918 // "/usr". On the other hand, canonical installation prefix for OpenCPN is
919 // "/usr/local".
920 wxString locale_location;
921 if (!wxGetEnv("OPENCPN_PREFIX", &locale_location)) {
922 locale_location = "/usr/local";
923 }
924
925 if (g_bportable) {
926 locale_location = g_Platform->GetHomeDir();
927 }
928
929 wxFileName location;
930 location.AssignDir(locale_location);
931 location.AppendDir("share");
932 location.SetName("locale");
933 locale_location = location.GetFullPath();
934 wxLocale::AddCatalogLookupPathPrefix(locale_location);
935
936 // And then for managed plugins
937 std::string dir = PluginPaths::GetInstance()->UserDatadir();
938 wxString managed_locale_location(dir + "/locale");
939 wxLocale::AddCatalogLookupPathPrefix(managed_locale_location);
940#endif
941
942#ifdef __WXOSX__
943 std::string macDir =
945 "/Library/Application Support/OpenCPN/Contents/Resources";
946 wxString Mac_managed_locale_location(macDir);
947 wxLocale::AddCatalogLookupPathPrefix(Mac_managed_locale_location);
948#endif
949
950#endif
951}
952
953wxString OCPNPlatform::GetDefaultSystemLocale() {
954 wxLogMessage("Getting DefaultSystemLocale...");
955
956 wxString retval = "en_US";
957
958#if wxUSE_XLOCALE
959
960 const wxLanguageInfo *languageInfo =
961 wxLocale::GetLanguageInfo(wxLANGUAGE_DEFAULT);
962 if (languageInfo) retval = languageInfo->CanonicalName;
963
964#if defined(__WXMSW__)
965 LANGID lang_id = GetUserDefaultUILanguage();
966
967 wchar_t lngcp[101];
968 const wxLanguageInfo *languageInfoW = 0;
969 if (0 != GetLocaleInfo(MAKELCID(lang_id, SORT_DEFAULT), LOCALE_SENGLANGUAGE,
970 lngcp, 100)) {
971 wxString lstring = wxString(lngcp);
972
973 languageInfoW = wxLocale::FindLanguageInfo(lngcp);
974 if (languageInfoW)
975 wxLogMessage("Found LanguageInfo for: " + lstring);
976 else
977 wxLogMessage("Could not find LanguageInfo for: " + lstring);
978 } else {
979 wxLogMessage("Could not get LocaleInfo, using wxLANGUAGE_DEFAULT");
980 languageInfoW = wxLocale::GetLanguageInfo(wxLANGUAGE_DEFAULT);
981 }
982
983 if (languageInfoW) retval = languageInfoW->CanonicalName;
984#endif
985
986#if defined(__ANDROID__)
987 retval = androidGetAndroidSystemLocale();
988#endif
989
990#endif
991
992 return retval;
993}
994
995#if wxUSE_XLOCALE || !wxCHECK_VERSION(3, 0, 0)
996wxString OCPNPlatform::GetAdjustedAppLocale() {
997 wxString adjLocale = g_locale;
998
999// For windows, installer may have left information in the registry defining
1000// the user's selected install language. If so, override the config file value
1001// and use this selection for opencpn...
1002#if defined(__WXMSW__)
1003 if (g_bFirstRun || wxIsEmpty(adjLocale)) {
1004 wxRegKey RegKey(wxString("HKEY_LOCAL_MACHINE\\SOFTWARE\\OpenCPN"));
1005 if (RegKey.Exists()) {
1006 wxLogMessage(
1007 "Retrieving initial language selection from Windows Registry");
1008 RegKey.QueryValue(wxString("InstallerLanguage"), adjLocale);
1009 }
1010 }
1011 if (wxIsEmpty(adjLocale)) {
1012 if (g_localeOverride.Length())
1013 adjLocale = g_localeOverride;
1014 else
1015 adjLocale = GetDefaultSystemLocale();
1016 }
1017#endif
1018#if defined(__ANDROID__)
1019 if (g_localeOverride.Length())
1020 adjLocale = g_localeOverride;
1021 else
1022 adjLocale = GetDefaultSystemLocale();
1023#endif
1024
1025 return adjLocale;
1026}
1027
1028wxString OCPNPlatform::ChangeLocale(wxString &newLocaleID,
1029 wxLocale *presentLocale,
1030 wxLocale **newLocale) {
1031 wxString return_val;
1032
1033 wxString imsg = "ChangeLocale: Language load for: ";
1034 imsg += newLocaleID;
1035 wxLogMessage(imsg);
1036
1037 // Old locale is done.
1038 delete (wxLocale *)presentLocale;
1039
1040 wxLocale *locale = new wxLocale;
1041 if (isFlatpacked()) {
1042 std::string path(getenv("HOME"));
1043 path += "/.var/app/org.opencpn.OpenCPN/data/locale";
1044 locale->AddCatalogLookupPathPrefix(path);
1045 wxLogMessage("Using flatpak locales at %s", path.c_str());
1046 }
1047 wxString loc_lang_canonical;
1048
1049 const wxLanguageInfo *pli = wxLocale::FindLanguageInfo(newLocaleID);
1050 bool b_initok = false;
1051
1052 if (pli) {
1053 locale->Init(pli->Language, 1);
1054 // If the locale was not initialized OK, it may be that the wxstd.mo
1055 // translations of the wxWidgets strings is not present. So try again,
1056 // without attempting to load defaults wxstd.mo.
1057 if (!locale->IsOk()) {
1058 wxString imsg = "ChangeLocale: could not initialize: ";
1059 imsg += newLocaleID;
1060 wxLogMessage(imsg);
1061
1062 delete locale;
1063 locale = new wxLocale;
1064 locale->Init(pli->Language, 0);
1065 }
1066 loc_lang_canonical = pli->CanonicalName;
1067
1068 b_initok = locale->IsOk();
1069#ifdef __ANDROID__
1070 b_initok = true;
1071#endif
1072 }
1073
1074 if (!b_initok) {
1075 wxString imsg = "ChangeLocale: Fall back to en_US";
1076 wxLogMessage(imsg);
1077
1078 delete locale;
1079 locale = new wxLocale;
1080 locale->Init(wxLANGUAGE_ENGLISH_US, 0);
1081 loc_lang_canonical =
1082 wxLocale::GetLanguageInfo(wxLANGUAGE_ENGLISH_US)->CanonicalName;
1083 }
1084
1085 if (b_initok) {
1086 wxString imsg = "ChangeLocale: Locale Init OK for: ";
1087 imsg += loc_lang_canonical;
1088 wxLogMessage(imsg);
1089
1090 // wxWidgets assigneds precedence to message catalogs in reverse order of
1091 // loading. That is, the last catalog containing a certain translatable
1092 // item takes precedence.
1093
1094 // So, Load the catalogs saved in a global string array which is populated
1095 // as PlugIns request a catalog load. We want to load the PlugIn catalogs
1096 // first, so that core opencpn translations loaded later will become
1097 // precedent.
1098
1099 for (unsigned int i = 0; i < g_locale_catalog_array.GetCount(); i++) {
1100 if (!locale->AddCatalog(g_locale_catalog_array[i])) {
1101 wxString emsg = "ERROR Loading translation catalog for: ";
1102 emsg += g_locale_catalog_array[i];
1103 wxLogMessage(emsg);
1104 } else {
1105 wxString imsg = "Loaded translation catalog for: ";
1106 imsg += g_locale_catalog_array[i];
1107 wxLogMessage(imsg);
1108 }
1109 }
1110
1111 // Get core opencpn catalog translation (.mo) file
1112 wxLogMessage("Loading catalog for opencpn core.");
1113 locale->AddCatalog("opencpn");
1114
1115 return_val = locale->GetCanonicalName();
1116
1117 // We may want to override the default system locale, so set a flag.
1118 if (return_val != GetDefaultSystemLocale())
1119 g_localeOverride = return_val;
1120 else
1121 g_localeOverride = "";
1122 }
1123
1124 *newLocale = locale; // return the new locale
1125
1126 // Always use dot as decimal
1127 setlocale(LC_NUMERIC, "C");
1128
1129 return return_val;
1130}
1131#endif
1132
1133// Setup default global options when config file is unavailable,
1134// as on initial startup after new install
1135// The global config object (pConfig) is available, so direct updates are
1136// also allowed
1137
1138void OCPNPlatform::SetDefaultOptions() {
1139 // General options, applied to all platforms
1140 g_bShowOutlines = true;
1141
1142 g_CPAMax_NM = 20.;
1143 g_CPAWarn_NM = 2.;
1144 g_TCPA_Max = 30.;
1145 g_bMarkLost = true;
1146 g_MarkLost_Mins = 8;
1147 g_bRemoveLost = true;
1148 g_RemoveLost_Mins = 10;
1149 g_bShowCOG = true;
1150 g_ShowCOG_Mins = 6;
1151 g_bSyncCogPredictors = false;
1152 g_bHideMoored = false;
1153 g_ShowMoored_Kts = 0.2;
1154 g_SOGminCOG_kts = 0.2;
1155 g_bTrackDaily = false;
1156 g_PlanSpeed = 6.;
1157 g_bFullScreenQuilt = true;
1158 g_bQuiltEnable = true;
1159 g_bskew_comp = false;
1160 g_bShowAreaNotices = false;
1161 g_bDrawAISSize = false;
1162 g_bDrawAISRealtime = false;
1163 g_AIS_RealtPred_Kts = 0.7;
1164 g_bShowAISName = false;
1165 g_nTrackPrecision = 2;
1166 g_bPreserveScaleOnX = true;
1167 g_nAWDefault = 50;
1168 g_nAWMax = 1852;
1169 gps_watchdog_timeout_ticks = kGpsTimeoutSeconds;
1170 g_n_ownship_min_mm = 8;
1171 g_bShowMuiZoomButtons = true;
1172 g_bresponsive = false;
1173
1174 // Initial S52/S57 options
1175 if (pConfig) {
1176 pConfig->SetPath("/Settings/GlobalState");
1177 pConfig->Write("bShowS57Text", true);
1178 pConfig->Write("bShowS57ImportantTextOnly", false);
1179 pConfig->Write("nDisplayCategory", (int)(_DisCat)OTHER);
1180 pConfig->Write("nSymbolStyle", (int)(_LUPname)PAPER_CHART);
1181 pConfig->Write("nBoundaryStyle", (int)(_LUPname)PLAIN_BOUNDARIES);
1182
1183 pConfig->Write("bShowSoundg", true);
1184 pConfig->Write("bShowMeta", false);
1185 pConfig->Write("bUseSCAMIN", true);
1186 pConfig->Write("bShowAtonText", false);
1187 pConfig->Write("bShowLightDescription", false);
1188 pConfig->Write("bExtendLightSectors", true);
1189 pConfig->Write("bDeClutterText", true);
1190 pConfig->Write("bShowNationalText", true);
1191
1192 pConfig->Write("S52_MAR_SAFETY_CONTOUR", 3);
1193 pConfig->Write("S52_MAR_SHALLOW_CONTOUR", 2);
1194 pConfig->Write("S52_MAR_DEEP_CONTOUR", 6);
1195 pConfig->Write("S52_MAR_TWO_SHADES", 0);
1196 pConfig->Write("S52_DEPTH_UNIT_SHOW", 1);
1197
1198 pConfig->Write("ZoomDetailFactorVector", 3);
1199
1200 pConfig->Write("nColorScheme", 1); // higher contrast on NOAA RNCs
1201
1202// A few more often requested defaults, not applicable to Android
1203#ifndef __ANDROID__
1204 g_bEnableZoomToCursor = true;
1205 g_bsmoothpanzoom = true;
1206 g_bShowMenuBar = true;
1207#endif
1208 }
1209
1210#ifdef __WXMSW__
1211 g_bShowMenuBar = false;
1212 // Enable some default PlugIns, and their default options
1213 if (pConfig) {
1214 pConfig->SetPath("/PlugIns/chartdldr_pi.dll");
1215 pConfig->Write("bEnabled", true);
1216
1217 pConfig->SetPath("/PlugIns/wmm_pi.dll");
1218 pConfig->Write("bEnabled", true);
1219
1220 pConfig->SetPath("/Settings/WMM");
1221 pConfig->Write("ShowIcon", true);
1222 pConfig->Write("ShowLiveIcon", true);
1223 }
1224#endif
1225
1226#ifdef __WXOSX__
1227 // Enable some default PlugIns, and their default options
1228 if (pConfig) {
1229 pConfig->SetPath("/PlugIns/libchartdldr_pi.dylib");
1230 pConfig->Write("bEnabled", true);
1231
1232 pConfig->SetPath("/PlugIns/libwmm_pi.dylib");
1233 pConfig->Write("bEnabled", true);
1234
1235 pConfig->SetPath("/Settings/WMM");
1236 pConfig->Write("ShowIcon", true);
1237 pConfig->Write("ShowLiveIcon", true);
1238 }
1239#endif
1240
1241#ifdef __linux__
1242 // Enable some default PlugIns, and their default options
1243 if (pConfig) {
1244 pConfig->SetPath("/PlugIns/libchartdldr_pi.so");
1245 pConfig->Write("bEnabled", true);
1246
1247 pConfig->SetPath("/PlugIns/libwmm_pi.so");
1248 pConfig->Write("bEnabled", true);
1249
1250 pConfig->SetPath("/Settings/WMM");
1251 pConfig->Write("ShowIcon", true);
1252 pConfig->Write("ShowLiveIcon", true);
1253 }
1254#endif
1255
1256#ifdef __ANDROID__
1257
1258#ifdef ocpnUSE_GL
1259 g_bopengl = true;
1260 g_GLOptions.m_bTextureCompression = 1;
1261 g_GLOptions.m_bTextureCompressionCaching = 1;
1262#endif
1263
1264 qDebug() << "SetDefaultOptions";
1265
1266 g_btouch = true;
1267 g_bresponsive = true;
1268 g_default_font_size = 18; // This is pretty close to TextAppearance.Medium
1269 g_bUIexpert = true;
1270
1271 g_bShowStatusBar = true;
1272 g_cm93_zoom_factor = 0;
1273 g_oz_vector_scale = false;
1274 g_fog_overzoom = false;
1275
1276 g_bRollover = true;
1277 g_bShowMuiZoomButtons = true;
1278
1279 g_GUIScaleFactor = 0; // nominal
1280 g_ChartNotRenderScaleFactor = 2.0;
1281
1282 // Suppress most tools, especially those that appear in the Basic menus.
1283 // Of course, they may be re-enabled by experts...
1284 g_toolbarConfig = "X.....XX.......XX.XXXXXXXXXXX";
1285 g_bPermanentMOBIcon = false;
1286
1287 wxString sGPS = "2;3;;0;0;;0;1;0;0;;0;;1;0;0;0;0"; // 17 parms
1288 ConnectionParams *new_params = new ConnectionParams(sGPS);
1289
1290 new_params->bEnabled = true;
1291 TheConnectionParams().push_back(new_params);
1292
1293 g_default_font_facename = "Roboto";
1294
1295 // Enable some default PlugIns, and their default options
1296
1297 if (pConfig) {
1298 pConfig->SetPath("/PlugIns/libchartdldr_pi.so");
1299 pConfig->Write("bEnabled", true);
1300
1301 pConfig->SetPath("/PlugIns/libwmm_pi.so");
1302 pConfig->Write("bEnabled", true);
1303
1304 pConfig->SetPath("/Settings/WMM");
1305 pConfig->Write("ShowIcon", true);
1306 pConfig->Write("ShowLiveIcon", true);
1307
1308 pConfig->SetPath("/PlugIns/libgrib_pi.so");
1309 pConfig->Write("bEnabled", true);
1310
1311 pConfig->SetPath("/PlugIns/libdashboard_pi.so");
1312 pConfig->Write("bEnabled", true);
1313
1314 pConfig->SetPath("/PlugIns/GRIB");
1315 pConfig->Write("GRIBCtrlBarPosX", 100);
1316 pConfig->Write("GRIBCtrlBarPosY", 0);
1317
1318 pConfig->SetPath("/Settings/GRIB");
1319 pConfig->Write("CursorDataShown", 0);
1320
1321 // This is ugly hack
1322 // TODO
1323 pConfig->SetPath("/PlugIns/liboesenc_pi.so");
1324 pConfig->Write("bEnabled", true);
1325
1326 pConfig->SetPath("/Settings/QTFonts");
1327
1328 // Status Bar
1329 wxString str = "en_US-b25a3899";
1330 wxString pval = "StatusBar:Roboto,26,-1,5,75,0,0,0,0,0:rgb(0, 0, 0)";
1331 pConfig->Write(str, pval);
1332 FontMgr::Get().LoadFontNative(&str, &pval);
1333
1334 // Dialog
1335 str = "en_US-9c3b3a0d";
1336 pval = "DialogStatusBar:Roboto,18,-1,5,50,0,0,0,0,0:rgb(0, 0, 0)";
1337 pConfig->Write(str, pval);
1338 FontMgr::Get().LoadFontNative(&str, &pval);
1339
1340 // Set track default color to magenta
1341 pConfig->SetPath("/Settings/Others");
1342 pConfig->Write("TrackLineColour", "#C545C3");
1343 g_colourTrackLineColour.Set(197, 69, 195);
1344
1345 qDebug() << "SetDefaultOptions.Config";
1346 }
1347
1348#endif
1349}
1350
1351// Setup global options on upgrade detected
1352// The global config object (pConfig) has already been loaded, so updates
1353// here override values set by config.
1354// Direct updates to config for next boot are also supported.
1355
1356void OCPNPlatform::SetUpgradeOptions(wxString vNew, wxString vOld) {
1357#ifdef __ANDROID__
1358
1359 qDebug() << "Upgrade check"
1360 << "from: " << vOld.mb_str() << " to: " << vNew.mb_str();
1361
1362 if (androidGetVersionCode() > g_AndroidVersionCode) { // upgrade
1363 qDebug() << "Upgrade detected"
1364 << "from VC: " << g_AndroidVersionCode
1365 << " to VC: " << androidGetVersionCode();
1366
1367 // Set some S52/S57 options
1368 if (pConfig) {
1369 pConfig->SetPath("/Settings/GlobalState");
1370 pConfig->Write("bShowS57Text", true);
1371 }
1372
1373 g_ChartNotRenderScaleFactor = 2.0;
1374 g_n_ownship_min_mm = 8;
1375 g_toolbarConfig = "X.....XX.......XX.XXXXXXXXXXX";
1376
1377 // Avoid changing fonts on app upgrade.
1378 // pConfig->DeleteGroup("/Settings/QTFonts");
1379 // g_default_font_size = 20;
1380 // g_default_font_facename = "Roboto";
1381 // FontMgr::Get().Shutdown(); // Restart the font manager
1382
1383 // Reshow the zoom buttons
1384 g_bShowMuiZoomButtons = true;
1385
1386 // Clear the default chart storage location
1387 // Will get set to e.g. "/storage/emulated/0" later
1388 pInit_Chart_Dir->Clear();
1389
1390 // A few popular requests,
1391 // which will take effect on next App startup.
1392 pConfig->SetPath("/Settings/WMM");
1393 pConfig->Write("ShowIcon", true);
1394 pConfig->Write("ShowLiveIcon", true);
1395
1396 pConfig->SetPath("/Canvas/CanvasConfig1");
1397 pConfig->Write("canvasENCShowVisibleSectorLights", 0);
1398
1399 // Manage plugins
1400 // Clear the cache, allowing deprecation of obsolete plugins
1402
1403 // Remove any problematic plugins, so preparing for upgraded versions
1404 // Todo: Scrub this list from time to time
1405
1406 // VersionCode 123 (August, 2025)
1407 AndroidRemoveSystemFile(
1408 "/data/user/0/org.opencpn.opencpn/manPlug/libchartscale_pi.so");
1409 }
1410
1411 // Set track default color to magenta
1412 g_colourTrackLineColour.Set(197, 69, 195);
1413#endif
1414
1415 // Check for upgrade....
1416 if (!vOld.IsSameAs(vNew)) { // upgrade
1417
1418 // Verify some default directories, create if necessary
1419
1420 // UserIcons
1421 wxString UserIconPath = GetPrivateDataDir();
1422 wxChar sep = wxFileName::GetPathSeparator();
1423 if (UserIconPath.Last() != sep) UserIconPath.Append(sep);
1424 UserIconPath.Append("UserIcons");
1425
1426 if (!::wxDirExists(UserIconPath)) {
1427 ::wxMkdir(UserIconPath);
1428 }
1429
1430 // layers
1431 wxString LayersPath = GetPrivateDataDir();
1432 if (LayersPath.Last() != sep) LayersPath.Append(sep);
1433 LayersPath.Append("layers");
1434
1435 if (!::wxDirExists(LayersPath)) {
1436 ::wxMkdir(LayersPath);
1437 }
1438
1439 // Force a generally useable sound command, overriding any previous user's
1440 // selection
1441 // that may not be available on new build.
1442 g_CmdSoundString = wxString(OCPN_SOUND_CMD);
1443 pConfig->SetPath("/Settings");
1444 pConfig->Write("CmdSoundString", g_CmdSoundString);
1445
1446 // Force AIS specific sound effects ON, leaving the master control
1447 // (g_bAIS_CPA_Alert_Audio) as configured
1448 g_bAIS_GCPA_Alert_Audio = true;
1449 g_bAIS_SART_Alert_Audio = true;
1450 g_bAIS_DSC_Alert_Audio = true;
1451
1452 // Force a recalculation of default main toolbar location
1453 g_maintoolbar_x = -1;
1454
1455 // Check the tide/current databases for readability,
1456 // remove any not readable
1457 std::vector<std::string> TCDS_temp;
1458 for (unsigned int i = 0; i < TideCurrentDataSet.size(); i++)
1459 TCDS_temp.push_back(TideCurrentDataSet[i]);
1460
1461 TideCurrentDataSet.clear();
1462 for (unsigned int i = 0; i < TCDS_temp.size(); i++) {
1463 wxString tide = TCDS_temp[i];
1464 wxFileName ft(tide);
1465 if (ft.FileExists()) TideCurrentDataSet.push_back(TCDS_temp[i]);
1466 }
1467
1468 // Force default (baked-in) values for CompatOS
1469 // Thus providing for the case when some core dependent elements
1470 // are updated. (e.g. flatpak SDK 22.08->24.08->???)
1471 g_compatOS = "";
1472 g_compatOsVersion = "";
1473 pConfig->SetPath("/Settings");
1474 pConfig->Write("CompatOS", g_compatOS);
1475 pConfig->Write("CompatOsVersion", g_compatOsVersion);
1476 }
1477}
1478
1479int OCPNPlatform::platformApplyPrivateSettingsString(wxString settings,
1480 ArrayOfCDI *pDirArray) {
1481 int ret_val = 0;
1482#ifdef __ANDROID__
1483 ret_val = androidApplySettingsString(settings, pDirArray);
1484#endif
1485
1486 return ret_val;
1487}
1488
1489void OCPNPlatform::applyExpertMode(bool mode) {
1490#ifdef __ANDROID__
1491 // g_bexpert = mode; // toolbar only shows plugin icons if expert mode is
1492 // false
1493 g_bBasicMenus = !mode; // simplified context menus in basic mode
1494#endif
1495}
1496
1497//--------------------------------------------------------------------------
1498// Per-Platform file/directory support
1499//--------------------------------------------------------------------------
1500
1501static wxString ExpandPaths(wxString paths, OCPNPlatform *platform);
1502
1503int OCPNPlatform::DoFileSelectorDialog(wxWindow *parent, wxString *file_spec,
1504 wxString Title, wxString initDir,
1505 wxString suggestedName,
1506 wxString wildcard) {
1507 wxString file;
1508 int result = wxID_CANCEL;
1509
1510#ifdef __ANDROID__
1511 // Verify that initDir is traversable, fix it if not...
1512 wxString idir = initDir;
1513 if (initDir.StartsWith(
1514 "/data/data")) // not good, provokes a crash usually...
1515 idir = GetWritableDocumentsDir();
1516
1517 result = androidFileChooser(&file, idir, Title, suggestedName, wildcard);
1518 if (file_spec) *file_spec = file;
1519#else
1520 long flag = wxFD_DEFAULT_STYLE;
1521 if (suggestedName.Length()) { // new file
1522 flag = wxFD_SAVE;
1523 }
1524
1525 wxString mask = wildcard;
1526 if (wxNOT_FOUND != mask.Find("gpx")) mask.Prepend("GPX files (*.gpx)|");
1527
1528 wxFileDialog *psaveDialog =
1529 new wxFileDialog(parent, Title, initDir, suggestedName, mask, flag);
1530
1531 // Try to reduce the dialog size, and scale fonts down, if necessary.
1532 // if(g_bresponsive && parent)
1533 // psaveDialog = g_Platform->AdjustFileDialogFont(parent,
1534 // psaveDialog);
1535
1536#ifdef __WXOSX__
1537 if (parent) parent->HideWithEffect(wxSHOW_EFFECT_BLEND);
1538#endif
1539
1540 result = psaveDialog->ShowModal();
1541
1542#ifdef __WXOSX__
1543 if (parent) parent->ShowWithEffect(wxSHOW_EFFECT_BLEND);
1544#endif
1545
1546 if (file_spec) *file_spec = psaveDialog->GetPath();
1547 delete psaveDialog;
1548
1549#endif
1550
1551 return result;
1552}
1553
1554int OCPNPlatform::DoDirSelectorDialog(wxWindow *parent, wxString *file_spec,
1555 wxString Title, wxString initDir,
1556 bool b_addFiles) {
1557 wxString dir;
1558 int result = wxID_CANCEL;
1559
1560#ifdef __ANDROID__
1561 // Verify that initDir is traversable, fix it if not...
1562 wxString idir = initDir;
1563 if (initDir.StartsWith(
1564 "/data/data")) // not good, provokes a crash usually...
1565 idir = GetWritableDocumentsDir();
1566
1567 result = androidFileChooser(&dir, idir, Title, "", "", true,
1568 b_addFiles); // Directories only, maybe add dirs
1569 if (file_spec) *file_spec = dir;
1570#else
1571 wxDirDialog *dirSelector = new wxDirDialog(
1572 parent, Title, initDir, wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST);
1573
1574 wxFont *qFont = GetOCPNScaledFont(_("Dialog"));
1575 dirSelector->SetFont(*qFont);
1576
1577 // Try to reduce the dialog size, and scale fonts down, if necessary.
1578 // if(g_bresponsive && parent)
1579 // dirSelector = AdjustDirDialogFont(parent, dirSelector);
1580
1581#ifdef __WXOSX__
1582 if (parent) parent->HideWithEffect(wxSHOW_EFFECT_BLEND);
1583#endif
1584
1585 result = dirSelector->ShowModal();
1586
1587#ifdef __WXOSX__
1588 if (parent) parent->ShowWithEffect(wxSHOW_EFFECT_BLEND);
1589#endif
1590
1591 if (result == wxID_CANCEL) {
1592 } else {
1593 if (file_spec) {
1594 *file_spec = dirSelector->GetPath();
1595 }
1596 }
1597
1598 delete dirSelector;
1599#endif
1600
1601 return result;
1602}
1603
1604MyConfig *OCPNPlatform::GetConfigObject() {
1605 MyConfig *result = NULL;
1606
1607 result = new MyConfig(GetConfigFileName());
1608
1609 return result;
1610}
1611
1612//--------------------------------------------------------------------------
1613// Internal GPS Support
1614//--------------------------------------------------------------------------
1615
1616bool OCPNPlatform::hasInternalGPS(wxString profile) {
1617#ifdef __ANDROID__
1618 bool t = androidDeviceHasGPS();
1619 // qDebug() << "androidDeviceHasGPS" << t;
1620 return t;
1621#else
1622
1623 return false;
1624
1625#endif
1626}
1627
1628//--------------------------------------------------------------------------
1629// Platform Display Support
1630//--------------------------------------------------------------------------
1631
1632void OCPNPlatform::ShowBusySpinner() { AbstractPlatform::ShowBusySpinner(); }
1633
1634void OCPNPlatform::HideBusySpinner() { AbstractPlatform::HideBusySpinner(); }
1635
1636double OCPNPlatform::GetDisplayDensityFactor() {
1637#ifdef __ANDROID__
1638 return getAndroidDisplayDensity();
1639#else
1640 return 1.0;
1641#endif
1642}
1643
1644long OCPNPlatform::GetDefaultToolbarOrientation() {
1645#ifndef __ANDROID__
1646 return wxTB_VERTICAL;
1647#else
1648 return wxTB_VERTICAL;
1649#endif
1650}
1651
1652int OCPNPlatform::GetStatusBarFieldCount() {
1653#ifdef __ANDROID__
1654 int count = 1;
1655
1656 // Make a horizontal measurement...
1657 wxScreenDC dc;
1658 wxFont *templateFont = FontMgr::Get().GetFont(_("StatusBar"), 0);
1659 dc.SetFont(*templateFont);
1660
1661 int width;
1662 dc.GetTextExtent("WWWWWW", &width, NULL, NULL, NULL, templateFont);
1663 double font_size_pix = (double)width / 6.0;
1664
1665 wxSize dispSize = getDisplaySize();
1666
1667 double nChars = dispSize.x / font_size_pix;
1668
1669 if (nChars < 40)
1670 count = 1;
1671 else
1672 count = 2;
1673
1674 return count;
1675
1676#else
1677 return kStatFieldCount; // default
1678#endif
1679}
1680
1681double OCPNPlatform::getFontPointsperPixel() {
1682 double pt_per_pixel = 1.0;
1683
1684 // #ifdef __ANDROID__
1685 // On Android, this calculation depends on the density bucket in use.
1686 // Also uses some magic numbers...
1687 // For reference, see http://pixplicity.com/dp-px-converter/
1688 // pt_per_pixel = 14.0 / (31.11 * getAndroidDisplayDensity()) ;
1689
1690 // #else
1691
1692 if (m_pt_per_pixel == 0) {
1693 // Make a measurement...
1694 wxScreenDC dc;
1695
1696 wxFont *f = FontMgr::Get().FindOrCreateFont(
1697 12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, FALSE,
1698 wxString(""), wxFONTENCODING_SYSTEM);
1699 dc.SetFont(*f);
1700
1701 int width, height;
1702 dc.GetTextExtent("H", &width, &height, NULL, NULL, f);
1703
1704 if (height > 0) m_pt_per_pixel = 12.0 / (double)height;
1705 }
1706 if (m_pt_per_pixel > 0) pt_per_pixel = m_pt_per_pixel;
1707 // #endif
1708
1709 return pt_per_pixel;
1710}
1711
1713#ifdef __ANDROID__
1714 return getAndroidDisplayDimensions();
1715#else
1716 return wxSize(g_monitor_info[g_current_monitor].width,
1718#endif
1719}
1720
1722 if (g_current_monitor < m_displaySizeMMOverride.size()) {
1723 if (m_displaySizeMMOverride[g_current_monitor] > 0) {
1724 return m_displaySizeMMOverride[g_current_monitor];
1725 }
1726 }
1727
1728 double ret = g_monitor_info[g_current_monitor].width_mm;
1729
1730#ifdef __ANDROID__
1731 ret = GetAndroidDisplaySize();
1732#endif
1733
1734 return ret;
1735}
1736
1737double OCPNPlatform::GetDisplayAreaCM2() {
1738 double size1 = GetDisplaySizeMM();
1739 wxSize sz = getDisplaySize();
1740 double ratio = 1.;
1741 if (sz.x < sz.y)
1742 ratio = (double)sz.x / (double)sz.y; // <1
1743 else
1744 ratio = (double)sz.y / (double)sz.x; // <1
1745
1746 double area = size1 * (size1 * ratio) / 100.;
1747 // qDebug() << "cm2" << size1 << ratio << sz.x << sz.y;
1748 return area;
1749}
1750
1751void OCPNPlatform::SetDisplaySizeMM(size_t monitor, double sizeMM) {
1752 if (monitor < m_displaySizeMMOverride.size()) {
1753 m_displaySizeMMOverride[monitor] = sizeMM;
1754 }
1755}
1756
1757double OCPNPlatform::GetDisplayDPmm() {
1758#ifdef __ANDROID__
1759 return getAndroidDPmm();
1760#else
1761 double r = getDisplaySize().x; // dots
1762 return r / GetDisplaySizeMM();
1763#endif
1764}
1765
1766unsigned int OCPNPlatform::GetSelectRadiusPix() {
1767 return GetDisplayDPmm() *
1768 (g_btouch ? g_selection_radius_touch_mm : g_selection_radius_mm);
1769}
1770
1771bool OCPNPlatform::GetFullscreen() {
1772 bool bret = false;
1773#ifdef __ANDROID__
1774 bret = androidGetFullscreen();
1775#else
1776
1777#endif
1778
1779 return bret;
1780}
1781
1782bool OCPNPlatform::SetFullscreen(bool bFull) {
1783 bool bret = false;
1784#ifdef __ANDROID__
1785 bret = androidSetFullscreen(bFull);
1786#else
1787#endif
1788
1789 return bret;
1790}
1791
1792void OCPNPlatform::PositionAISAlert(wxWindow *alert_window) {
1793#ifndef __ANDROID__
1794 if (alert_window) {
1795 alert_window->SetSize(g_ais_alert_dialog_x, g_ais_alert_dialog_y,
1796 g_ais_alert_dialog_sx, g_ais_alert_dialog_sy);
1797 }
1798#else
1799 if (alert_window) {
1800 alert_window->Centre();
1801 }
1802
1803#endif
1804}
1805
1806wxDirDialog *OCPNPlatform::AdjustDirDialogFont(wxWindow *container,
1807 wxDirDialog *dlg) {
1808 wxDirDialog *ret_dlg = dlg;
1809#ifndef __WXGTK__
1810
1811 dlg->Show();
1812 dlg->SetSize(container->GetSize());
1813 dlg->Centre();
1814
1815 wxSize sds = dlg->GetSize();
1816 wxSize ss = container->GetSize();
1817
1818 if (sds.x > ss.x) {
1819 dlg->Hide();
1820
1821 wxString msg = dlg->GetMessage();
1822 wxString default_dir = dlg->GetPath();
1823
1824 delete dlg;
1825
1826 ret_dlg = new wxDirDialog(NULL, msg, default_dir,
1827 wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST);
1828
1829 wxFont *dialogFont = GetOCPNScaledFont(_("Dialog"));
1830 wxFont *smallFont = new wxFont(*dialogFont);
1831 smallFont->SetPointSize((smallFont->GetPointSize() / 2) +
1832 0.5); // + 0.5 to round instead of truncate
1833 ret_dlg->SetFont(*smallFont);
1834
1835 ret_dlg->SetSize(container->GetSize());
1836 ret_dlg->Centre();
1837 }
1838 ret_dlg->Hide();
1839#endif
1840 return ret_dlg;
1841}
1842
1843wxFileDialog *OCPNPlatform::AdjustFileDialogFont(wxWindow *container,
1844 wxFileDialog *dlg) {
1845 wxFileDialog *ret_dlg = dlg;
1846#ifndef __WXGTK__
1847
1848 dlg->Show();
1849 dlg->SetSize(container->GetSize());
1850 dlg->Centre();
1851
1852 wxSize sds = dlg->GetSize();
1853 wxSize ss = container->GetSize();
1854
1855 if (sds.x > ss.x) {
1856 dlg->Hide();
1857
1858 wxString msg = dlg->GetMessage();
1859 wxString default_dir = dlg->GetDirectory();
1860 wxString default_file = dlg->GetFilename();
1861 wxString wildcard = dlg->GetWildcard();
1862
1863 delete dlg;
1864
1865 ret_dlg = new wxFileDialog(NULL, msg, default_dir, default_file, wildcard,
1866 wxFD_OPEN);
1867
1868 wxFont *dialogFont = GetOCPNScaledFont(_("Dialog"));
1869 wxFont *smallFont = new wxFont(*dialogFont);
1870 smallFont->SetPointSize((smallFont->GetPointSize() / 2) +
1871 0.5); // + 0.5 to round instead of truncate
1872 ret_dlg->SetFont(*smallFont);
1873
1874 ret_dlg->SetSize(container->GetSize());
1875 ret_dlg->Centre();
1876 }
1877 ret_dlg->Hide();
1878#endif
1879 return ret_dlg;
1880}
1881
1882double OCPNPlatform::GetToolbarScaleFactor(int GUIScaleFactor) {
1883 double rv = 1.0;
1884#ifdef __ANDROID__
1885
1886 // We try to arrange matters so that at GUIScaleFactor=0, the tool icons are
1887 // approximately 9 mm in size and that the value may range from 0.5 -> 2.0
1888
1889 // Get the basic size of a tool icon
1890 wxSize style_tool_size(32, 32);
1891
1892 if (g_StyleManager) {
1893 ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
1894 if (style) style_tool_size = style->GetToolSize();
1895 }
1896 double tool_size = style_tool_size.x;
1897
1898 // unless overridden by user, we declare the "best" tool size
1899 // to be roughly the same as the ActionBar height.
1900 // This may be approximated in a device orientation-independent way as:
1901 // 45pixels * DENSITY
1902 double premult = 1.0;
1903 if (g_config_display_size_manual && g_config_display_size_mm.size()) {
1904 if (g_config_display_size_mm[0] > 0) {
1905 double target_size = 9.0; // mm
1906 double basic_tool_size_mm = tool_size / GetDisplayDPmm();
1907 premult = target_size / basic_tool_size_mm;
1908 }
1909 } else {
1910 premult = wxMax(45 * getAndroidDisplayDensity(), 45) /
1911 tool_size; // make sure not too small
1912 }
1913
1914 // Adjust the scale factor using the global GUI scale parameter, ranging from
1915 // 0.5 -> 2.0
1916 double postmult = exp(GUIScaleFactor * (log(2.0) / 5.0));
1917
1918 // qDebug() << "parmsF" << GUIScaleFactor << premult << postmult;
1919
1920 rv = premult * postmult;
1921 rv = wxMin(rv, getAndroidDisplayDensity() *
1922 3); // Clamp at density * arbitrary limit factor
1923
1924#else
1925 double premult = 1.0;
1926
1927 if (g_bresponsive) {
1928 // Get the basic size of a tool icon
1929 ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
1930 wxSize style_tool_size = style->GetToolSize();
1931 double tool_size = style_tool_size.x;
1932
1933 // unless overridden by user, we declare the "best" tool size
1934 // to be roughly 9 mm square.
1935 double target_size = 9.0; // mm
1936
1937 double basic_tool_size_mm = tool_size / GetDisplayDPmm();
1938 premult = target_size / basic_tool_size_mm;
1939 }
1940
1941 // Adjust the scale factor using the global GUI scale parameter
1942 double postmult = exp(GUIScaleFactor * (0.693 / 5.0)); // exp(2)
1943
1944 rv = premult * postmult;
1945 rv = wxMin(rv, 3.0); // Clamp at 3.0
1946 rv = wxMax(rv, 0.5); // and at 0.5
1947
1948 rv /= g_BasePlatform->GetDisplayDIPMult(wxTheApp->GetTopWindow());
1949
1950#endif
1951
1952 return rv;
1953}
1954
1955double OCPNPlatform::GetCompassScaleFactor(int GUIScaleFactor) {
1956 double rv = 1.0;
1957#ifdef __ANDROID__
1958
1959 // We try to arrange matters so that at GUIScaleFactor=0, the compass icon is
1960 // approximately 9 mm in size and that the value may range from 0.5 -> 2.0
1961
1962 if (g_bresponsive) {
1963 // Get the basic size of a tool icon
1964 ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
1965 wxSize style_tool_size = style->GetToolSize();
1966 double compass_size = style_tool_size.x;
1967
1968 // We declare the "nominal best" icon size to be a bit smaller than the
1969 // ActionBar height.
1970 // This may be approximated in a device orientation-independent way as:
1971 // 28pixels * DENSITY
1972 double premult = wxMax(28 * getAndroidDisplayDensity(), 50) / compass_size;
1973
1974 // Adjust the scale factor using the global GUI scale parameter
1975 double postmult = exp(GUIScaleFactor * (log(2.0) / 5.0));
1976 // rv = wxMin(rv, 1.5); // Clamp at 1.5
1977
1978 rv = premult * postmult;
1979 rv = wxMin(rv, getAndroidDisplayDensity() *
1980 3); // Clamp at density * arbitrary limit factor
1981 }
1982
1983#else
1984 double premult = 1.0;
1985
1986 if (g_bresponsive) {
1987 ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
1988 wxSize style_tool_size = style->GetToolSize();
1989 double compass_size = style_tool_size.x;
1990
1991 // We declare the "best" tool size to be roughly 6 mm.
1992 double target_size = 6.0; // mm
1993
1994 double basic_tool_size_mm = compass_size / GetDisplayDPmm();
1995 premult = target_size / basic_tool_size_mm;
1996 }
1997
1998 double postmult = exp(GUIScaleFactor * (0.693 / 5.0)); // exp(2)
1999
2000 rv = premult * postmult;
2001
2002 rv = wxMin(rv, 3.0); // Clamp at 3.0
2003 rv = wxMax(rv, 0.5);
2004
2005#if defined(__WXOSX__) || defined(__WXGTK3__)
2006 // Support scaled HDPI displays.
2007
2008 if (top_frame::Get()) rv *= top_frame::Get()->GetContentScaleFactor();
2009#endif
2010
2011 rv /= g_BasePlatform->GetDisplayDIPMult(wxTheApp->GetTopWindow());
2012
2013#endif
2014
2015 return rv;
2016}
2017
2018float OCPNPlatform::GetChartScaleFactorExp(float scale_linear) {
2019 double factor = 1.0;
2020#ifndef __ANDROID__
2021 factor = exp(scale_linear * (log(3.0) / 5.0));
2022
2023#else
2024 // the idea here is to amplify the scale factor for higher density displays,
2025 // in a measured way....
2026 factor = exp(scale_linear * (0.693 / 5.0));
2027// factor *= getAndroidDisplayDensity();
2028#endif
2029
2030 factor = wxMax(factor, .5);
2031 factor = wxMin(factor, 6.);
2032
2033 return factor;
2034}
2035
2036float OCPNPlatform::GetMarkScaleFactorExp(float scale_linear) {
2037 if (scale_linear <= 0)
2038 return GetChartScaleFactorExp(scale_linear);
2039 else
2040 return GetChartScaleFactorExp(scale_linear - 1);
2041}
2042
2043// float OCPNPlatform::GetDIPScaleFactor() {
2044// float rv = 1.0;
2045// #ifdef __WXMSW__
2046// if (gFrame)
2047// rv = (double)(gFrame->FromDIP(100))/100.;
2048// #endif
2049//
2050// return rv;
2051// }
2052
2053//--------------------------------------------------------------------------
2054// Internal Bluetooth Support
2055//--------------------------------------------------------------------------
2056
2057bool OCPNPlatform::hasInternalBT(wxString profile) {
2058#ifdef __ANDROID__
2059 bool t = androidDeviceHasBlueTooth();
2060 // qDebug() << "androidDeviceHasBluetooth" << t;
2061 return t;
2062#else
2063
2064 return false;
2065#endif
2066}
2067
2068bool OCPNPlatform::startBluetoothScan() {
2069#ifdef __ANDROID__
2070 return androidStartBluetoothScan();
2071#else
2072
2073 return false;
2074#endif
2075}
2076
2077bool OCPNPlatform::stopBluetoothScan() {
2078#ifdef __ANDROID__
2079 return androidStopBluetoothScan();
2080#else
2081
2082 return false;
2083#endif
2084}
2085
2086wxArrayString OCPNPlatform::getBluetoothScanResults() {
2087 wxArrayString ret_val;
2088#ifdef __ANDROID__
2089 return androidGetBluetoothScanResults();
2090#else
2091
2092 ret_val.Add("line 1");
2093 ret_val.Add("line 2");
2094 ret_val.Add("line 3");
2095 return ret_val;
2096
2097#endif
2098}
2099
2100//--------------------------------------------------------------------------
2101// Per-Platform Utility support
2102//--------------------------------------------------------------------------
2103
2104bool OCPNPlatform::AllowAlertDialog(const wxString &class_name) {
2105#ifdef __ANDROID__
2106 // allow if TopLevelWindow count is <=4, implying normal runtime screen
2107 // layout
2108 int nTLW = 0;
2109 wxWindowList::compatibility_iterator node = wxTopLevelWindows.GetFirst();
2110 while (node) {
2111 wxWindow *win = node->GetData();
2112 if (win->IsShown()) nTLW++;
2113
2114 node = node->GetNext();
2115 }
2116
2117 // qDebug() << "AllowAlertDialog" << g_boptionsactive << g_running << nTLW;
2118 if (g_options)
2119 return (g_running && !g_options->IsShown() && (nTLW <= 4));
2120 else
2121 return (g_running && (nTLW <= 4));
2122
2123#else
2124 return true;
2125#endif
2126}
2127
2128void OCPNPlatform::setChartTypeMaskSel(int mask, wxString &indicator) {
2129#ifdef __ANDROID__
2130 return androidSetChartTypeMaskSel(mask, indicator);
2131#endif
2132}
2133
2134#ifdef __ANDROID__
2135QString g_qtStyleSheet;
2136
2137bool LoadQtStyleSheet(wxString &sheet_file) {
2138 if (wxFileExists(sheet_file)) {
2139 // QApplication qApp = getqApp();
2140 if (qApp) {
2141 QString file(sheet_file.c_str());
2142 QFile File(file);
2143 File.open(QFile::ReadOnly);
2144 g_qtStyleSheet = QLatin1String(File.readAll());
2145
2146 // qApp->setStyleSheet(g_qtStyleSheet);
2147 return true;
2148 } else
2149 return false;
2150 } else
2151 return false;
2152}
2153
2154QString getQtStyleSheet() { return g_qtStyleSheet; }
2155
2156#endif
2157
2158bool OCPNPlatform::isPlatformCapable(int flag) {
2159#ifndef __ANDROID__
2160 return true;
2161#else
2162 if (flag == PLATFORM_CAP_PLUGINS) {
2163 long platver;
2164 wxString tsdk(android_plat_spc.msdk);
2165 if (tsdk.ToLong(&platver)) {
2166 if (platver >= 11) return true;
2167 }
2168 } else if (flag == PLATFORM_CAP_FASTPAN) {
2169 long platver;
2170 wxString tsdk(android_plat_spc.msdk);
2171 if (tsdk.ToLong(&platver)) {
2172 if (platver >= 14) return true;
2173 }
2174 }
2175
2176 return false;
2177#endif
2178}
2179
2180void OCPNPlatform::platformLaunchDefaultBrowser(wxString URL) {
2181#ifdef __ANDROID__
2182 androidLaunchBrowser(URL);
2183#else
2184 ::wxLaunchDefaultBrowser(URL);
2185#endif
2186}
2187
2188// ============================================================================
2189// OCPNColourPickerCtrl implementation
2190// ============================================================================
2191
2192BEGIN_EVENT_TABLE(OCPNColourPickerCtrl, wxButton)
2193#ifdef __WXMSW__
2194EVT_PAINT(OCPNColourPickerCtrl::OnPaint)
2195#endif
2196END_EVENT_TABLE()
2197
2198// ----------------------------------------------------------------------------
2199// OCPNColourPickerCtrl
2200// ----------------------------------------------------------------------------
2201
2202OCPNColourPickerCtrl::OCPNColourPickerCtrl(wxWindow *parent, wxWindowID id,
2203 const wxColour &initial,
2204 const wxPoint &pos,
2205 const wxSize &size, long style,
2206 const wxValidator &validator,
2207 const wxString &name) {
2208 Create(parent, id, initial, pos, size, style, validator, name);
2209}
2210
2211bool OCPNColourPickerCtrl::Create(wxWindow *parent, wxWindowID id,
2212 const wxColour &col, const wxPoint &pos,
2213 const wxSize &size, long style,
2214 const wxValidator &validator,
2215 const wxString &name) {
2216 m_bitmap = wxBitmap(60, 13);
2217
2218 // create this button
2219 if (!wxBitmapButton::Create(parent, id, m_bitmap, pos, size,
2220 style | wxBU_AUTODRAW, validator, name)) {
2221 wxFAIL_MSG("OCPNColourPickerCtrl creation failed");
2222 return false;
2223 }
2224
2225 // and handle user clicks on it
2226 Connect(GetId(), wxEVT_BUTTON,
2227 wxCommandEventHandler(OCPNColourPickerCtrl::OnButtonClick), NULL,
2228 this);
2229
2230 m_colour = col;
2231 UpdateColour();
2232 InitColourData();
2233
2234 return true;
2235}
2236
2237void OCPNColourPickerCtrl::InitColourData() {
2238#if 0
2239 ms_data.SetChooseFull(true);
2240 unsigned char grey = 0;
2241 for (int i = 0; i < 16; i++, grey += 16)
2242 {
2243 // fill with grey tones the custom colors palette
2244 wxColour colour(grey, grey, grey);
2245 ms_data.SetCustomColour(i, colour);
2246 }
2247#endif
2248}
2249
2250void OCPNColourPickerCtrl::OnButtonClick(wxCommandEvent &WXUNUSED(ev)) {
2251#ifdef __ANDROID__
2252 unsigned int cco = 0;
2253 cco |= 0xff;
2254 cco = cco << 8;
2255 cco |= m_colour.Red();
2256 cco = cco << 8;
2257 cco |= m_colour.Green();
2258 cco = cco << 8;
2259 cco |= m_colour.Blue();
2260 unsigned int cc = androidColorPicker(cco);
2261
2262 wxColour cnew;
2263 unsigned char blue = (unsigned char)cc % 256;
2264 unsigned char green = (unsigned char)(cc >> 8) % 256;
2265 ;
2266 unsigned char red = (unsigned char)(cc >> 16) % 256;
2267 cnew.Set(red, green, blue);
2268
2269 SetColour(cnew);
2270
2271#else
2272 // update the wxColouData to be shown in the dialog
2273 ms_data.SetColour(m_colour);
2274
2275 // create the colour dialog and display it
2276 wxColourDialog dlg(this, &ms_data);
2277 if (dlg.ShowModal() == wxID_OK) {
2278 ms_data = dlg.GetColourData();
2279 SetColour(ms_data.GetColour());
2280 }
2281#endif
2282}
2283
2284void OCPNColourPickerCtrl::UpdateColour() {
2285#ifndef __ANDROID__
2286 SetBitmapLabel(wxBitmap());
2287#endif
2288
2289 wxMemoryDC dc(m_bitmap);
2290 dc.SetPen(*wxTRANSPARENT_PEN);
2291 dc.SetBrush(wxBrush(m_colour));
2292 dc.DrawRectangle(0, 0, m_bitmap.GetWidth(), m_bitmap.GetHeight());
2293
2294 dc.SelectObject(wxNullBitmap);
2295 SetBitmapLabel(m_bitmap);
2296}
2297
2298void OCPNColourPickerCtrl::SetColour(wxColour &c) {
2299 m_colour = c;
2300 m_bitmap = wxBitmap(GetSize().x - 20, GetSize().y - 20);
2301 UpdateColour();
2302}
2303
2304wxColour OCPNColourPickerCtrl::GetColour() { return m_colour; }
2305
2306wxSize OCPNColourPickerCtrl::DoGetBestSize() const {
2307 wxSize sz(wxBitmapButton::DoGetBestSize());
2308#ifdef __WXMAC__
2309 sz.y += 6;
2310#else
2311 sz.y += 2;
2312#endif
2313 sz.x += 30;
2314 if (HasFlag(wxCLRP_SHOW_LABEL)) return sz;
2315
2316 // if we have no label, then make this button a square
2317 // (like e.g. native GTK version of this control) ???
2318 // sz.SetWidth(sz.GetHeight());
2319 return sz;
2320}
2321
2322void OCPNColourPickerCtrl::OnPaint(wxPaintEvent &event) {
2323 wxPaintDC dc(this);
2324
2325 int offset_x = (GetSize().x - m_bitmap.GetWidth()) / 2;
2326 int offset_y = (GetSize().y - m_bitmap.GetHeight()) / 2;
2327
2328 dc.SetPen(*wxTRANSPARENT_PEN);
2329 dc.SetBrush(wxBrush(m_colour));
2330 dc.DrawRectangle(offset_x, offset_y, m_bitmap.GetWidth(),
2331 m_bitmap.GetHeight());
2332
2333 event.Skip();
2334}
class About
Class AboutFrameImpl.
Select * pSelectAIS
Global instance.
Class AisDecoder and helpers.
Global state for AIS decoder.
BasePlatform * g_BasePlatform
points to g_platform, handles brain-dead MS linker.
Basic platform specific support utilities without GUI deps.
wxString GetWinPluginBaseDir()
Base directory for user writable windows plugins, reflects winPluginDir option, defaults to LOCALAPPD...
double GetDisplayDIPMult(wxWindow *win)
Get the display scaling factor for DPI-aware rendering.
wxString & GetPrivateDataDir()
Return dir path for opencpn.log, etc., respecting -c cli option.
wxFont * FindOrCreateFont(int point_size, wxFontFamily family, wxFontStyle style, wxFontWeight weight, bool underline=false, const wxString &facename=wxEmptyString, wxFontEncoding encoding=wxFONTENCODING_DEFAULT)
Creates or finds a matching font in the font cache.
Definition font_mgr.cpp:442
void LoadFontNative(wxString *pConfigString, wxString *pNativeDesc)
Loads font settings from a string descriptor.
Definition font_mgr.cpp:387
wxFont * GetFont(const wxString &TextElement, int requested_font_size=0)
Get a font object for a UI element.
Definition font_mgr.cpp:193
Provides platform-specific support utilities for OpenCPN.
void SetDisplaySizeMM(size_t monitor, double size)
Set the width of the monitor in millimeters.
wxSize getDisplaySize()
Get the display size in logical pixels.
double GetDisplaySizeMM()
Get the width of the screen in millimeters.
std::string Homedir() const
home directory, convenience stuff.
std::string UserDatadir()
The single, user-writable common parent for plugin data directories, typically ending in 'plugins'.
static PluginPaths * GetInstance()
Return the singleton instance.
The JSON parser.
Definition jsonreader.h:50
int GetErrorCount() const
Return the size of the error message's array.
int Parse(const wxString &doc, wxJSONValue *val)
Parse the JSON document.
The JSON value class implementation.
Definition jsonval.h:84
int Size() const
Return the size of the array or map stored in this value.
Definition jsonval.cpp:1330
bool HasMember(unsigned index) const
Return TRUE if the object contains an element at the specified index.
Definition jsonval.cpp:1296
wxString AsString() const
Return the stored value as a wxWidget's string.
Definition jsonval.cpp:872
Global variables reflecting command line options and arguments.
bool g_bsmoothpanzoom
Controls how the chart panning and zooming smoothing is done during user interactions.
wxString g_default_font_facename
Default font size for user interface elements such as menus, dialogs, etc.
bool g_bRollover
enable/disable mouse rollover GUI effects
std::vector< size_t > g_config_display_size_mm
Size of pysical screen in millimeters.
Global variables stored in configuration file.
Connection parameters.
Dump debug info on crash.
Extern C linked utilities.
std::vector< OCPN_MonitorInfo > g_monitor_info
Information about the monitors connected to the system.
Definition displays.cpp:45
void EnumerateMonitors()
Enumerate the monitors connected to the system.
Definition displays.cpp:108
Display utilities.
Font list manager.
bool g_running
Android only.
OpenGL chart rendering canvas.
Platform independent GL includes.
wxFont * GetOCPNScaledFont(wxString item, int default_size)
Retrieves a font from FontMgr, optionally scaled for physical readability.
Definition gui_lib.cpp:61
General purpose GUI support.
size_t g_current_monitor
Current monitor displaying main application frame.
Definition gui_vars.cpp:75
Miscellaneous globals primarely used by gui layer, not persisted in configuration file.
GUI constant definitions.
Enhanced logging interface on top of wx/log.h.
MacOS hardware probing functions.
void cache_clear()
Remove all files in cache:
MyConfig * pConfig
Global instance.
Definition navutil.cpp:118
Utility functions.
OCPNPlatform * g_Platform
Global instance.
wxArrayString g_locale_catalog_array
Global instance.
OpenCPN Platform specific support utilities.
constexpr int kGpsTimeoutSeconds
Default GPS timeout.
OCPNPlatform * g_Platform
Global instance.
wxArrayString g_locale_catalog_array
Global instance.
Miscellaneous utilities, many of which string related.
options * g_options
Global instance.
Definition options.cpp:181
Options dialog.
Downloaded plugins cache.
Plugin installation and data paths support.
wxString GetWritableDocumentsDir()
Returns the platform-specific default documents directory.
Select * pSelect
Global instance.
Definition select.cpp:36
Select * pSelectTC
Global instance.
Definition select.cpp:37
Selected route, segment, waypoint, etc.
Chart Symbols.
Abstract gFrame/MyFrame interface.