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