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