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