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
125class MyApp;
126DECLARE_APP(MyApp)
127
128void appendOSDirSlash(wxString *pString);
129
130#ifndef __WXMSW__
131struct sigaction sa_all;
132struct sigaction sa_all_old;
133extern sigjmp_buf env; // the context saved by sigsetjmp();
134#endif
135
136extern OCPNPlatform *g_Platform;
137extern bool g_bFirstRun;
138extern bool g_bUpgradeInProcess;
139
140extern int quitflag;
141extern MyFrame *gFrame;
142
143extern MyConfig *pConfig;
144
145extern ocpnStyle::StyleManager *g_StyleManager;
146
147extern bool g_bshowToolbar;
148extern bool g_bexpert;
149extern bool g_bBasicMenus;
150extern bool g_bUIexpert;
151
152extern bool g_bshowToolbar;
153extern bool g_bBasicMenus;
154
155extern bool g_bShowOutlines;
156extern int g_nAWDefault;
157extern int g_nAWMax;
158extern bool g_bPermanentMOBIcon;
159extern float g_toolbar_scalefactor;
160
161extern options *g_options;
162
163extern wxString *pInit_Chart_Dir;
164
165extern std::vector<size_t> g_config_display_size_mm;
166extern bool g_config_display_size_manual;
167
168extern bool g_bFullScreenQuilt;
169extern bool g_bQuiltEnable;
170extern bool g_bskew_comp;
171
172extern bool g_bopengl;
173extern bool g_bresponsive;
174extern bool g_bShowStatusBar;
175extern int g_cm93_zoom_factor;
176extern int g_GUIScaleFactor;
177extern bool g_fog_overzoom;
178extern bool g_oz_vector_scale;
179extern wxString g_toolbarConfig;
180extern bool g_bPreserveScaleOnX;
181extern bool g_running;
182extern bool g_bEnableZoomToCursor;
183extern bool g_bsmoothpanzoom;
184extern bool g_bShowMenuBar;
185
186extern Select *pSelectTC;
187
188#ifdef ocpnUSE_GL
189extern ocpnGLOptions g_GLOptions;
190#endif
191extern int g_default_font_size;
192extern wxString g_default_font_facename;
193
194bool g_bEmailCrashReport;
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 + _T("\\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 += _T("PrivacyPolicy.txt");
418 policy_file.Prepend(_T("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, _T("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 = _T("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 = _T("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(_T("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(_T("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 += _T("share\\locale");
976 wxLocale::AddCatalogLookupPathPrefix(locale_location);
977 wxString imsg = _T("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 = _T("Adding catalog lookup path: ");
987 imsg += locale_location;
988 wxLogMessage(imsg);
989
990#elif defined(__ANDROID__)
991
992 wxString locale_location = GetSharedDataDir() + _T("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(_T("OPENCPN_PREFIX"), &locale_location)) {
1002 locale_location = _T("/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(_T("share"));
1012 location.SetName(_T("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(_T("Getting DefaultSystemLocale..."));
1035
1036 wxString retval = _T("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(_T("Found LanguageInfo for: ") + lstring);
1056 else
1057 wxLogMessage(_T("Could not find LanguageInfo for: ") + lstring);
1058 } else {
1059 wxLogMessage(_T("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(_T("HKEY_LOCAL_MACHINE\\SOFTWARE\\OpenCPN")));
1085 if (RegKey.Exists()) {
1086 wxLogMessage(
1087 _T("Retrieving initial language selection from Windows Registry"));
1088 RegKey.QueryValue(wxString(_T("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 = _T("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 = _T("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 = _T("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 = _T("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 = _T("ERROR Loading translation catalog for: ");
1182 emsg += g_locale_catalog_array[i];
1183 wxLogMessage(emsg);
1184 } else {
1185 wxString imsg = _T("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(_T("Loading catalog for opencpn core."));
1193 locale->AddCatalog(_T("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 = _T("");
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 = _T("X.....XX.......XX.XXXXXXXXXXX");
1364 g_bPermanentMOBIcon = false;
1365
1366 wxString sGPS = _T("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 = _T("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 = _T("en_US-b25a3899");
1409 wxString pval = _T("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 = _T("en_US-9c3b3a0d");
1415 pval = _T("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(_T("TrackLineColour"), _T("#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 = _T("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 = _T("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(_T("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(_T("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
1576wxString OCPNPlatform::GetSupplementalLicenseString() {
1577 wxString lic;
1578#ifdef __ANDROID__
1579 lic = androidGetSupplementalLicense();
1580#endif
1581 return lic;
1582}
1583
1584//--------------------------------------------------------------------------
1585// Per-Platform file/directory support
1586//--------------------------------------------------------------------------
1587
1588static wxString ExpandPaths(wxString paths, OCPNPlatform *platform);
1589
1590int OCPNPlatform::DoFileSelectorDialog(wxWindow *parent, wxString *file_spec,
1591 wxString Title, wxString initDir,
1592 wxString suggestedName,
1593 wxString wildcard) {
1594 wxString file;
1595 int result = wxID_CANCEL;
1596
1597#ifdef __ANDROID__
1598 // Verify that initDir is traversable, fix it if not...
1599 wxString idir = initDir;
1600 if (initDir.StartsWith(
1601 _T("/data/data"))) // not good, provokes a crash usually...
1602 idir = GetWritableDocumentsDir();
1603
1604 result = androidFileChooser(&file, idir, Title, suggestedName, wildcard);
1605 if (file_spec) *file_spec = file;
1606#else
1607 long flag = wxFD_DEFAULT_STYLE;
1608 if (suggestedName.Length()) { // new file
1609 flag = wxFD_SAVE;
1610 }
1611
1612 wxString mask = wildcard;
1613 if (wxNOT_FOUND != mask.Find(_T("gpx")))
1614 mask.Prepend(_T("GPX files (*.gpx)|"));
1615
1616 wxFileDialog *psaveDialog =
1617 new wxFileDialog(parent, Title, initDir, suggestedName, mask, flag);
1618
1619 // Try to reduce the dialog size, and scale fonts down, if necessary.
1620 // if(g_bresponsive && parent)
1621 // psaveDialog = g_Platform->AdjustFileDialogFont(parent,
1622 // psaveDialog);
1623
1624#ifdef __WXOSX__
1625 if (parent) parent->HideWithEffect(wxSHOW_EFFECT_BLEND);
1626#endif
1627
1628 result = psaveDialog->ShowModal();
1629
1630#ifdef __WXOSX__
1631 if (parent) parent->ShowWithEffect(wxSHOW_EFFECT_BLEND);
1632#endif
1633
1634 if (file_spec) *file_spec = psaveDialog->GetPath();
1635 delete psaveDialog;
1636
1637#endif
1638
1639 return result;
1640}
1641
1642int OCPNPlatform::DoDirSelectorDialog(wxWindow *parent, wxString *file_spec,
1643 wxString Title, wxString initDir,
1644 bool b_addFiles) {
1645 wxString dir;
1646 int result = wxID_CANCEL;
1647
1648#ifdef __ANDROID__
1649 // Verify that initDir is traversable, fix it if not...
1650 wxString idir = initDir;
1651 if (initDir.StartsWith(
1652 _T("/data/data"))) // not good, provokes a crash usually...
1653 idir = GetWritableDocumentsDir();
1654
1655 result = androidFileChooser(&dir, idir, Title, _T(""), _T(""), true,
1656 b_addFiles); // Directories only, maybe add dirs
1657 if (file_spec) *file_spec = dir;
1658#else
1659 wxDirDialog *dirSelector = new wxDirDialog(
1660 parent, Title, initDir, wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST);
1661
1662 wxFont *qFont = GetOCPNScaledFont(_("Dialog"));
1663 dirSelector->SetFont(*qFont);
1664
1665 // Try to reduce the dialog size, and scale fonts down, if necessary.
1666 // if(g_bresponsive && parent)
1667 // dirSelector = AdjustDirDialogFont(parent, dirSelector);
1668
1669#ifdef __WXOSX__
1670 if (parent) parent->HideWithEffect(wxSHOW_EFFECT_BLEND);
1671#endif
1672
1673 result = dirSelector->ShowModal();
1674
1675#ifdef __WXOSX__
1676 if (parent) parent->ShowWithEffect(wxSHOW_EFFECT_BLEND);
1677#endif
1678
1679 if (result == wxID_CANCEL) {
1680 } else {
1681 if (file_spec) {
1682 *file_spec = dirSelector->GetPath();
1683 }
1684 }
1685
1686 delete dirSelector;
1687#endif
1688
1689 return result;
1690}
1691
1692MyConfig *OCPNPlatform::GetConfigObject() {
1693 MyConfig *result = NULL;
1694
1695 result = new MyConfig(GetConfigFileName());
1696
1697 return result;
1698}
1699
1700//--------------------------------------------------------------------------
1701// Internal GPS Support
1702//--------------------------------------------------------------------------
1703
1704bool OCPNPlatform::hasInternalGPS(wxString profile) {
1705#ifdef __ANDROID__
1706 bool t = androidDeviceHasGPS();
1707 // qDebug() << "androidDeviceHasGPS" << t;
1708 return t;
1709#else
1710
1711 return false;
1712
1713#endif
1714}
1715
1716//--------------------------------------------------------------------------
1717// Platform Display Support
1718//--------------------------------------------------------------------------
1719
1720void OCPNPlatform::ShowBusySpinner(void) {
1721 AbstractPlatform::ShowBusySpinner();
1722}
1723
1724void OCPNPlatform::HideBusySpinner(void) {
1725 AbstractPlatform::HideBusySpinner();
1726}
1727
1728double OCPNPlatform::GetDisplayDensityFactor() {
1729#ifdef __ANDROID__
1730 return getAndroidDisplayDensity();
1731#else
1732 return 1.0;
1733#endif
1734}
1735
1736long OCPNPlatform::GetDefaultToolbarOrientation() {
1737#ifndef __ANDROID__
1738 return wxTB_VERTICAL;
1739#else
1740 return wxTB_VERTICAL;
1741#endif
1742}
1743
1744int OCPNPlatform::GetStatusBarFieldCount() {
1745#ifdef __ANDROID__
1746 int count = 1;
1747
1748 // Make a horizontal measurement...
1749 wxScreenDC dc;
1750 wxFont *templateFont = FontMgr::Get().GetFont(_("StatusBar"), 0);
1751 dc.SetFont(*templateFont);
1752
1753 int width;
1754 dc.GetTextExtent(_T("WWWWWW"), &width, NULL, NULL, NULL, templateFont);
1755 double font_size_pix = (double)width / 6.0;
1756
1757 wxSize dispSize = getDisplaySize();
1758
1759 double nChars = dispSize.x / font_size_pix;
1760
1761 if (nChars < 40)
1762 count = 1;
1763 else
1764 count = 2;
1765
1766 return count;
1767
1768#else
1769 return STAT_FIELD_COUNT; // default
1770#endif
1771}
1772
1773double OCPNPlatform::getFontPointsperPixel(void) {
1774 double pt_per_pixel = 1.0;
1775
1776 // #ifdef __ANDROID__
1777 // On Android, this calculation depends on the density bucket in use.
1778 // Also uses some magic numbers...
1779 // For reference, see http://pixplicity.com/dp-px-converter/
1780 // pt_per_pixel = 14.0 / (31.11 * getAndroidDisplayDensity()) ;
1781
1782 // #else
1783
1784 if (m_pt_per_pixel == 0) {
1785 // Make a measurement...
1786 wxScreenDC dc;
1787
1788 wxFont *f = FontMgr::Get().FindOrCreateFont(
1789 12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, FALSE,
1790 wxString(_T ( "" )), wxFONTENCODING_SYSTEM);
1791 dc.SetFont(*f);
1792
1793 int width, height;
1794 dc.GetTextExtent(_T("H"), &width, &height, NULL, NULL, f);
1795
1796 if (height > 0) m_pt_per_pixel = 12.0 / (double)height;
1797 }
1798 if (m_pt_per_pixel > 0) pt_per_pixel = m_pt_per_pixel;
1799 // #endif
1800
1801 return pt_per_pixel;
1802}
1803
1805#ifdef __ANDROID__
1806 return getAndroidDisplayDimensions();
1807#else
1808 return wxSize(g_monitor_info[g_current_monitor].width,
1809 g_monitor_info[g_current_monitor].height);
1810#endif
1811}
1812
1814 if (g_current_monitor < m_displaySizeMMOverride.size()) {
1815 if (m_displaySizeMMOverride[g_current_monitor] > 0) {
1816 return m_displaySizeMMOverride[g_current_monitor];
1817 }
1818 }
1819
1820 double ret = g_monitor_info[g_current_monitor].width_mm;
1821
1822#ifdef __ANDROID__
1823 ret = GetAndroidDisplaySize();
1824#endif
1825
1826 return ret;
1827}
1828
1829double OCPNPlatform::GetDisplayAreaCM2() {
1830 double size1 = GetDisplaySizeMM();
1831 wxSize sz = getDisplaySize();
1832 double ratio = 1.;
1833 if (sz.x < sz.y)
1834 ratio = (double)sz.x / (double)sz.y; // <1
1835 else
1836 ratio = (double)sz.y / (double)sz.x; // <1
1837
1838 double area = size1 * (size1 * ratio) / 100.;
1839 // qDebug() << "cm2" << size1 << ratio << sz.x << sz.y;
1840 return area;
1841}
1842
1843void OCPNPlatform::SetDisplaySizeMM(size_t monitor, double sizeMM) {
1844 if (monitor < m_displaySizeMMOverride.size()) {
1845 m_displaySizeMMOverride[monitor] = sizeMM;
1846 }
1847}
1848
1849double OCPNPlatform::GetDisplayDPmm() {
1850#ifdef __ANDROID__
1851 return getAndroidDPmm();
1852#else
1853 double r = getDisplaySize().x; // dots
1854 return r / GetDisplaySizeMM();
1855#endif
1856}
1857
1858unsigned int OCPNPlatform::GetSelectRadiusPix() {
1859 return GetDisplayDPmm() *
1860 (g_btouch ? g_selection_radius_touch_mm : g_selection_radius_mm);
1861}
1862
1863bool OCPNPlatform::GetFullscreen() {
1864 bool bret = false;
1865#ifdef __ANDROID__
1866 bret = androidGetFullscreen();
1867#else
1868
1869#endif
1870
1871 return bret;
1872}
1873
1874bool OCPNPlatform::SetFullscreen(bool bFull) {
1875 bool bret = false;
1876#ifdef __ANDROID__
1877 bret = androidSetFullscreen(bFull);
1878#else
1879#endif
1880
1881 return bret;
1882}
1883
1884void OCPNPlatform::PositionAISAlert(wxWindow *alert_window) {
1885#ifndef __ANDROID__
1886 if (alert_window) {
1887 alert_window->SetSize(g_ais_alert_dialog_x, g_ais_alert_dialog_y,
1888 g_ais_alert_dialog_sx, g_ais_alert_dialog_sy);
1889 }
1890#else
1891 if (alert_window) {
1892 alert_window->Centre();
1893 }
1894
1895#endif
1896}
1897
1898wxDirDialog *OCPNPlatform::AdjustDirDialogFont(wxWindow *container,
1899 wxDirDialog *dlg) {
1900 wxDirDialog *ret_dlg = dlg;
1901#ifndef __WXGTK__
1902
1903 dlg->Show();
1904 dlg->SetSize(container->GetSize());
1905 dlg->Centre();
1906
1907 wxSize sds = dlg->GetSize();
1908 wxSize ss = container->GetSize();
1909
1910 if (sds.x > ss.x) {
1911 dlg->Hide();
1912
1913 wxString msg = dlg->GetMessage();
1914 wxString default_dir = dlg->GetPath();
1915
1916 delete dlg;
1917
1918 ret_dlg = new wxDirDialog(NULL, msg, default_dir,
1919 wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST);
1920
1921 wxFont *dialogFont = GetOCPNScaledFont(_("Dialog"));
1922 wxFont *smallFont = new wxFont(*dialogFont);
1923 smallFont->SetPointSize((smallFont->GetPointSize() / 2) +
1924 0.5); // + 0.5 to round instead of truncate
1925 ret_dlg->SetFont(*smallFont);
1926
1927 ret_dlg->SetSize(container->GetSize());
1928 ret_dlg->Centre();
1929 }
1930 ret_dlg->Hide();
1931#endif
1932 return ret_dlg;
1933}
1934
1935wxFileDialog *OCPNPlatform::AdjustFileDialogFont(wxWindow *container,
1936 wxFileDialog *dlg) {
1937 wxFileDialog *ret_dlg = dlg;
1938#ifndef __WXGTK__
1939
1940 dlg->Show();
1941 dlg->SetSize(container->GetSize());
1942 dlg->Centre();
1943
1944 wxSize sds = dlg->GetSize();
1945 wxSize ss = container->GetSize();
1946
1947 if (sds.x > ss.x) {
1948 dlg->Hide();
1949
1950 wxString msg = dlg->GetMessage();
1951 wxString default_dir = dlg->GetDirectory();
1952 wxString default_file = dlg->GetFilename();
1953 wxString wildcard = dlg->GetWildcard();
1954
1955 delete dlg;
1956
1957 ret_dlg = new wxFileDialog(NULL, msg, default_dir, default_file, wildcard,
1958 wxFD_OPEN);
1959
1960 wxFont *dialogFont = GetOCPNScaledFont(_("Dialog"));
1961 wxFont *smallFont = new wxFont(*dialogFont);
1962 smallFont->SetPointSize((smallFont->GetPointSize() / 2) +
1963 0.5); // + 0.5 to round instead of truncate
1964 ret_dlg->SetFont(*smallFont);
1965
1966 ret_dlg->SetSize(container->GetSize());
1967 ret_dlg->Centre();
1968 }
1969 ret_dlg->Hide();
1970#endif
1971 return ret_dlg;
1972}
1973
1974double OCPNPlatform::GetToolbarScaleFactor(int GUIScaleFactor) {
1975 double rv = 1.0;
1976#ifdef __ANDROID__
1977
1978 // We try to arrange matters so that at GUIScaleFactor=0, the tool icons are
1979 // approximately 9 mm in size and that the value may range from 0.5 -> 2.0
1980
1981 // Get the basic size of a tool icon
1982 wxSize style_tool_size(32, 32);
1983
1984 if (g_StyleManager) {
1985 ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
1986 if (style) style_tool_size = style->GetToolSize();
1987 }
1988 double tool_size = style_tool_size.x;
1989
1990 // unless overridden by user, we declare the "best" tool size
1991 // to be roughly the same as the ActionBar height.
1992 // This may be approximated in a device orientation-independent way as:
1993 // 45pixels * DENSITY
1994 double premult = 1.0;
1995 if (g_config_display_size_manual && g_config_display_size_mm[0] > 0) {
1996 double target_size = 9.0; // mm
1997
1998 double basic_tool_size_mm = tool_size / GetDisplayDPmm();
1999 premult = target_size / basic_tool_size_mm;
2000
2001 } else {
2002 premult = wxMax(45 * getAndroidDisplayDensity(), 45) /
2003 tool_size; // make sure not too small
2004 }
2005
2006 // Adjust the scale factor using the global GUI scale parameter, ranging from
2007 // 0.5 -> 2.0
2008 double postmult = exp(GUIScaleFactor * (log(2.0) / 5.0));
2009
2010 // qDebug() << "parmsF" << GUIScaleFactor << premult << postmult;
2011
2012 rv = premult * postmult;
2013 rv = wxMin(rv, getAndroidDisplayDensity() *
2014 3); // Clamp at density * arbitrary limit factor
2015
2016#else
2017 double premult = 1.0;
2018
2019 if (g_bresponsive) {
2020 // Get the basic size of a tool icon
2021 ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
2022 wxSize style_tool_size = style->GetToolSize();
2023 double tool_size = style_tool_size.x;
2024
2025 // unless overridden by user, we declare the "best" tool size
2026 // to be roughly 9 mm square.
2027 double target_size = 9.0; // mm
2028
2029 double basic_tool_size_mm = tool_size / GetDisplayDPmm();
2030 premult = target_size / basic_tool_size_mm;
2031 }
2032
2033 // Adjust the scale factor using the global GUI scale parameter
2034 double postmult = exp(GUIScaleFactor * (0.693 / 5.0)); // exp(2)
2035
2036 rv = premult * postmult;
2037 rv = wxMin(rv, 3.0); // Clamp at 3.0
2038 rv = wxMax(rv, 0.5); // and at 0.5
2039
2040 rv /= g_BasePlatform->GetDisplayDIPMult(gFrame);
2041
2042#endif
2043
2044 return rv;
2045}
2046
2047double OCPNPlatform::GetCompassScaleFactor(int GUIScaleFactor) {
2048 double rv = 1.0;
2049#ifdef __ANDROID__
2050
2051 // We try to arrange matters so that at GUIScaleFactor=0, the compass icon is
2052 // approximately 9 mm in size and that the value may range from 0.5 -> 2.0
2053
2054 if (g_bresponsive) {
2055 // Get the basic size of a tool icon
2056 ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
2057 wxSize style_tool_size = style->GetToolSize();
2058 double compass_size = style_tool_size.x;
2059
2060 // We declare the "nominal best" icon size to be a bit smaller than the
2061 // ActionBar height.
2062 // This may be approximated in a device orientation-independent way as:
2063 // 28pixels * DENSITY
2064 double premult = wxMax(28 * getAndroidDisplayDensity(), 50) / compass_size;
2065
2066 // Adjust the scale factor using the global GUI scale parameter
2067 double postmult = exp(GUIScaleFactor * (log(2.0) / 5.0));
2068 // rv = wxMin(rv, 1.5); // Clamp at 1.5
2069
2070 rv = premult * postmult;
2071 rv = wxMin(rv, getAndroidDisplayDensity() *
2072 3); // Clamp at density * arbitrary limit factor
2073 }
2074
2075#else
2076 double premult = 1.0;
2077
2078 if (g_bresponsive) {
2079 ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
2080 wxSize style_tool_size = style->GetToolSize();
2081 double compass_size = style_tool_size.x;
2082
2083 // We declare the "best" tool size to be roughly 6 mm.
2084 double target_size = 6.0; // mm
2085
2086 double basic_tool_size_mm = compass_size / GetDisplayDPmm();
2087 premult = target_size / basic_tool_size_mm;
2088 }
2089
2090 double postmult = exp(GUIScaleFactor * (0.693 / 5.0)); // exp(2)
2091
2092 rv = premult * postmult;
2093
2094 rv = wxMin(rv, 3.0); // Clamp at 3.0
2095 rv = wxMax(rv, 0.5);
2096
2097#if defined(__WXOSX__) || defined(__WXGTK3__)
2098 // Support scaled HDPI displays.
2099 if (gFrame) rv *= gFrame->GetContentScaleFactor();
2100#endif
2101
2102 rv /= g_BasePlatform->GetDisplayDIPMult(gFrame);
2103
2104#endif
2105
2106 return rv;
2107}
2108
2109float OCPNPlatform::GetChartScaleFactorExp(float scale_linear) {
2110 double factor = 1.0;
2111#ifndef __ANDROID__
2112 factor = exp(scale_linear * (log(3.0) / 5.0));
2113
2114#else
2115 // the idea here is to amplify the scale factor for higher density displays,
2116 // in a measured way....
2117 factor = exp(scale_linear * (0.693 / 5.0));
2118// factor *= getAndroidDisplayDensity();
2119#endif
2120
2121 factor = wxMax(factor, .5);
2122 factor = wxMin(factor, 6.);
2123
2124 return factor;
2125}
2126
2127float OCPNPlatform::GetMarkScaleFactorExp(float scale_linear) {
2128 if (scale_linear <= 0)
2129 return GetChartScaleFactorExp(scale_linear);
2130 else
2131 return GetChartScaleFactorExp(scale_linear - 1);
2132}
2133
2134// float OCPNPlatform::GetDIPScaleFactor() {
2135// float rv = 1.0;
2136// #ifdef __WXMSW__
2137// if (gFrame)
2138// rv = (double)(gFrame->FromDIP(100))/100.;
2139// #endif
2140//
2141// return rv;
2142// }
2143
2144//--------------------------------------------------------------------------
2145// Internal Bluetooth Support
2146//--------------------------------------------------------------------------
2147
2148bool OCPNPlatform::hasInternalBT(wxString profile) {
2149#ifdef __ANDROID__
2150 bool t = androidDeviceHasBlueTooth();
2151 // qDebug() << "androidDeviceHasBluetooth" << t;
2152 return t;
2153#else
2154
2155 return false;
2156#endif
2157}
2158
2159bool OCPNPlatform::startBluetoothScan() {
2160#ifdef __ANDROID__
2161 return androidStartBluetoothScan();
2162#else
2163
2164 return false;
2165#endif
2166}
2167
2168bool OCPNPlatform::stopBluetoothScan() {
2169#ifdef __ANDROID__
2170 return androidStopBluetoothScan();
2171#else
2172
2173 return false;
2174#endif
2175}
2176
2177wxArrayString OCPNPlatform::getBluetoothScanResults() {
2178 wxArrayString ret_val;
2179#ifdef __ANDROID__
2180 return androidGetBluetoothScanResults();
2181#else
2182
2183 ret_val.Add(_T("line 1"));
2184 ret_val.Add(_T("line 2"));
2185 ret_val.Add(_T("line 3"));
2186 return ret_val;
2187
2188#endif
2189}
2190
2191//--------------------------------------------------------------------------
2192// Per-Platform Utility support
2193//--------------------------------------------------------------------------
2194
2195bool OCPNPlatform::AllowAlertDialog(const wxString &class_name) {
2196#ifdef __ANDROID__
2197 // allow if TopLevelWindow count is <=4, implying normal runtime screen
2198 // layout
2199 int nTLW = 0;
2200 wxWindowList::compatibility_iterator node = wxTopLevelWindows.GetFirst();
2201 while (node) {
2202 wxWindow *win = node->GetData();
2203 if (win->IsShown()) nTLW++;
2204
2205 node = node->GetNext();
2206 }
2207
2208 // qDebug() << "AllowAlertDialog" << g_boptionsactive << g_running << nTLW;
2209 if (g_options)
2210 return (g_running && !g_options->IsShown() && (nTLW <= 4));
2211 else
2212 return (g_running && (nTLW <= 4));
2213
2214#else
2215 return true;
2216#endif
2217}
2218
2219void OCPNPlatform::setChartTypeMaskSel(int mask, wxString &indicator) {
2220#ifdef __ANDROID__
2221 return androidSetChartTypeMaskSel(mask, indicator);
2222#endif
2223}
2224
2225#ifdef __ANDROID__
2226QString g_qtStyleSheet;
2227
2228bool LoadQtStyleSheet(wxString &sheet_file) {
2229 if (wxFileExists(sheet_file)) {
2230 // QApplication qApp = getqApp();
2231 if (qApp) {
2232 QString file(sheet_file.c_str());
2233 QFile File(file);
2234 File.open(QFile::ReadOnly);
2235 g_qtStyleSheet = QLatin1String(File.readAll());
2236
2237 // qApp->setStyleSheet(g_qtStyleSheet);
2238 return true;
2239 } else
2240 return false;
2241 } else
2242 return false;
2243}
2244
2245QString getQtStyleSheet(void) { return g_qtStyleSheet; }
2246
2247#endif
2248
2249bool OCPNPlatform::isPlatformCapable(int flag) {
2250#ifndef __ANDROID__
2251 return true;
2252#else
2253 if (flag == PLATFORM_CAP_PLUGINS) {
2254 long platver;
2255 wxString tsdk(android_plat_spc.msdk);
2256 if (tsdk.ToLong(&platver)) {
2257 if (platver >= 11) return true;
2258 }
2259 } else if (flag == PLATFORM_CAP_FASTPAN) {
2260 long platver;
2261 wxString tsdk(android_plat_spc.msdk);
2262 if (tsdk.ToLong(&platver)) {
2263 if (platver >= 14) return true;
2264 }
2265 }
2266
2267 return false;
2268#endif
2269}
2270
2271void OCPNPlatform::platformLaunchDefaultBrowser(wxString URL) {
2272#ifdef __ANDROID__
2273 androidLaunchBrowser(URL);
2274#else
2275 ::wxLaunchDefaultBrowser(URL);
2276#endif
2277}
2278
2279// ============================================================================
2280// OCPNColourPickerCtrl implementation
2281// ============================================================================
2282
2283BEGIN_EVENT_TABLE(OCPNColourPickerCtrl, wxButton)
2284#ifdef __WXMSW__
2285EVT_PAINT(OCPNColourPickerCtrl::OnPaint)
2286#endif
2287END_EVENT_TABLE()
2288
2289// ----------------------------------------------------------------------------
2290// OCPNColourPickerCtrl
2291// ----------------------------------------------------------------------------
2292
2293OCPNColourPickerCtrl::OCPNColourPickerCtrl(wxWindow *parent, wxWindowID id,
2294 const wxColour &initial,
2295 const wxPoint &pos,
2296 const wxSize &size, long style,
2297 const wxValidator &validator,
2298 const wxString &name) {
2299 Create(parent, id, initial, pos, size, style, validator, name);
2300}
2301
2302bool OCPNColourPickerCtrl::Create(wxWindow *parent, wxWindowID id,
2303 const wxColour &col, const wxPoint &pos,
2304 const wxSize &size, long style,
2305 const wxValidator &validator,
2306 const wxString &name) {
2307 m_bitmap = wxBitmap(60, 13);
2308
2309 // create this button
2310 if (!wxBitmapButton::Create(parent, id, m_bitmap, pos, size,
2311 style | wxBU_AUTODRAW, validator, name)) {
2312 wxFAIL_MSG(wxT("OCPNColourPickerCtrl creation failed"));
2313 return false;
2314 }
2315
2316 // and handle user clicks on it
2317 Connect(GetId(), wxEVT_BUTTON,
2318 wxCommandEventHandler(OCPNColourPickerCtrl::OnButtonClick), NULL,
2319 this);
2320
2321 m_colour = col;
2322 UpdateColour();
2323 InitColourData();
2324
2325 return true;
2326}
2327
2328void OCPNColourPickerCtrl::InitColourData() {
2329#if 0
2330 ms_data.SetChooseFull(true);
2331 unsigned char grey = 0;
2332 for (int i = 0; i < 16; i++, grey += 16)
2333 {
2334 // fill with grey tones the custom colors palette
2335 wxColour colour(grey, grey, grey);
2336 ms_data.SetCustomColour(i, colour);
2337 }
2338#endif
2339}
2340
2341void OCPNColourPickerCtrl::OnButtonClick(wxCommandEvent &WXUNUSED(ev)) {
2342#ifdef __ANDROID__
2343 unsigned int cco = 0;
2344 cco |= 0xff;
2345 cco = cco << 8;
2346 cco |= m_colour.Red();
2347 cco = cco << 8;
2348 cco |= m_colour.Green();
2349 cco = cco << 8;
2350 cco |= m_colour.Blue();
2351 unsigned int cc = androidColorPicker(cco);
2352
2353 wxColour cnew;
2354 unsigned char blue = (unsigned char)cc % 256;
2355 unsigned char green = (unsigned char)(cc >> 8) % 256;
2356 ;
2357 unsigned char red = (unsigned char)(cc >> 16) % 256;
2358 cnew.Set(red, green, blue);
2359
2360 SetColour(cnew);
2361
2362#else
2363 // update the wxColouData to be shown in the dialog
2364 ms_data.SetColour(m_colour);
2365
2366 // create the colour dialog and display it
2367 wxColourDialog dlg(this, &ms_data);
2368 if (dlg.ShowModal() == wxID_OK) {
2369 ms_data = dlg.GetColourData();
2370 SetColour(ms_data.GetColour());
2371 }
2372#endif
2373}
2374
2375void OCPNColourPickerCtrl::UpdateColour() {
2376#ifndef __ANDROID__
2377 SetBitmapLabel(wxBitmap());
2378#endif
2379
2380 wxMemoryDC dc(m_bitmap);
2381 dc.SetPen(*wxTRANSPARENT_PEN);
2382 dc.SetBrush(wxBrush(m_colour));
2383 dc.DrawRectangle(0, 0, m_bitmap.GetWidth(), m_bitmap.GetHeight());
2384
2385 dc.SelectObject(wxNullBitmap);
2386 SetBitmapLabel(m_bitmap);
2387}
2388
2389void OCPNColourPickerCtrl::SetColour(wxColour &c) {
2390 m_colour = c;
2391 m_bitmap = wxBitmap(GetSize().x - 20, GetSize().y - 20);
2392 UpdateColour();
2393}
2394
2395wxColour OCPNColourPickerCtrl::GetColour(void) { return m_colour; }
2396
2397wxSize OCPNColourPickerCtrl::DoGetBestSize() const {
2398 wxSize sz(wxBitmapButton::DoGetBestSize());
2399#ifdef __WXMAC__
2400 sz.y += 6;
2401#else
2402 sz.y += 2;
2403#endif
2404 sz.x += 30;
2405 if (HasFlag(wxCLRP_SHOW_LABEL)) return sz;
2406
2407 // if we have no label, then make this button a square
2408 // (like e.g. native GTK version of this control) ???
2409 // sz.SetWidth(sz.GetHeight());
2410 return sz;
2411}
2412
2413void OCPNColourPickerCtrl::OnPaint(wxPaintEvent &event) {
2414 wxPaintDC dc(this);
2415
2416 int offset_x = (GetSize().x - m_bitmap.GetWidth()) / 2;
2417 int offset_y = (GetSize().y - m_bitmap.GetHeight()) / 2;
2418
2419 dc.SetPen(*wxTRANSPARENT_PEN);
2420 dc.SetBrush(wxBrush(m_colour));
2421 dc.DrawRectangle(offset_x, offset_y, m_bitmap.GetWidth(),
2422 m_bitmap.GetHeight());
2423
2424 event.Skip();
2425}
bool g_bresponsive
Flag to control adaptive UI scaling.
Definition ocpn_app.cpp:666
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:52
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:467
void LoadFontNative(wxString *pConfigString, wxString *pNativeDesc)
Loads font settings from a string descriptor.
Definition FontMgr.cpp:409
wxFont * GetFont(const wxString &TextElement, int requested_font_size=0)
Gets a font object for a UI element.
Definition FontMgr.cpp:203
Main application frame.
Definition ocpn_frame.h:136
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.
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.