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