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