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 g_bShowMenuBar = false;
1206 // Enable some default PlugIns, and their default options
1207 if (pConfig) {
1208 pConfig->SetPath("/PlugIns/chartdldr_pi.dll");
1209 pConfig->Write("bEnabled", true);
1210
1211 pConfig->SetPath("/PlugIns/wmm_pi.dll");
1212 pConfig->Write("bEnabled", true);
1213
1214 pConfig->SetPath("/Settings/WMM");
1215 pConfig->Write("ShowIcon", true);
1216 pConfig->Write("ShowLiveIcon", true);
1217 }
1218#endif
1219
1220#ifdef __WXOSX__
1221 // Enable some default PlugIns, and their default options
1222 if (pConfig) {
1223 pConfig->SetPath("/PlugIns/libchartdldr_pi.dylib");
1224 pConfig->Write("bEnabled", true);
1225
1226 pConfig->SetPath("/PlugIns/libwmm_pi.dylib");
1227 pConfig->Write("bEnabled", true);
1228
1229 pConfig->SetPath("/Settings/WMM");
1230 pConfig->Write("ShowIcon", true);
1231 pConfig->Write("ShowLiveIcon", true);
1232 }
1233#endif
1234
1235#ifdef __linux__
1236 // Enable some default PlugIns, and their default options
1237 if (pConfig) {
1238 pConfig->SetPath("/PlugIns/libchartdldr_pi.so");
1239 pConfig->Write("bEnabled", true);
1240
1241 pConfig->SetPath("/PlugIns/libwmm_pi.so");
1242 pConfig->Write("bEnabled", true);
1243
1244 pConfig->SetPath("/Settings/WMM");
1245 pConfig->Write("ShowIcon", true);
1246 pConfig->Write("ShowLiveIcon", true);
1247 }
1248#endif
1249
1250#ifdef __ANDROID__
1251
1252#ifdef ocpnUSE_GL
1253 g_bopengl = true;
1254 g_GLOptions.m_bTextureCompression = 1;
1255 g_GLOptions.m_bTextureCompressionCaching = 1;
1256#endif
1257
1258 qDebug() << "SetDefaultOptions";
1259
1260 g_btouch = true;
1261 g_bresponsive = true;
1262 g_default_font_size = 18; // This is pretty close to TextAppearance.Medium
1263 g_bUIexpert = true;
1264
1265 g_bShowStatusBar = true;
1266 g_cm93_zoom_factor = 0;
1267 g_oz_vector_scale = false;
1268 g_fog_overzoom = false;
1269
1270 g_bRollover = true;
1271 g_bShowMuiZoomButtons = true;
1272
1273 g_GUIScaleFactor = 0; // nominal
1274 g_ChartNotRenderScaleFactor = 2.0;
1275
1276 // Suppress most tools, especially those that appear in the Basic menus.
1277 // Of course, they may be re-enabled by experts...
1278 g_toolbarConfig = "X.....XX.......XX.XXXXXXXXXXX";
1279 g_bPermanentMOBIcon = false;
1280
1281 wxString sGPS = "2;3;;0;0;;0;1;0;0;;0;;1;0;0;0;0"; // 17 parms
1282 ConnectionParams *new_params = new ConnectionParams(sGPS);
1283
1284 new_params->bEnabled = true;
1285 TheConnectionParams().push_back(new_params);
1286
1287 g_default_font_facename = "Roboto";
1288
1289 // Enable some default PlugIns, and their default options
1290
1291 if (pConfig) {
1292 pConfig->SetPath("/PlugIns/libchartdldr_pi.so");
1293 pConfig->Write("bEnabled", true);
1294
1295 pConfig->SetPath("/PlugIns/libwmm_pi.so");
1296 pConfig->Write("bEnabled", true);
1297
1298 pConfig->SetPath("/Settings/WMM");
1299 pConfig->Write("ShowIcon", true);
1300 pConfig->Write("ShowLiveIcon", true);
1301
1302 pConfig->SetPath("/PlugIns/libgrib_pi.so");
1303 pConfig->Write("bEnabled", true);
1304
1305 pConfig->SetPath("/PlugIns/libdashboard_pi.so");
1306 pConfig->Write("bEnabled", true);
1307
1308 pConfig->SetPath("/PlugIns/GRIB");
1309 pConfig->Write("GRIBCtrlBarPosX", 100);
1310 pConfig->Write("GRIBCtrlBarPosY", 0);
1311
1312 pConfig->SetPath("/Settings/GRIB");
1313 pConfig->Write("CursorDataShown", 0);
1314
1315 // This is ugly hack
1316 // TODO
1317 pConfig->SetPath("/PlugIns/liboesenc_pi.so");
1318 pConfig->Write("bEnabled", true);
1319
1320 pConfig->SetPath("/Settings/QTFonts");
1321
1322 // Status Bar
1323 wxString str = "en_US-b25a3899";
1324 wxString pval = "StatusBar:Roboto,26,-1,5,75,0,0,0,0,0:rgb(0, 0, 0)";
1325 pConfig->Write(str, pval);
1326 FontMgr::Get().LoadFontNative(&str, &pval);
1327
1328 // Dialog
1329 str = "en_US-9c3b3a0d";
1330 pval = "DialogStatusBar:Roboto,18,-1,5,50,0,0,0,0,0:rgb(0, 0, 0)";
1331 pConfig->Write(str, pval);
1332 FontMgr::Get().LoadFontNative(&str, &pval);
1333
1334 // Set track default color to magenta
1335 pConfig->SetPath("/Settings/Others");
1336 pConfig->Write("TrackLineColour", "#C545C3");
1337 g_colourTrackLineColour.Set(197, 69, 195);
1338
1339 qDebug() << "SetDefaultOptions.Config";
1340 }
1341
1342#endif
1343}
1344
1345// Setup global options on upgrade detected
1346// The global config object (pConfig) has already been loaded, so updates
1347// here override values set by config.
1348// Direct updates to config for next boot are also supported.
1349
1350void OCPNPlatform::SetUpgradeOptions(wxString vNew, wxString vOld) {
1351#ifdef __ANDROID__
1352
1353 qDebug() << "Upgrade check"
1354 << "from: " << vOld.mb_str() << " to: " << vNew.mb_str();
1355
1356 if (androidGetVersionCode() > g_AndroidVersionCode) { // upgrade
1357 qDebug() << "Upgrade detected"
1358 << "from VC: " << g_AndroidVersionCode
1359 << " to VC: " << androidGetVersionCode();
1360
1361 // Set some S52/S57 options
1362 if (pConfig) {
1363 pConfig->SetPath("/Settings/GlobalState");
1364 pConfig->Write("bShowS57Text", true);
1365 }
1366
1367 g_ChartNotRenderScaleFactor = 2.0;
1368 g_n_ownship_min_mm = 8;
1369 g_toolbarConfig = "X.....XX.......XX.XXXXXXXXXXX";
1370
1371 // Experience indicates a slightly larger default font size is better
1372 pConfig->DeleteGroup("/Settings/QTFonts");
1373 g_default_font_size = 20;
1374 g_default_font_facename = "Roboto";
1375
1376 FontMgr::Get().Shutdown(); // Restart the font manager
1377
1378 // Reshow the zoom buttons
1379 g_bShowMuiZoomButtons = true;
1380
1381 // Clear the default chart storage location
1382 // Will get set to e.g. "/storage/emulated/0" later
1383 pInit_Chart_Dir->Clear();
1384
1385 // A few popular requests,
1386 // which will take effect on next App startup.
1387 pConfig->SetPath("/Settings/WMM");
1388 pConfig->Write("ShowIcon", true);
1389 pConfig->Write("ShowLiveIcon", true);
1390
1391 pConfig->SetPath("/Canvas/CanvasConfig1");
1392 pConfig->Write("canvasENCShowVisibleSectorLights", 0);
1393
1394 // Manage plugins
1395 // Clear the cache, allowing deprecation of obsolete plugins
1397
1398 // Remove any problematic plugins, so preparing for upgraded versions
1399 // Todo: Scrub this list from time to time
1400
1401 // VersionCode 123 (August, 2025)
1402 AndroidRemoveSystemFile(
1403 "/data/user/0/org.opencpn.opencpn/manPlug/libchartscale_pi.so");
1404 }
1405
1406 // Set track default color to magenta
1407 g_colourTrackLineColour.Set(197, 69, 195);
1408#endif
1409
1410 // Check for upgrade....
1411 if (!vOld.IsSameAs(vNew)) { // upgrade
1412
1413 // Verify some default directories, create if necessary
1414
1415 // UserIcons
1416 wxString UserIconPath = GetPrivateDataDir();
1417 wxChar sep = wxFileName::GetPathSeparator();
1418 if (UserIconPath.Last() != sep) UserIconPath.Append(sep);
1419 UserIconPath.Append("UserIcons");
1420
1421 if (!::wxDirExists(UserIconPath)) {
1422 ::wxMkdir(UserIconPath);
1423 }
1424
1425 // layers
1426 wxString LayersPath = GetPrivateDataDir();
1427 if (LayersPath.Last() != sep) LayersPath.Append(sep);
1428 LayersPath.Append("layers");
1429
1430 if (!::wxDirExists(LayersPath)) {
1431 ::wxMkdir(LayersPath);
1432 }
1433
1434 // Force a generally useable sound command, overriding any previous user's
1435 // selection
1436 // that may not be available on new build.
1437 g_CmdSoundString = wxString(OCPN_SOUND_CMD);
1438 pConfig->SetPath("/Settings");
1439 pConfig->Write("CmdSoundString", g_CmdSoundString);
1440
1441 // Force AIS specific sound effects ON, leaving the master control
1442 // (g_bAIS_CPA_Alert_Audio) as configured
1443 g_bAIS_GCPA_Alert_Audio = true;
1444 g_bAIS_SART_Alert_Audio = true;
1445 g_bAIS_DSC_Alert_Audio = true;
1446
1447 // Force a recalculation of default main toolbar location
1448 g_maintoolbar_x = -1;
1449
1450 // Check the tide/current databases for readability,
1451 // remove any not readable
1452 std::vector<std::string> TCDS_temp;
1453 for (unsigned int i = 0; i < TideCurrentDataSet.size(); i++)
1454 TCDS_temp.push_back(TideCurrentDataSet[i]);
1455
1456 TideCurrentDataSet.clear();
1457 for (unsigned int i = 0; i < TCDS_temp.size(); i++) {
1458 wxString tide = TCDS_temp[i];
1459 wxFileName ft(tide);
1460 if (ft.FileExists()) TideCurrentDataSet.push_back(TCDS_temp[i]);
1461 }
1462
1463 // Force default (baked-in) values for CompatOS
1464 // Thus providing for the case when some core dependent elements
1465 // are updated. (e.g. flatpak SDK 22.08->24.08->???)
1466 g_compatOS = "";
1467 g_compatOsVersion = "";
1468 pConfig->SetPath("/Settings");
1469 pConfig->Write("CompatOS", g_compatOS);
1470 pConfig->Write("CompatOsVersion", g_compatOsVersion);
1471 }
1472}
1473
1474int OCPNPlatform::platformApplyPrivateSettingsString(wxString settings,
1475 ArrayOfCDI *pDirArray) {
1476 int ret_val = 0;
1477#ifdef __ANDROID__
1478 ret_val = androidApplySettingsString(settings, pDirArray);
1479#endif
1480
1481 return ret_val;
1482}
1483
1484void OCPNPlatform::applyExpertMode(bool mode) {
1485#ifdef __ANDROID__
1486 // g_bexpert = mode; // toolbar only shows plugin icons if expert mode is
1487 // false
1488 g_bBasicMenus = !mode; // simplified context menus in basic mode
1489#endif
1490}
1491
1492//--------------------------------------------------------------------------
1493// Per-Platform file/directory support
1494//--------------------------------------------------------------------------
1495
1496static wxString ExpandPaths(wxString paths, OCPNPlatform *platform);
1497
1498int OCPNPlatform::DoFileSelectorDialog(wxWindow *parent, wxString *file_spec,
1499 wxString Title, wxString initDir,
1500 wxString suggestedName,
1501 wxString wildcard) {
1502 wxString file;
1503 int result = wxID_CANCEL;
1504
1505#ifdef __ANDROID__
1506 // Verify that initDir is traversable, fix it if not...
1507 wxString idir = initDir;
1508 if (initDir.StartsWith(
1509 "/data/data")) // not good, provokes a crash usually...
1510 idir = GetWritableDocumentsDir();
1511
1512 result = androidFileChooser(&file, idir, Title, suggestedName, wildcard);
1513 if (file_spec) *file_spec = file;
1514#else
1515 long flag = wxFD_DEFAULT_STYLE;
1516 if (suggestedName.Length()) { // new file
1517 flag = wxFD_SAVE;
1518 }
1519
1520 wxString mask = wildcard;
1521 if (wxNOT_FOUND != mask.Find("gpx")) mask.Prepend("GPX files (*.gpx)|");
1522
1523 wxFileDialog *psaveDialog =
1524 new wxFileDialog(parent, Title, initDir, suggestedName, mask, flag);
1525
1526 // Try to reduce the dialog size, and scale fonts down, if necessary.
1527 // if(g_bresponsive && parent)
1528 // psaveDialog = g_Platform->AdjustFileDialogFont(parent,
1529 // psaveDialog);
1530
1531#ifdef __WXOSX__
1532 if (parent) parent->HideWithEffect(wxSHOW_EFFECT_BLEND);
1533#endif
1534
1535 result = psaveDialog->ShowModal();
1536
1537#ifdef __WXOSX__
1538 if (parent) parent->ShowWithEffect(wxSHOW_EFFECT_BLEND);
1539#endif
1540
1541 if (file_spec) *file_spec = psaveDialog->GetPath();
1542 delete psaveDialog;
1543
1544#endif
1545
1546 return result;
1547}
1548
1549int OCPNPlatform::DoDirSelectorDialog(wxWindow *parent, wxString *file_spec,
1550 wxString Title, wxString initDir,
1551 bool b_addFiles) {
1552 wxString dir;
1553 int result = wxID_CANCEL;
1554
1555#ifdef __ANDROID__
1556 // Verify that initDir is traversable, fix it if not...
1557 wxString idir = initDir;
1558 if (initDir.StartsWith(
1559 "/data/data")) // not good, provokes a crash usually...
1560 idir = GetWritableDocumentsDir();
1561
1562 result = androidFileChooser(&dir, idir, Title, "", "", true,
1563 b_addFiles); // Directories only, maybe add dirs
1564 if (file_spec) *file_spec = dir;
1565#else
1566 wxDirDialog *dirSelector = new wxDirDialog(
1567 parent, Title, initDir, wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST);
1568
1569 wxFont *qFont = GetOCPNScaledFont(_("Dialog"));
1570 dirSelector->SetFont(*qFont);
1571
1572 // Try to reduce the dialog size, and scale fonts down, if necessary.
1573 // if(g_bresponsive && parent)
1574 // dirSelector = AdjustDirDialogFont(parent, dirSelector);
1575
1576#ifdef __WXOSX__
1577 if (parent) parent->HideWithEffect(wxSHOW_EFFECT_BLEND);
1578#endif
1579
1580 result = dirSelector->ShowModal();
1581
1582#ifdef __WXOSX__
1583 if (parent) parent->ShowWithEffect(wxSHOW_EFFECT_BLEND);
1584#endif
1585
1586 if (result == wxID_CANCEL) {
1587 } else {
1588 if (file_spec) {
1589 *file_spec = dirSelector->GetPath();
1590 }
1591 }
1592
1593 delete dirSelector;
1594#endif
1595
1596 return result;
1597}
1598
1599MyConfig *OCPNPlatform::GetConfigObject() {
1600 MyConfig *result = NULL;
1601
1602 result = new MyConfig(GetConfigFileName());
1603
1604 return result;
1605}
1606
1607//--------------------------------------------------------------------------
1608// Internal GPS Support
1609//--------------------------------------------------------------------------
1610
1611bool OCPNPlatform::hasInternalGPS(wxString profile) {
1612#ifdef __ANDROID__
1613 bool t = androidDeviceHasGPS();
1614 // qDebug() << "androidDeviceHasGPS" << t;
1615 return t;
1616#else
1617
1618 return false;
1619
1620#endif
1621}
1622
1623//--------------------------------------------------------------------------
1624// Platform Display Support
1625//--------------------------------------------------------------------------
1626
1627void OCPNPlatform::ShowBusySpinner() { AbstractPlatform::ShowBusySpinner(); }
1628
1629void OCPNPlatform::HideBusySpinner() { AbstractPlatform::HideBusySpinner(); }
1630
1631double OCPNPlatform::GetDisplayDensityFactor() {
1632#ifdef __ANDROID__
1633 return getAndroidDisplayDensity();
1634#else
1635 return 1.0;
1636#endif
1637}
1638
1639long OCPNPlatform::GetDefaultToolbarOrientation() {
1640#ifndef __ANDROID__
1641 return wxTB_VERTICAL;
1642#else
1643 return wxTB_VERTICAL;
1644#endif
1645}
1646
1647int OCPNPlatform::GetStatusBarFieldCount() {
1648#ifdef __ANDROID__
1649 int count = 1;
1650
1651 // Make a horizontal measurement...
1652 wxScreenDC dc;
1653 wxFont *templateFont = FontMgr::Get().GetFont(_("StatusBar"), 0);
1654 dc.SetFont(*templateFont);
1655
1656 int width;
1657 dc.GetTextExtent("WWWWWW", &width, NULL, NULL, NULL, templateFont);
1658 double font_size_pix = (double)width / 6.0;
1659
1660 wxSize dispSize = getDisplaySize();
1661
1662 double nChars = dispSize.x / font_size_pix;
1663
1664 if (nChars < 40)
1665 count = 1;
1666 else
1667 count = 2;
1668
1669 return count;
1670
1671#else
1672 return kStatFieldCount; // default
1673#endif
1674}
1675
1676double OCPNPlatform::getFontPointsperPixel() {
1677 double pt_per_pixel = 1.0;
1678
1679 // #ifdef __ANDROID__
1680 // On Android, this calculation depends on the density bucket in use.
1681 // Also uses some magic numbers...
1682 // For reference, see http://pixplicity.com/dp-px-converter/
1683 // pt_per_pixel = 14.0 / (31.11 * getAndroidDisplayDensity()) ;
1684
1685 // #else
1686
1687 if (m_pt_per_pixel == 0) {
1688 // Make a measurement...
1689 wxScreenDC dc;
1690
1691 wxFont *f = FontMgr::Get().FindOrCreateFont(
1692 12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, FALSE,
1693 wxString(""), wxFONTENCODING_SYSTEM);
1694 dc.SetFont(*f);
1695
1696 int width, height;
1697 dc.GetTextExtent("H", &width, &height, NULL, NULL, f);
1698
1699 if (height > 0) m_pt_per_pixel = 12.0 / (double)height;
1700 }
1701 if (m_pt_per_pixel > 0) pt_per_pixel = m_pt_per_pixel;
1702 // #endif
1703
1704 return pt_per_pixel;
1705}
1706
1708#ifdef __ANDROID__
1709 return getAndroidDisplayDimensions();
1710#else
1711 return wxSize(g_monitor_info[g_current_monitor].width,
1713#endif
1714}
1715
1717 if (g_current_monitor < m_displaySizeMMOverride.size()) {
1718 if (m_displaySizeMMOverride[g_current_monitor] > 0) {
1719 return m_displaySizeMMOverride[g_current_monitor];
1720 }
1721 }
1722
1723 double ret = g_monitor_info[g_current_monitor].width_mm;
1724
1725#ifdef __ANDROID__
1726 ret = GetAndroidDisplaySize();
1727#endif
1728
1729 return ret;
1730}
1731
1732double OCPNPlatform::GetDisplayAreaCM2() {
1733 double size1 = GetDisplaySizeMM();
1734 wxSize sz = getDisplaySize();
1735 double ratio = 1.;
1736 if (sz.x < sz.y)
1737 ratio = (double)sz.x / (double)sz.y; // <1
1738 else
1739 ratio = (double)sz.y / (double)sz.x; // <1
1740
1741 double area = size1 * (size1 * ratio) / 100.;
1742 // qDebug() << "cm2" << size1 << ratio << sz.x << sz.y;
1743 return area;
1744}
1745
1746void OCPNPlatform::SetDisplaySizeMM(size_t monitor, double sizeMM) {
1747 if (monitor < m_displaySizeMMOverride.size()) {
1748 m_displaySizeMMOverride[monitor] = sizeMM;
1749 }
1750}
1751
1752double OCPNPlatform::GetDisplayDPmm() {
1753#ifdef __ANDROID__
1754 return getAndroidDPmm();
1755#else
1756 double r = getDisplaySize().x; // dots
1757 return r / GetDisplaySizeMM();
1758#endif
1759}
1760
1761unsigned int OCPNPlatform::GetSelectRadiusPix() {
1762 return GetDisplayDPmm() *
1763 (g_btouch ? g_selection_radius_touch_mm : g_selection_radius_mm);
1764}
1765
1766bool OCPNPlatform::GetFullscreen() {
1767 bool bret = false;
1768#ifdef __ANDROID__
1769 bret = androidGetFullscreen();
1770#else
1771
1772#endif
1773
1774 return bret;
1775}
1776
1777bool OCPNPlatform::SetFullscreen(bool bFull) {
1778 bool bret = false;
1779#ifdef __ANDROID__
1780 bret = androidSetFullscreen(bFull);
1781#else
1782#endif
1783
1784 return bret;
1785}
1786
1787void OCPNPlatform::PositionAISAlert(wxWindow *alert_window) {
1788#ifndef __ANDROID__
1789 if (alert_window) {
1790 alert_window->SetSize(g_ais_alert_dialog_x, g_ais_alert_dialog_y,
1791 g_ais_alert_dialog_sx, g_ais_alert_dialog_sy);
1792 }
1793#else
1794 if (alert_window) {
1795 alert_window->Centre();
1796 }
1797
1798#endif
1799}
1800
1801wxDirDialog *OCPNPlatform::AdjustDirDialogFont(wxWindow *container,
1802 wxDirDialog *dlg) {
1803 wxDirDialog *ret_dlg = dlg;
1804#ifndef __WXGTK__
1805
1806 dlg->Show();
1807 dlg->SetSize(container->GetSize());
1808 dlg->Centre();
1809
1810 wxSize sds = dlg->GetSize();
1811 wxSize ss = container->GetSize();
1812
1813 if (sds.x > ss.x) {
1814 dlg->Hide();
1815
1816 wxString msg = dlg->GetMessage();
1817 wxString default_dir = dlg->GetPath();
1818
1819 delete dlg;
1820
1821 ret_dlg = new wxDirDialog(NULL, msg, default_dir,
1822 wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST);
1823
1824 wxFont *dialogFont = GetOCPNScaledFont(_("Dialog"));
1825 wxFont *smallFont = new wxFont(*dialogFont);
1826 smallFont->SetPointSize((smallFont->GetPointSize() / 2) +
1827 0.5); // + 0.5 to round instead of truncate
1828 ret_dlg->SetFont(*smallFont);
1829
1830 ret_dlg->SetSize(container->GetSize());
1831 ret_dlg->Centre();
1832 }
1833 ret_dlg->Hide();
1834#endif
1835 return ret_dlg;
1836}
1837
1838wxFileDialog *OCPNPlatform::AdjustFileDialogFont(wxWindow *container,
1839 wxFileDialog *dlg) {
1840 wxFileDialog *ret_dlg = dlg;
1841#ifndef __WXGTK__
1842
1843 dlg->Show();
1844 dlg->SetSize(container->GetSize());
1845 dlg->Centre();
1846
1847 wxSize sds = dlg->GetSize();
1848 wxSize ss = container->GetSize();
1849
1850 if (sds.x > ss.x) {
1851 dlg->Hide();
1852
1853 wxString msg = dlg->GetMessage();
1854 wxString default_dir = dlg->GetDirectory();
1855 wxString default_file = dlg->GetFilename();
1856 wxString wildcard = dlg->GetWildcard();
1857
1858 delete dlg;
1859
1860 ret_dlg = new wxFileDialog(NULL, msg, default_dir, default_file, wildcard,
1861 wxFD_OPEN);
1862
1863 wxFont *dialogFont = GetOCPNScaledFont(_("Dialog"));
1864 wxFont *smallFont = new wxFont(*dialogFont);
1865 smallFont->SetPointSize((smallFont->GetPointSize() / 2) +
1866 0.5); // + 0.5 to round instead of truncate
1867 ret_dlg->SetFont(*smallFont);
1868
1869 ret_dlg->SetSize(container->GetSize());
1870 ret_dlg->Centre();
1871 }
1872 ret_dlg->Hide();
1873#endif
1874 return ret_dlg;
1875}
1876
1877double OCPNPlatform::GetToolbarScaleFactor(int GUIScaleFactor) {
1878 double rv = 1.0;
1879#ifdef __ANDROID__
1880
1881 // We try to arrange matters so that at GUIScaleFactor=0, the tool icons are
1882 // approximately 9 mm in size and that the value may range from 0.5 -> 2.0
1883
1884 // Get the basic size of a tool icon
1885 wxSize style_tool_size(32, 32);
1886
1887 if (g_StyleManager) {
1888 ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
1889 if (style) style_tool_size = style->GetToolSize();
1890 }
1891 double tool_size = style_tool_size.x;
1892
1893 // unless overridden by user, we declare the "best" tool size
1894 // to be roughly the same as the ActionBar height.
1895 // This may be approximated in a device orientation-independent way as:
1896 // 45pixels * DENSITY
1897 double premult = 1.0;
1898 if (g_config_display_size_manual && g_config_display_size_mm[0] > 0) {
1899 double target_size = 9.0; // mm
1900
1901 double basic_tool_size_mm = tool_size / GetDisplayDPmm();
1902 premult = target_size / basic_tool_size_mm;
1903
1904 } else {
1905 premult = wxMax(45 * getAndroidDisplayDensity(), 45) /
1906 tool_size; // make sure not too small
1907 }
1908
1909 // Adjust the scale factor using the global GUI scale parameter, ranging from
1910 // 0.5 -> 2.0
1911 double postmult = exp(GUIScaleFactor * (log(2.0) / 5.0));
1912
1913 // qDebug() << "parmsF" << GUIScaleFactor << premult << postmult;
1914
1915 rv = premult * postmult;
1916 rv = wxMin(rv, getAndroidDisplayDensity() *
1917 3); // Clamp at density * arbitrary limit factor
1918
1919#else
1920 double premult = 1.0;
1921
1922 if (g_bresponsive) {
1923 // Get the basic size of a tool icon
1924 ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
1925 wxSize style_tool_size = style->GetToolSize();
1926 double tool_size = style_tool_size.x;
1927
1928 // unless overridden by user, we declare the "best" tool size
1929 // to be roughly 9 mm square.
1930 double target_size = 9.0; // mm
1931
1932 double basic_tool_size_mm = tool_size / GetDisplayDPmm();
1933 premult = target_size / basic_tool_size_mm;
1934 }
1935
1936 // Adjust the scale factor using the global GUI scale parameter
1937 double postmult = exp(GUIScaleFactor * (0.693 / 5.0)); // exp(2)
1938
1939 rv = premult * postmult;
1940 rv = wxMin(rv, 3.0); // Clamp at 3.0
1941 rv = wxMax(rv, 0.5); // and at 0.5
1942
1943 rv /= g_BasePlatform->GetDisplayDIPMult(wxTheApp->GetTopWindow());
1944
1945#endif
1946
1947 return rv;
1948}
1949
1950double OCPNPlatform::GetCompassScaleFactor(int GUIScaleFactor) {
1951 double rv = 1.0;
1952#ifdef __ANDROID__
1953
1954 // We try to arrange matters so that at GUIScaleFactor=0, the compass icon is
1955 // approximately 9 mm in size and that the value may range from 0.5 -> 2.0
1956
1957 if (g_bresponsive) {
1958 // Get the basic size of a tool icon
1959 ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
1960 wxSize style_tool_size = style->GetToolSize();
1961 double compass_size = style_tool_size.x;
1962
1963 // We declare the "nominal best" icon size to be a bit smaller than the
1964 // ActionBar height.
1965 // This may be approximated in a device orientation-independent way as:
1966 // 28pixels * DENSITY
1967 double premult = wxMax(28 * getAndroidDisplayDensity(), 50) / compass_size;
1968
1969 // Adjust the scale factor using the global GUI scale parameter
1970 double postmult = exp(GUIScaleFactor * (log(2.0) / 5.0));
1971 // rv = wxMin(rv, 1.5); // Clamp at 1.5
1972
1973 rv = premult * postmult;
1974 rv = wxMin(rv, getAndroidDisplayDensity() *
1975 3); // Clamp at density * arbitrary limit factor
1976 }
1977
1978#else
1979 double premult = 1.0;
1980
1981 if (g_bresponsive) {
1982 ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
1983 wxSize style_tool_size = style->GetToolSize();
1984 double compass_size = style_tool_size.x;
1985
1986 // We declare the "best" tool size to be roughly 6 mm.
1987 double target_size = 6.0; // mm
1988
1989 double basic_tool_size_mm = compass_size / GetDisplayDPmm();
1990 premult = target_size / basic_tool_size_mm;
1991 }
1992
1993 double postmult = exp(GUIScaleFactor * (0.693 / 5.0)); // exp(2)
1994
1995 rv = premult * postmult;
1996
1997 rv = wxMin(rv, 3.0); // Clamp at 3.0
1998 rv = wxMax(rv, 0.5);
1999
2000#if defined(__WXOSX__) || defined(__WXGTK3__)
2001 // Support scaled HDPI displays.
2002
2003 if (top_frame::Get()) rv *= top_frame::Get()->GetContentScaleFactor();
2004#endif
2005
2006 rv /= g_BasePlatform->GetDisplayDIPMult(wxTheApp->GetTopWindow());
2007
2008#endif
2009
2010 return rv;
2011}
2012
2013float OCPNPlatform::GetChartScaleFactorExp(float scale_linear) {
2014 double factor = 1.0;
2015#ifndef __ANDROID__
2016 factor = exp(scale_linear * (log(3.0) / 5.0));
2017
2018#else
2019 // the idea here is to amplify the scale factor for higher density displays,
2020 // in a measured way....
2021 factor = exp(scale_linear * (0.693 / 5.0));
2022// factor *= getAndroidDisplayDensity();
2023#endif
2024
2025 factor = wxMax(factor, .5);
2026 factor = wxMin(factor, 6.);
2027
2028 return factor;
2029}
2030
2031float OCPNPlatform::GetMarkScaleFactorExp(float scale_linear) {
2032 if (scale_linear <= 0)
2033 return GetChartScaleFactorExp(scale_linear);
2034 else
2035 return GetChartScaleFactorExp(scale_linear - 1);
2036}
2037
2038// float OCPNPlatform::GetDIPScaleFactor() {
2039// float rv = 1.0;
2040// #ifdef __WXMSW__
2041// if (gFrame)
2042// rv = (double)(gFrame->FromDIP(100))/100.;
2043// #endif
2044//
2045// return rv;
2046// }
2047
2048//--------------------------------------------------------------------------
2049// Internal Bluetooth Support
2050//--------------------------------------------------------------------------
2051
2052bool OCPNPlatform::hasInternalBT(wxString profile) {
2053#ifdef __ANDROID__
2054 bool t = androidDeviceHasBlueTooth();
2055 // qDebug() << "androidDeviceHasBluetooth" << t;
2056 return t;
2057#else
2058
2059 return false;
2060#endif
2061}
2062
2063bool OCPNPlatform::startBluetoothScan() {
2064#ifdef __ANDROID__
2065 return androidStartBluetoothScan();
2066#else
2067
2068 return false;
2069#endif
2070}
2071
2072bool OCPNPlatform::stopBluetoothScan() {
2073#ifdef __ANDROID__
2074 return androidStopBluetoothScan();
2075#else
2076
2077 return false;
2078#endif
2079}
2080
2081wxArrayString OCPNPlatform::getBluetoothScanResults() {
2082 wxArrayString ret_val;
2083#ifdef __ANDROID__
2084 return androidGetBluetoothScanResults();
2085#else
2086
2087 ret_val.Add("line 1");
2088 ret_val.Add("line 2");
2089 ret_val.Add("line 3");
2090 return ret_val;
2091
2092#endif
2093}
2094
2095//--------------------------------------------------------------------------
2096// Per-Platform Utility support
2097//--------------------------------------------------------------------------
2098
2099bool OCPNPlatform::AllowAlertDialog(const wxString &class_name) {
2100#ifdef __ANDROID__
2101 // allow if TopLevelWindow count is <=4, implying normal runtime screen
2102 // layout
2103 int nTLW = 0;
2104 wxWindowList::compatibility_iterator node = wxTopLevelWindows.GetFirst();
2105 while (node) {
2106 wxWindow *win = node->GetData();
2107 if (win->IsShown()) nTLW++;
2108
2109 node = node->GetNext();
2110 }
2111
2112 // qDebug() << "AllowAlertDialog" << g_boptionsactive << g_running << nTLW;
2113 if (g_options)
2114 return (g_running && !g_options->IsShown() && (nTLW <= 4));
2115 else
2116 return (g_running && (nTLW <= 4));
2117
2118#else
2119 return true;
2120#endif
2121}
2122
2123void OCPNPlatform::setChartTypeMaskSel(int mask, wxString &indicator) {
2124#ifdef __ANDROID__
2125 return androidSetChartTypeMaskSel(mask, indicator);
2126#endif
2127}
2128
2129#ifdef __ANDROID__
2130QString g_qtStyleSheet;
2131
2132bool LoadQtStyleSheet(wxString &sheet_file) {
2133 if (wxFileExists(sheet_file)) {
2134 // QApplication qApp = getqApp();
2135 if (qApp) {
2136 QString file(sheet_file.c_str());
2137 QFile File(file);
2138 File.open(QFile::ReadOnly);
2139 g_qtStyleSheet = QLatin1String(File.readAll());
2140
2141 // qApp->setStyleSheet(g_qtStyleSheet);
2142 return true;
2143 } else
2144 return false;
2145 } else
2146 return false;
2147}
2148
2149QString getQtStyleSheet() { return g_qtStyleSheet; }
2150
2151#endif
2152
2153bool OCPNPlatform::isPlatformCapable(int flag) {
2154#ifndef __ANDROID__
2155 return true;
2156#else
2157 if (flag == PLATFORM_CAP_PLUGINS) {
2158 long platver;
2159 wxString tsdk(android_plat_spc.msdk);
2160 if (tsdk.ToLong(&platver)) {
2161 if (platver >= 11) return true;
2162 }
2163 } else if (flag == PLATFORM_CAP_FASTPAN) {
2164 long platver;
2165 wxString tsdk(android_plat_spc.msdk);
2166 if (tsdk.ToLong(&platver)) {
2167 if (platver >= 14) return true;
2168 }
2169 }
2170
2171 return false;
2172#endif
2173}
2174
2175void OCPNPlatform::platformLaunchDefaultBrowser(wxString URL) {
2176#ifdef __ANDROID__
2177 androidLaunchBrowser(URL);
2178#else
2179 ::wxLaunchDefaultBrowser(URL);
2180#endif
2181}
2182
2183// ============================================================================
2184// OCPNColourPickerCtrl implementation
2185// ============================================================================
2186
2187BEGIN_EVENT_TABLE(OCPNColourPickerCtrl, wxButton)
2188#ifdef __WXMSW__
2189EVT_PAINT(OCPNColourPickerCtrl::OnPaint)
2190#endif
2191END_EVENT_TABLE()
2192
2193// ----------------------------------------------------------------------------
2194// OCPNColourPickerCtrl
2195// ----------------------------------------------------------------------------
2196
2197OCPNColourPickerCtrl::OCPNColourPickerCtrl(wxWindow *parent, wxWindowID id,
2198 const wxColour &initial,
2199 const wxPoint &pos,
2200 const wxSize &size, long style,
2201 const wxValidator &validator,
2202 const wxString &name) {
2203 Create(parent, id, initial, pos, size, style, validator, name);
2204}
2205
2206bool OCPNColourPickerCtrl::Create(wxWindow *parent, wxWindowID id,
2207 const wxColour &col, const wxPoint &pos,
2208 const wxSize &size, long style,
2209 const wxValidator &validator,
2210 const wxString &name) {
2211 m_bitmap = wxBitmap(60, 13);
2212
2213 // create this button
2214 if (!wxBitmapButton::Create(parent, id, m_bitmap, pos, size,
2215 style | wxBU_AUTODRAW, validator, name)) {
2216 wxFAIL_MSG("OCPNColourPickerCtrl creation failed");
2217 return false;
2218 }
2219
2220 // and handle user clicks on it
2221 Connect(GetId(), wxEVT_BUTTON,
2222 wxCommandEventHandler(OCPNColourPickerCtrl::OnButtonClick), NULL,
2223 this);
2224
2225 m_colour = col;
2226 UpdateColour();
2227 InitColourData();
2228
2229 return true;
2230}
2231
2232void OCPNColourPickerCtrl::InitColourData() {
2233#if 0
2234 ms_data.SetChooseFull(true);
2235 unsigned char grey = 0;
2236 for (int i = 0; i < 16; i++, grey += 16)
2237 {
2238 // fill with grey tones the custom colors palette
2239 wxColour colour(grey, grey, grey);
2240 ms_data.SetCustomColour(i, colour);
2241 }
2242#endif
2243}
2244
2245void OCPNColourPickerCtrl::OnButtonClick(wxCommandEvent &WXUNUSED(ev)) {
2246#ifdef __ANDROID__
2247 unsigned int cco = 0;
2248 cco |= 0xff;
2249 cco = cco << 8;
2250 cco |= m_colour.Red();
2251 cco = cco << 8;
2252 cco |= m_colour.Green();
2253 cco = cco << 8;
2254 cco |= m_colour.Blue();
2255 unsigned int cc = androidColorPicker(cco);
2256
2257 wxColour cnew;
2258 unsigned char blue = (unsigned char)cc % 256;
2259 unsigned char green = (unsigned char)(cc >> 8) % 256;
2260 ;
2261 unsigned char red = (unsigned char)(cc >> 16) % 256;
2262 cnew.Set(red, green, blue);
2263
2264 SetColour(cnew);
2265
2266#else
2267 // update the wxColouData to be shown in the dialog
2268 ms_data.SetColour(m_colour);
2269
2270 // create the colour dialog and display it
2271 wxColourDialog dlg(this, &ms_data);
2272 if (dlg.ShowModal() == wxID_OK) {
2273 ms_data = dlg.GetColourData();
2274 SetColour(ms_data.GetColour());
2275 }
2276#endif
2277}
2278
2279void OCPNColourPickerCtrl::UpdateColour() {
2280#ifndef __ANDROID__
2281 SetBitmapLabel(wxBitmap());
2282#endif
2283
2284 wxMemoryDC dc(m_bitmap);
2285 dc.SetPen(*wxTRANSPARENT_PEN);
2286 dc.SetBrush(wxBrush(m_colour));
2287 dc.DrawRectangle(0, 0, m_bitmap.GetWidth(), m_bitmap.GetHeight());
2288
2289 dc.SelectObject(wxNullBitmap);
2290 SetBitmapLabel(m_bitmap);
2291}
2292
2293void OCPNColourPickerCtrl::SetColour(wxColour &c) {
2294 m_colour = c;
2295 m_bitmap = wxBitmap(GetSize().x - 20, GetSize().y - 20);
2296 UpdateColour();
2297}
2298
2299wxColour OCPNColourPickerCtrl::GetColour() { return m_colour; }
2300
2301wxSize OCPNColourPickerCtrl::DoGetBestSize() const {
2302 wxSize sz(wxBitmapButton::DoGetBestSize());
2303#ifdef __WXMAC__
2304 sz.y += 6;
2305#else
2306 sz.y += 2;
2307#endif
2308 sz.x += 30;
2309 if (HasFlag(wxCLRP_SHOW_LABEL)) return sz;
2310
2311 // if we have no label, then make this button a square
2312 // (like e.g. native GTK version of this control) ???
2313 // sz.SetWidth(sz.GetHeight());
2314 return sz;
2315}
2316
2317void OCPNColourPickerCtrl::OnPaint(wxPaintEvent &event) {
2318 wxPaintDC dc(this);
2319
2320 int offset_x = (GetSize().x - m_bitmap.GetWidth()) / 2;
2321 int offset_y = (GetSize().y - m_bitmap.GetHeight()) / 2;
2322
2323 dc.SetPen(*wxTRANSPARENT_PEN);
2324 dc.SetBrush(wxBrush(m_colour));
2325 dc.DrawRectangle(offset_x, offset_y, m_bitmap.GetWidth(),
2326 m_bitmap.GetHeight());
2327
2328 event.Skip();
2329}
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:183
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.