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