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)) {
634 gl_util_path =
635 ep.parent_path().parent_path().append("libexec").append(gl_util_exe);
636 }
637 if (!fs::exists(gl_util_path)) {
638 // TODO: What to do if the utility is not found (Which it is not for
639 // developer builds that are not installed)?
640 wxLogMessage("OpenGL test utility not found at %s.", gl_util_path.c_str());
641 return false;
642 }
643
644 std::string gl_json = fs::path(GetPrivateDataDir().ToStdString())
645 .append("gl_caps.json")
646 .string();
647
648 wxString cmd = wxString::Format("\"%s\" opengl-info \"%s\"",
649 gl_util_path.c_str(), gl_json.c_str());
650
651 wxLogMessage("Starting OpenGL test utility: %s", cmd);
652
653 wxArrayString output;
654 if (long res = wxExecute(cmd, output); res != 0) {
655 wxLogMessage("OpenGL test utility failed with exit code %d", res);
656 for (const auto &l : output) {
657 wxLogMessage(l);
658 }
659 return false;
660 }
661
662 wxFileInputStream fis(gl_json);
663 wxJSONReader reader;
664 wxJSONValue root;
665 reader.Parse(fis, &root);
666 if (reader.GetErrorCount() > 0) {
667 wxLogMessage("Failed to parse JSON output from OpenGL test utility.");
668 for (const auto &l : reader.GetErrors()) {
669 wxLogMessage(l);
670 }
671 return false;
672 }
673
674 OCPN_GLCaps *pcaps = (OCPN_GLCaps *)pbuf;
675
676 if (root.HasMember("GL_RENDERER")) {
677 pcaps->Renderer = root["GL_RENDERER"].AsString();
678 } else {
679 wxLogMessage("GL_RENDERER not found.");
680 return false;
681 }
682 if (root.HasMember("GL_VERSION")) {
683 pcaps->Version = root["GL_VERSION"].AsString();
684 } else {
685 wxLogMessage("GL_VERSION not found.");
686 return false;
687 }
688 if (root.HasMember("GL_SHADING_LANGUAGE_VERSION")) {
689 pcaps->GLSL_Version = root["GL_SHADING_LANGUAGE_VERSION"].AsString();
690 } else {
691 wxLogMessage("GL_SHADING_LANGUAGE_VERSION not found.");
692 return false;
693 }
694 if (root.HasMember("GL_USABLE")) {
695 if (!root["GL_USABLE"].AsBool()) {
696 wxLogMessage("OpenGL test utility reports that OpenGL is not usable.");
697 return false;
698 }
699 } else {
700 wxLogMessage("GL_USABLE not found.");
701 return false;
702 }
703 pcaps->dGLSL_Version = 0;
704 pcaps->dGLSL_Version = ::atof(pcaps->GLSL_Version.c_str());
705 if (pcaps->dGLSL_Version < 1.2) {
706 wxString msg;
707 msg.Printf("GLCaps Probe: OpenGL-> GLSL Version reported: ");
708 msg += wxString(pcaps->GLSL_Version.c_str());
709 msg += "\n OpenGL disabled due to insufficient OpenGL capabilities";
710 wxLogMessage(msg);
711 pcaps->bCanDoGLSL = false;
712 return false;
713 }
714 pcaps->bCanDoGLSL = true;
715 if (HasGLExt(root, "GL_ARB_texture_non_power_of_two")) {
716 pcaps->TextureRectangleFormat = GL_TEXTURE_2D;
717 } else if (HasGLExt(root, "GL_OES_texture_npot")) {
718 pcaps->TextureRectangleFormat = GL_TEXTURE_2D;
719 } else if (HasGLExt(root, "GL_ARB_texture_rectangle")) {
720 pcaps->TextureRectangleFormat = GL_TEXTURE_RECTANGLE_ARB;
721 }
722
723 pcaps->bOldIntel = false;
724
725 pcaps->bCanDoFBO = HasGLExt(root, "GL_EXT_framebuffer_object");
726 if (!pcaps->TextureRectangleFormat) {
727 pcaps->bCanDoFBO = false;
728 }
729
730 pcaps->bCanDoVBO = HasGLExt(
731 root, "GL_ARB_vertex_buffer_object"); // TODO: Or the old way where we
732 // enable it without querying the
733 // extension is right?
734 top_frame::Get()->Show();
735 return true;
736#else
737 // The original codepath doing direct probing in the main OpenCPN process, now
738 // only for Android Investigate OpenGL capabilities
739 top_frame::Get()->Show();
740 glTestCanvas *tcanvas = new glTestCanvas(wxTheApp->GetTopWindow());
741 tcanvas->Show();
742 wxYield();
743 wxGLContext *pctx = new wxGLContext(tcanvas);
744 tcanvas->SetCurrent(*pctx);
745
746 OCPN_GLCaps *pcaps = (OCPN_GLCaps *)pbuf;
747
748 char *str = (char *)glGetString(GL_RENDERER);
749 if (str == NULL) { // No GL at all...
750 wxLogMessage("GL_RENDERER not found.");
751 delete tcanvas;
752 delete pctx;
753 return false;
754 }
755 pcaps->Renderer = std::string(str);
756
757 char *stv = (char *)glGetString(GL_VERSION);
758 if (stv == NULL) { // No GL Version...
759 wxLogMessage("GL_VERSION not found");
760 delete tcanvas;
761 delete pctx;
762 return false;
763 }
764 pcaps->Version = std::string(stv);
765
766 char *stsv = (char *)glGetString(GL_SHADING_LANGUAGE_VERSION);
767 if (stsv == NULL) { // No GLSL...
768 wxLogMessage("GL_SHADING_LANGUAGE_VERSION not found");
769 delete tcanvas;
770 delete pctx;
771 return false;
772 }
773 pcaps->GLSL_Version = std::string(stsv);
774
775 pcaps->dGLSL_Version = 0;
776 pcaps->dGLSL_Version = ::atof(pcaps->GLSL_Version.c_str());
777
778 if (pcaps->dGLSL_Version < 1.2) {
779 wxString msg;
780 msg.Printf("GLCaps Probe: OpenGL-> GLSL Version reported: ");
781 msg += wxString(pcaps->GLSL_Version.c_str());
782 msg += "\n OpenGL disabled due to insufficient OpenGL capabilities";
783 wxLogMessage(msg);
784 pcaps->bCanDoGLSL = false;
785 delete tcanvas;
786 delete pctx;
787 return false;
788 }
789
790 pcaps->bCanDoGLSL = true;
791
792 if (QueryExtension("GL_ARB_texture_non_power_of_two"))
793 pcaps->TextureRectangleFormat = GL_TEXTURE_2D;
794 else if (QueryExtension("GL_OES_texture_npot"))
795 pcaps->TextureRectangleFormat = GL_TEXTURE_2D;
796 else if (QueryExtension("GL_ARB_texture_rectangle"))
797 pcaps->TextureRectangleFormat = GL_TEXTURE_RECTANGLE_ARB;
798
799 pcaps->bOldIntel = false;
800
801 // Can we use VBO?
802 pcaps->bCanDoVBO = true;
803
804#if defined(__WXMSW__) || defined(__WXOSX__)
805 if (pcaps->bOldIntel) pcaps->bCanDoVBO = false;
806#endif
807
808#ifdef __ANDROID__
809 pcaps->bCanDoVBO = false;
810#endif
811
812 // Can we use FBO?
813 pcaps->bCanDoFBO = true;
814
815#ifndef __ANDROID__
816 // We need NPOT to support FBO rendering
817 if (!pcaps->TextureRectangleFormat) pcaps->bCanDoFBO = false;
818
819 // We require certain extensions to support FBO rendering
820 if (!QueryExtension("GL_EXT_framebuffer_object")) pcaps->bCanDoFBO = false;
821#endif
822
823 delete tcanvas;
824 delete pctx;
825 return true;
826#endif
827}
828#endif
829
830bool OCPNPlatform::IsGLCapable() {
831#ifdef ocpnUSE_GL
832
833#ifdef __ANDROID__
834 return true;
835#elif defined(CLI)
836 return false;
837#else
838
839 if (g_bdisable_opengl) return false;
840
841 wxLogMessage("Starting OpenGL test...");
842 wxLog::FlushActive();
843
844 if (!GL_Caps) {
845 GL_Caps = new OCPN_GLCaps();
846 bool bcaps = BuildGLCaps(GL_Caps);
847
848 wxLogMessage("OpenGL test complete.");
849 if (!bcaps) {
850 wxLogMessage("BuildGLCaps fails.");
851 wxLog::FlushActive();
852 return false;
853 }
854 }
855
856 // and so we decide....
857
858 // Require a modern GLSL implementation
859 if (!GL_Caps->bCanDoGLSL) {
860 return false;
861 }
862
863 // We insist on FBO support, since otherwise DC mode is always faster on
864 // canvas panning..
865 if (!GL_Caps->bCanDoFBO) {
866 return false;
867 }
868
869 // OpenGL is OK for OCPN
870 wxLogMessage("OpenGL determined CAPABLE.");
871 wxLog::FlushActive();
872
873 g_bdisable_opengl = false;
874 // g_bopengl = true;
875
876 // Update and flush the config file
877 pConfig->UpdateSettings();
878
879 return true;
880#endif
881#else
882 return false;
883#endif
884}
885
886void OCPNPlatform::SetLocaleSearchPrefixes() {
887#if wxUSE_XLOCALE
888// Add a new prefixes for search order.
889#if defined(__WINDOWS__)
890
891 // Legacy and system plugin location
892 wxString locale_location = GetSharedDataDir();
893 locale_location += "share\\locale";
894 wxLocale::AddCatalogLookupPathPrefix(locale_location);
895 wxString imsg = "Adding catalog lookup path: ";
896 imsg += locale_location;
897 wxLogMessage(imsg);
898
899 // Managed plugin location
900 wxFileName usrShare(GetWinPluginBaseDir() + wxFileName::GetPathSeparator());
901 usrShare.RemoveLastDir();
902 locale_location = usrShare.GetFullPath() + ("share\\locale");
903 wxLocale::AddCatalogLookupPathPrefix(locale_location);
904 imsg = "Adding catalog lookup path: ";
905 imsg += locale_location;
906 wxLogMessage(imsg);
907
908#elif defined(__ANDROID__)
909
910 wxString locale_location = GetSharedDataDir() + "locale";
911 wxLocale::AddCatalogLookupPathPrefix(locale_location);
912
913#elif defined(__UNIX__) && !defined(__WINE__)
914
915 // On Unix, wxWidgets defaults to installation prefix of its own, usually
916 // "/usr". On the other hand, canonical installation prefix for OpenCPN is
917 // "/usr/local".
918 wxString locale_location;
919 if (!wxGetEnv("OPENCPN_PREFIX", &locale_location)) {
920 locale_location = "/usr/local";
921 }
922
923 if (g_bportable) {
924 locale_location = g_Platform->GetHomeDir();
925 }
926
927 wxFileName location;
928 location.AssignDir(locale_location);
929 location.AppendDir("share");
930 location.SetName("locale");
931 locale_location = location.GetFullPath();
932 wxLocale::AddCatalogLookupPathPrefix(locale_location);
933
934 // And then for managed plugins
935 std::string dir = PluginPaths::GetInstance()->UserDatadir();
936 wxString managed_locale_location(dir + "/locale");
937 wxLocale::AddCatalogLookupPathPrefix(managed_locale_location);
938#endif
939
940#ifdef __WXOSX__
941 std::string macDir =
943 "/Library/Application Support/OpenCPN/Contents/Resources";
944 wxString Mac_managed_locale_location(macDir);
945 wxLocale::AddCatalogLookupPathPrefix(Mac_managed_locale_location);
946#endif
947
948#endif
949}
950
951wxString OCPNPlatform::GetDefaultSystemLocale() {
952 wxLogMessage("Getting DefaultSystemLocale...");
953
954 wxString retval = "en_US";
955
956#if wxUSE_XLOCALE
957
958 const wxLanguageInfo *languageInfo =
959 wxLocale::GetLanguageInfo(wxLANGUAGE_DEFAULT);
960 if (languageInfo) retval = languageInfo->CanonicalName;
961
962#if defined(__WXMSW__)
963 LANGID lang_id = GetUserDefaultUILanguage();
964
965 wchar_t lngcp[101];
966 const wxLanguageInfo *languageInfoW = 0;
967 if (0 != GetLocaleInfo(MAKELCID(lang_id, SORT_DEFAULT), LOCALE_SENGLANGUAGE,
968 lngcp, 100)) {
969 wxString lstring = wxString(lngcp);
970
971 languageInfoW = wxLocale::FindLanguageInfo(lngcp);
972 if (languageInfoW)
973 wxLogMessage("Found LanguageInfo for: " + lstring);
974 else
975 wxLogMessage("Could not find LanguageInfo for: " + lstring);
976 } else {
977 wxLogMessage("Could not get LocaleInfo, using wxLANGUAGE_DEFAULT");
978 languageInfoW = wxLocale::GetLanguageInfo(wxLANGUAGE_DEFAULT);
979 }
980
981 if (languageInfoW) retval = languageInfoW->CanonicalName;
982#endif
983
984#if defined(__ANDROID__)
985 retval = androidGetAndroidSystemLocale();
986#endif
987
988#endif
989
990 return retval;
991}
992
993#if wxUSE_XLOCALE || !wxCHECK_VERSION(3, 0, 0)
994wxString OCPNPlatform::GetAdjustedAppLocale() {
995 wxString adjLocale = g_locale;
996
997// For windows, installer may have left information in the registry defining
998// the user's selected install language. If so, override the config file value
999// and use this selection for opencpn...
1000#if defined(__WXMSW__)
1001 if (g_bFirstRun || wxIsEmpty(adjLocale)) {
1002 wxRegKey RegKey(wxString("HKEY_LOCAL_MACHINE\\SOFTWARE\\OpenCPN"));
1003 if (RegKey.Exists()) {
1004 wxLogMessage(
1005 "Retrieving initial language selection from Windows Registry");
1006 RegKey.QueryValue(wxString("InstallerLanguage"), adjLocale);
1007 }
1008 }
1009 if (wxIsEmpty(adjLocale)) {
1010 if (g_localeOverride.Length())
1011 adjLocale = g_localeOverride;
1012 else
1013 adjLocale = GetDefaultSystemLocale();
1014 }
1015#endif
1016#if defined(__ANDROID__)
1017 if (g_localeOverride.Length())
1018 adjLocale = g_localeOverride;
1019 else
1020 adjLocale = GetDefaultSystemLocale();
1021#endif
1022
1023 return adjLocale;
1024}
1025
1026wxString OCPNPlatform::ChangeLocale(wxString &newLocaleID,
1027 wxLocale *presentLocale,
1028 wxLocale **newLocale) {
1029 wxString return_val;
1030
1031 wxString imsg = "ChangeLocale: Language load for: ";
1032 imsg += newLocaleID;
1033 wxLogMessage(imsg);
1034
1035 // Old locale is done.
1036 delete (wxLocale *)presentLocale;
1037
1038 wxLocale *locale = new wxLocale;
1039 if (isFlatpacked()) {
1040 std::string path(getenv("HOME"));
1041 path += "/.var/app/org.opencpn.OpenCPN/data/locale";
1042 locale->AddCatalogLookupPathPrefix(path);
1043 wxLogMessage("Using flatpak locales at %s", path.c_str());
1044 }
1045 wxString loc_lang_canonical;
1046
1047 const wxLanguageInfo *pli = wxLocale::FindLanguageInfo(newLocaleID);
1048 bool b_initok = false;
1049
1050 if (pli) {
1051 locale->Init(pli->Language, 1);
1052 // If the locale was not initialized OK, it may be that the wxstd.mo
1053 // translations of the wxWidgets strings is not present. So try again,
1054 // without attempting to load defaults wxstd.mo.
1055 if (!locale->IsOk()) {
1056 wxString imsg = "ChangeLocale: could not initialize: ";
1057 imsg += newLocaleID;
1058 wxLogMessage(imsg);
1059
1060 delete locale;
1061 locale = new wxLocale;
1062 locale->Init(pli->Language, 0);
1063 }
1064 loc_lang_canonical = pli->CanonicalName;
1065
1066 b_initok = locale->IsOk();
1067#ifdef __ANDROID__
1068 b_initok = true;
1069#endif
1070 }
1071
1072 if (!b_initok) {
1073 wxString imsg = "ChangeLocale: Fall back to en_US";
1074 wxLogMessage(imsg);
1075
1076 delete locale;
1077 locale = new wxLocale;
1078 locale->Init(wxLANGUAGE_ENGLISH_US, 0);
1079 loc_lang_canonical =
1080 wxLocale::GetLanguageInfo(wxLANGUAGE_ENGLISH_US)->CanonicalName;
1081 }
1082
1083 if (b_initok) {
1084 wxString imsg = "ChangeLocale: Locale Init OK for: ";
1085 imsg += loc_lang_canonical;
1086 wxLogMessage(imsg);
1087
1088 // wxWidgets assigneds precedence to message catalogs in reverse order of
1089 // loading. That is, the last catalog containing a certain translatable
1090 // item takes precedence.
1091
1092 // So, Load the catalogs saved in a global string array which is populated
1093 // as PlugIns request a catalog load. We want to load the PlugIn catalogs
1094 // first, so that core opencpn translations loaded later will become
1095 // precedent.
1096
1097 for (unsigned int i = 0; i < g_locale_catalog_array.GetCount(); i++) {
1098 if (!locale->AddCatalog(g_locale_catalog_array[i])) {
1099 wxString emsg = "ERROR Loading translation catalog for: ";
1100 emsg += g_locale_catalog_array[i];
1101 wxLogMessage(emsg);
1102 } else {
1103 wxString imsg = "Loaded translation catalog for: ";
1104 imsg += g_locale_catalog_array[i];
1105 wxLogMessage(imsg);
1106 }
1107 }
1108
1109 // Get core opencpn catalog translation (.mo) file
1110 wxLogMessage("Loading catalog for opencpn core.");
1111 locale->AddCatalog("opencpn");
1112
1113 return_val = locale->GetCanonicalName();
1114
1115 // We may want to override the default system locale, so set a flag.
1116 if (return_val != GetDefaultSystemLocale())
1117 g_localeOverride = return_val;
1118 else
1119 g_localeOverride = "";
1120 }
1121
1122 *newLocale = locale; // return the new locale
1123
1124 // Always use dot as decimal
1125 setlocale(LC_NUMERIC, "C");
1126
1127 return return_val;
1128}
1129#endif
1130
1131// Setup default global options when config file is unavailable,
1132// as on initial startup after new install
1133// The global config object (pConfig) is available, so direct updates are
1134// also allowed
1135
1136void OCPNPlatform::SetDefaultOptions() {
1137 // General options, applied to all platforms
1138 g_bShowOutlines = true;
1139
1140 g_CPAMax_NM = 20.;
1141 g_CPAWarn_NM = 2.;
1142 g_TCPA_Max = 30.;
1143 g_bMarkLost = true;
1144 g_MarkLost_Mins = 8;
1145 g_bRemoveLost = true;
1146 g_RemoveLost_Mins = 10;
1147 g_bShowCOG = true;
1148 g_ShowCOG_Mins = 6;
1149 g_bSyncCogPredictors = false;
1150 g_bHideMoored = false;
1151 g_ShowMoored_Kts = 0.2;
1152 g_SOGminCOG_kts = 0.2;
1153 g_bTrackDaily = false;
1154 g_PlanSpeed = 6.;
1155 g_bFullScreenQuilt = true;
1156 g_bQuiltEnable = true;
1157 g_bskew_comp = false;
1158 g_bShowAreaNotices = false;
1159 g_bDrawAISSize = false;
1160 g_bDrawAISRealtime = false;
1161 g_AIS_RealtPred_Kts = 0.7;
1162 g_bShowAISName = false;
1163 g_nTrackPrecision = 2;
1164 g_bPreserveScaleOnX = true;
1165 g_nAWDefault = 50;
1166 g_nAWMax = 1852;
1167 gps_watchdog_timeout_ticks = kGpsTimeoutSeconds;
1168 g_n_ownship_min_mm = 8;
1169 g_bShowMuiZoomButtons = true;
1170 g_bresponsive = false;
1171
1172 // Initial S52/S57 options
1173 if (pConfig) {
1174 pConfig->SetPath("/Settings/GlobalState");
1175 pConfig->Write("bShowS57Text", true);
1176 pConfig->Write("bShowS57ImportantTextOnly", false);
1177 pConfig->Write("nDisplayCategory", (int)(_DisCat)OTHER);
1178 pConfig->Write("nSymbolStyle", (int)(_LUPname)PAPER_CHART);
1179 pConfig->Write("nBoundaryStyle", (int)(_LUPname)PLAIN_BOUNDARIES);
1180
1181 pConfig->Write("bShowSoundg", true);
1182 pConfig->Write("bShowMeta", false);
1183 pConfig->Write("bUseSCAMIN", true);
1184 pConfig->Write("bShowAtonText", false);
1185 pConfig->Write("bShowLightDescription", false);
1186 pConfig->Write("bExtendLightSectors", true);
1187 pConfig->Write("bDeClutterText", true);
1188 pConfig->Write("bShowNationalText", true);
1189
1190 pConfig->Write("S52_MAR_SAFETY_CONTOUR", 3);
1191 pConfig->Write("S52_MAR_SHALLOW_CONTOUR", 2);
1192 pConfig->Write("S52_MAR_DEEP_CONTOUR", 6);
1193 pConfig->Write("S52_MAR_TWO_SHADES", 0);
1194 pConfig->Write("S52_DEPTH_UNIT_SHOW", 1);
1195
1196 pConfig->Write("ZoomDetailFactorVector", 3);
1197
1198 pConfig->Write("nColorScheme", 1); // higher contrast on NOAA RNCs
1199
1200// A few more often requested defaults, not applicable to Android
1201#ifndef __ANDROID__
1202 g_bEnableZoomToCursor = true;
1203 g_bsmoothpanzoom = true;
1204 g_bShowMenuBar = true;
1205#endif
1206 }
1207
1208#ifdef __WXMSW__
1209 g_bShowMenuBar = false;
1210 // Enable some default PlugIns, and their default options
1211 if (pConfig) {
1212 pConfig->SetPath("/PlugIns/chartdldr_pi.dll");
1213 pConfig->Write("bEnabled", true);
1214
1215 pConfig->SetPath("/PlugIns/wmm_pi.dll");
1216 pConfig->Write("bEnabled", true);
1217
1218 pConfig->SetPath("/Settings/WMM");
1219 pConfig->Write("ShowIcon", true);
1220 pConfig->Write("ShowLiveIcon", true);
1221 }
1222#endif
1223
1224#ifdef __WXOSX__
1225 // Enable some default PlugIns, and their default options
1226 if (pConfig) {
1227 pConfig->SetPath("/PlugIns/libchartdldr_pi.dylib");
1228 pConfig->Write("bEnabled", true);
1229
1230 pConfig->SetPath("/PlugIns/libwmm_pi.dylib");
1231 pConfig->Write("bEnabled", true);
1232
1233 pConfig->SetPath("/Settings/WMM");
1234 pConfig->Write("ShowIcon", true);
1235 pConfig->Write("ShowLiveIcon", true);
1236 }
1237#endif
1238
1239#ifdef __linux__
1240 // Enable some default PlugIns, and their default options
1241 if (pConfig) {
1242 pConfig->SetPath("/PlugIns/libchartdldr_pi.so");
1243 pConfig->Write("bEnabled", true);
1244
1245 pConfig->SetPath("/PlugIns/libwmm_pi.so");
1246 pConfig->Write("bEnabled", true);
1247
1248 pConfig->SetPath("/Settings/WMM");
1249 pConfig->Write("ShowIcon", true);
1250 pConfig->Write("ShowLiveIcon", true);
1251 }
1252#endif
1253
1254#ifdef __ANDROID__
1255
1256#ifdef ocpnUSE_GL
1257 g_bopengl = true;
1258 g_GLOptions.m_bTextureCompression = 1;
1259 g_GLOptions.m_bTextureCompressionCaching = 1;
1260#endif
1261
1262 qDebug() << "SetDefaultOptions";
1263
1264 g_btouch = true;
1265 g_bresponsive = true;
1266 g_default_font_size = 18; // This is pretty close to TextAppearance.Medium
1267 g_bUIexpert = true;
1268
1269 g_bShowStatusBar = true;
1270 g_cm93_zoom_factor = 0;
1271 g_oz_vector_scale = false;
1272 g_fog_overzoom = false;
1273
1274 g_bRollover = true;
1275 g_bShowMuiZoomButtons = true;
1276
1277 g_GUIScaleFactor = 0; // nominal
1278 g_ChartNotRenderScaleFactor = 2.0;
1279
1280 // Suppress most tools, especially those that appear in the Basic menus.
1281 // Of course, they may be re-enabled by experts...
1282 g_toolbarConfig = "X.....XX.......XX.XXXXXXXXXXX";
1283 g_bPermanentMOBIcon = false;
1284
1285 wxString sGPS = "2;3;;0;0;;0;1;0;0;;0;;1;0;0;0;0"; // 17 parms
1286 ConnectionParams *new_params = new ConnectionParams(sGPS);
1287
1288 new_params->bEnabled = true;
1289 TheConnectionParams().push_back(new_params);
1290
1291 g_default_font_facename = "Roboto";
1292
1293 // Enable some default PlugIns, and their default options
1294
1295 if (pConfig) {
1296 pConfig->SetPath("/PlugIns/libchartdldr_pi.so");
1297 pConfig->Write("bEnabled", true);
1298
1299 pConfig->SetPath("/PlugIns/libwmm_pi.so");
1300 pConfig->Write("bEnabled", true);
1301
1302 pConfig->SetPath("/Settings/WMM");
1303 pConfig->Write("ShowIcon", true);
1304 pConfig->Write("ShowLiveIcon", true);
1305
1306 pConfig->SetPath("/PlugIns/libgrib_pi.so");
1307 pConfig->Write("bEnabled", true);
1308
1309 pConfig->SetPath("/PlugIns/libdashboard_pi.so");
1310 pConfig->Write("bEnabled", true);
1311
1312 pConfig->SetPath("/PlugIns/GRIB");
1313 pConfig->Write("GRIBCtrlBarPosX", 100);
1314 pConfig->Write("GRIBCtrlBarPosY", 0);
1315
1316 pConfig->SetPath("/Settings/GRIB");
1317 pConfig->Write("CursorDataShown", 0);
1318
1319 // This is ugly hack
1320 // TODO
1321 pConfig->SetPath("/PlugIns/liboesenc_pi.so");
1322 pConfig->Write("bEnabled", true);
1323
1324 pConfig->SetPath("/Settings/QTFonts");
1325
1326 // Status Bar
1327 wxString str = "en_US-b25a3899";
1328 wxString pval = "StatusBar:Roboto,26,-1,5,75,0,0,0,0,0:rgb(0, 0, 0)";
1329 pConfig->Write(str, pval);
1330 FontMgr::Get().LoadFontNative(&str, &pval);
1331
1332 // Dialog
1333 str = "en_US-9c3b3a0d";
1334 pval = "DialogStatusBar:Roboto,18,-1,5,50,0,0,0,0,0:rgb(0, 0, 0)";
1335 pConfig->Write(str, pval);
1336 FontMgr::Get().LoadFontNative(&str, &pval);
1337
1338 // Set track default color to magenta
1339 pConfig->SetPath("/Settings/Others");
1340 pConfig->Write("TrackLineColour", "#C545C3");
1341 g_colourTrackLineColour.Set(197, 69, 195);
1342
1343 qDebug() << "SetDefaultOptions.Config";
1344 }
1345
1346#endif
1347}
1348
1349// Setup global options on upgrade detected
1350// The global config object (pConfig) has already been loaded, so updates
1351// here override values set by config.
1352// Direct updates to config for next boot are also supported.
1353
1354void OCPNPlatform::SetUpgradeOptions(wxString vNew, wxString vOld) {
1355#ifdef __ANDROID__
1356
1357 qDebug() << "Upgrade check"
1358 << "from: " << vOld.mb_str() << " to: " << vNew.mb_str();
1359
1360 if (androidGetVersionCode() > g_AndroidVersionCode) { // upgrade
1361 qDebug() << "Upgrade detected"
1362 << "from VC: " << g_AndroidVersionCode
1363 << " to VC: " << androidGetVersionCode();
1364
1365 // Set some S52/S57 options
1366 if (pConfig) {
1367 pConfig->SetPath("/Settings/GlobalState");
1368 pConfig->Write("bShowS57Text", true);
1369 }
1370
1371 g_ChartNotRenderScaleFactor = 2.0;
1372 g_n_ownship_min_mm = 8;
1373 g_toolbarConfig = "X.....XX.......XX.XXXXXXXXXXX";
1374
1375 // Experience indicates a slightly larger default font size is better
1376 pConfig->DeleteGroup("/Settings/QTFonts");
1377 g_default_font_size = 20;
1378 g_default_font_facename = "Roboto";
1379
1380 FontMgr::Get().Shutdown(); // Restart the font manager
1381
1382 // Reshow the zoom buttons
1383 g_bShowMuiZoomButtons = true;
1384
1385 // Clear the default chart storage location
1386 // Will get set to e.g. "/storage/emulated/0" later
1387 pInit_Chart_Dir->Clear();
1388
1389 // A few popular requests,
1390 // which will take effect on next App startup.
1391 pConfig->SetPath("/Settings/WMM");
1392 pConfig->Write("ShowIcon", true);
1393 pConfig->Write("ShowLiveIcon", true);
1394
1395 pConfig->SetPath("/Canvas/CanvasConfig1");
1396 pConfig->Write("canvasENCShowVisibleSectorLights", 0);
1397
1398 // Manage plugins
1399 // Clear the cache, allowing deprecation of obsolete plugins
1401
1402 // Remove any problematic plugins, so preparing for upgraded versions
1403 // Todo: Scrub this list from time to time
1404
1405 // VersionCode 123 (August, 2025)
1406 AndroidRemoveSystemFile(
1407 "/data/user/0/org.opencpn.opencpn/manPlug/libchartscale_pi.so");
1408 }
1409
1410 // Set track default color to magenta
1411 g_colourTrackLineColour.Set(197, 69, 195);
1412#endif
1413
1414 // Check for upgrade....
1415 if (!vOld.IsSameAs(vNew)) { // upgrade
1416
1417 // Verify some default directories, create if necessary
1418
1419 // UserIcons
1420 wxString UserIconPath = GetPrivateDataDir();
1421 wxChar sep = wxFileName::GetPathSeparator();
1422 if (UserIconPath.Last() != sep) UserIconPath.Append(sep);
1423 UserIconPath.Append("UserIcons");
1424
1425 if (!::wxDirExists(UserIconPath)) {
1426 ::wxMkdir(UserIconPath);
1427 }
1428
1429 // layers
1430 wxString LayersPath = GetPrivateDataDir();
1431 if (LayersPath.Last() != sep) LayersPath.Append(sep);
1432 LayersPath.Append("layers");
1433
1434 if (!::wxDirExists(LayersPath)) {
1435 ::wxMkdir(LayersPath);
1436 }
1437
1438 // Force a generally useable sound command, overriding any previous user's
1439 // selection
1440 // that may not be available on new build.
1441 g_CmdSoundString = wxString(OCPN_SOUND_CMD);
1442 pConfig->SetPath("/Settings");
1443 pConfig->Write("CmdSoundString", g_CmdSoundString);
1444
1445 // Force AIS specific sound effects ON, leaving the master control
1446 // (g_bAIS_CPA_Alert_Audio) as configured
1447 g_bAIS_GCPA_Alert_Audio = true;
1448 g_bAIS_SART_Alert_Audio = true;
1449 g_bAIS_DSC_Alert_Audio = true;
1450
1451 // Force a recalculation of default main toolbar location
1452 g_maintoolbar_x = -1;
1453
1454 // Check the tide/current databases for readability,
1455 // remove any not readable
1456 std::vector<std::string> TCDS_temp;
1457 for (unsigned int i = 0; i < TideCurrentDataSet.size(); i++)
1458 TCDS_temp.push_back(TideCurrentDataSet[i]);
1459
1460 TideCurrentDataSet.clear();
1461 for (unsigned int i = 0; i < TCDS_temp.size(); i++) {
1462 wxString tide = TCDS_temp[i];
1463 wxFileName ft(tide);
1464 if (ft.FileExists()) TideCurrentDataSet.push_back(TCDS_temp[i]);
1465 }
1466
1467 // Force default (baked-in) values for CompatOS
1468 // Thus providing for the case when some core dependent elements
1469 // are updated. (e.g. flatpak SDK 22.08->24.08->???)
1470 g_compatOS = "";
1471 g_compatOsVersion = "";
1472 pConfig->SetPath("/Settings");
1473 pConfig->Write("CompatOS", g_compatOS);
1474 pConfig->Write("CompatOsVersion", g_compatOsVersion);
1475 }
1476}
1477
1478int OCPNPlatform::platformApplyPrivateSettingsString(wxString settings,
1479 ArrayOfCDI *pDirArray) {
1480 int ret_val = 0;
1481#ifdef __ANDROID__
1482 ret_val = androidApplySettingsString(settings, pDirArray);
1483#endif
1484
1485 return ret_val;
1486}
1487
1488void OCPNPlatform::applyExpertMode(bool mode) {
1489#ifdef __ANDROID__
1490 // g_bexpert = mode; // toolbar only shows plugin icons if expert mode is
1491 // false
1492 g_bBasicMenus = !mode; // simplified context menus in basic mode
1493#endif
1494}
1495
1496//--------------------------------------------------------------------------
1497// Per-Platform file/directory support
1498//--------------------------------------------------------------------------
1499
1500static wxString ExpandPaths(wxString paths, OCPNPlatform *platform);
1501
1502int OCPNPlatform::DoFileSelectorDialog(wxWindow *parent, wxString *file_spec,
1503 wxString Title, wxString initDir,
1504 wxString suggestedName,
1505 wxString wildcard) {
1506 wxString file;
1507 int result = wxID_CANCEL;
1508
1509#ifdef __ANDROID__
1510 // Verify that initDir is traversable, fix it if not...
1511 wxString idir = initDir;
1512 if (initDir.StartsWith(
1513 "/data/data")) // not good, provokes a crash usually...
1514 idir = GetWritableDocumentsDir();
1515
1516 result = androidFileChooser(&file, idir, Title, suggestedName, wildcard);
1517 if (file_spec) *file_spec = file;
1518#else
1519 long flag = wxFD_DEFAULT_STYLE;
1520 if (suggestedName.Length()) { // new file
1521 flag = wxFD_SAVE;
1522 }
1523
1524 wxString mask = wildcard;
1525 if (wxNOT_FOUND != mask.Find("gpx")) mask.Prepend("GPX files (*.gpx)|");
1526
1527 wxFileDialog *psaveDialog =
1528 new wxFileDialog(parent, Title, initDir, suggestedName, mask, flag);
1529
1530 // Try to reduce the dialog size, and scale fonts down, if necessary.
1531 // if(g_bresponsive && parent)
1532 // psaveDialog = g_Platform->AdjustFileDialogFont(parent,
1533 // psaveDialog);
1534
1535#ifdef __WXOSX__
1536 if (parent) parent->HideWithEffect(wxSHOW_EFFECT_BLEND);
1537#endif
1538
1539 result = psaveDialog->ShowModal();
1540
1541#ifdef __WXOSX__
1542 if (parent) parent->ShowWithEffect(wxSHOW_EFFECT_BLEND);
1543#endif
1544
1545 if (file_spec) *file_spec = psaveDialog->GetPath();
1546 delete psaveDialog;
1547
1548#endif
1549
1550 return result;
1551}
1552
1553int OCPNPlatform::DoDirSelectorDialog(wxWindow *parent, wxString *file_spec,
1554 wxString Title, wxString initDir,
1555 bool b_addFiles) {
1556 wxString dir;
1557 int result = wxID_CANCEL;
1558
1559#ifdef __ANDROID__
1560 // Verify that initDir is traversable, fix it if not...
1561 wxString idir = initDir;
1562 if (initDir.StartsWith(
1563 "/data/data")) // not good, provokes a crash usually...
1564 idir = GetWritableDocumentsDir();
1565
1566 result = androidFileChooser(&dir, idir, Title, "", "", true,
1567 b_addFiles); // Directories only, maybe add dirs
1568 if (file_spec) *file_spec = dir;
1569#else
1570 wxDirDialog *dirSelector = new wxDirDialog(
1571 parent, Title, initDir, wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST);
1572
1573 wxFont *qFont = GetOCPNScaledFont(_("Dialog"));
1574 dirSelector->SetFont(*qFont);
1575
1576 // Try to reduce the dialog size, and scale fonts down, if necessary.
1577 // if(g_bresponsive && parent)
1578 // dirSelector = AdjustDirDialogFont(parent, dirSelector);
1579
1580#ifdef __WXOSX__
1581 if (parent) parent->HideWithEffect(wxSHOW_EFFECT_BLEND);
1582#endif
1583
1584 result = dirSelector->ShowModal();
1585
1586#ifdef __WXOSX__
1587 if (parent) parent->ShowWithEffect(wxSHOW_EFFECT_BLEND);
1588#endif
1589
1590 if (result == wxID_CANCEL) {
1591 } else {
1592 if (file_spec) {
1593 *file_spec = dirSelector->GetPath();
1594 }
1595 }
1596
1597 delete dirSelector;
1598#endif
1599
1600 return result;
1601}
1602
1603MyConfig *OCPNPlatform::GetConfigObject() {
1604 MyConfig *result = NULL;
1605
1606 result = new MyConfig(GetConfigFileName());
1607
1608 return result;
1609}
1610
1611//--------------------------------------------------------------------------
1612// Internal GPS Support
1613//--------------------------------------------------------------------------
1614
1615bool OCPNPlatform::hasInternalGPS(wxString profile) {
1616#ifdef __ANDROID__
1617 bool t = androidDeviceHasGPS();
1618 // qDebug() << "androidDeviceHasGPS" << t;
1619 return t;
1620#else
1621
1622 return false;
1623
1624#endif
1625}
1626
1627//--------------------------------------------------------------------------
1628// Platform Display Support
1629//--------------------------------------------------------------------------
1630
1631void OCPNPlatform::ShowBusySpinner() { AbstractPlatform::ShowBusySpinner(); }
1632
1633void OCPNPlatform::HideBusySpinner() { AbstractPlatform::HideBusySpinner(); }
1634
1635double OCPNPlatform::GetDisplayDensityFactor() {
1636#ifdef __ANDROID__
1637 return getAndroidDisplayDensity();
1638#else
1639 return 1.0;
1640#endif
1641}
1642
1643long OCPNPlatform::GetDefaultToolbarOrientation() {
1644#ifndef __ANDROID__
1645 return wxTB_VERTICAL;
1646#else
1647 return wxTB_VERTICAL;
1648#endif
1649}
1650
1651int OCPNPlatform::GetStatusBarFieldCount() {
1652#ifdef __ANDROID__
1653 int count = 1;
1654
1655 // Make a horizontal measurement...
1656 wxScreenDC dc;
1657 wxFont *templateFont = FontMgr::Get().GetFont(_("StatusBar"), 0);
1658 dc.SetFont(*templateFont);
1659
1660 int width;
1661 dc.GetTextExtent("WWWWWW", &width, NULL, NULL, NULL, templateFont);
1662 double font_size_pix = (double)width / 6.0;
1663
1664 wxSize dispSize = getDisplaySize();
1665
1666 double nChars = dispSize.x / font_size_pix;
1667
1668 if (nChars < 40)
1669 count = 1;
1670 else
1671 count = 2;
1672
1673 return count;
1674
1675#else
1676 return kStatFieldCount; // default
1677#endif
1678}
1679
1680double OCPNPlatform::getFontPointsperPixel() {
1681 double pt_per_pixel = 1.0;
1682
1683 // #ifdef __ANDROID__
1684 // On Android, this calculation depends on the density bucket in use.
1685 // Also uses some magic numbers...
1686 // For reference, see http://pixplicity.com/dp-px-converter/
1687 // pt_per_pixel = 14.0 / (31.11 * getAndroidDisplayDensity()) ;
1688
1689 // #else
1690
1691 if (m_pt_per_pixel == 0) {
1692 // Make a measurement...
1693 wxScreenDC dc;
1694
1695 wxFont *f = FontMgr::Get().FindOrCreateFont(
1696 12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, FALSE,
1697 wxString(""), wxFONTENCODING_SYSTEM);
1698 dc.SetFont(*f);
1699
1700 int width, height;
1701 dc.GetTextExtent("H", &width, &height, NULL, NULL, f);
1702
1703 if (height > 0) m_pt_per_pixel = 12.0 / (double)height;
1704 }
1705 if (m_pt_per_pixel > 0) pt_per_pixel = m_pt_per_pixel;
1706 // #endif
1707
1708 return pt_per_pixel;
1709}
1710
1712#ifdef __ANDROID__
1713 return getAndroidDisplayDimensions();
1714#else
1715 return wxSize(g_monitor_info[g_current_monitor].width,
1717#endif
1718}
1719
1721 if (g_current_monitor < m_displaySizeMMOverride.size()) {
1722 if (m_displaySizeMMOverride[g_current_monitor] > 0) {
1723 return m_displaySizeMMOverride[g_current_monitor];
1724 }
1725 }
1726
1727 double ret = g_monitor_info[g_current_monitor].width_mm;
1728
1729#ifdef __ANDROID__
1730 ret = GetAndroidDisplaySize();
1731#endif
1732
1733 return ret;
1734}
1735
1736double OCPNPlatform::GetDisplayAreaCM2() {
1737 double size1 = GetDisplaySizeMM();
1738 wxSize sz = getDisplaySize();
1739 double ratio = 1.;
1740 if (sz.x < sz.y)
1741 ratio = (double)sz.x / (double)sz.y; // <1
1742 else
1743 ratio = (double)sz.y / (double)sz.x; // <1
1744
1745 double area = size1 * (size1 * ratio) / 100.;
1746 // qDebug() << "cm2" << size1 << ratio << sz.x << sz.y;
1747 return area;
1748}
1749
1750void OCPNPlatform::SetDisplaySizeMM(size_t monitor, double sizeMM) {
1751 if (monitor < m_displaySizeMMOverride.size()) {
1752 m_displaySizeMMOverride[monitor] = sizeMM;
1753 }
1754}
1755
1756double OCPNPlatform::GetDisplayDPmm() {
1757#ifdef __ANDROID__
1758 return getAndroidDPmm();
1759#else
1760 double r = getDisplaySize().x; // dots
1761 return r / GetDisplaySizeMM();
1762#endif
1763}
1764
1765unsigned int OCPNPlatform::GetSelectRadiusPix() {
1766 return GetDisplayDPmm() *
1767 (g_btouch ? g_selection_radius_touch_mm : g_selection_radius_mm);
1768}
1769
1770bool OCPNPlatform::GetFullscreen() {
1771 bool bret = false;
1772#ifdef __ANDROID__
1773 bret = androidGetFullscreen();
1774#else
1775
1776#endif
1777
1778 return bret;
1779}
1780
1781bool OCPNPlatform::SetFullscreen(bool bFull) {
1782 bool bret = false;
1783#ifdef __ANDROID__
1784 bret = androidSetFullscreen(bFull);
1785#else
1786#endif
1787
1788 return bret;
1789}
1790
1791void OCPNPlatform::PositionAISAlert(wxWindow *alert_window) {
1792#ifndef __ANDROID__
1793 if (alert_window) {
1794 alert_window->SetSize(g_ais_alert_dialog_x, g_ais_alert_dialog_y,
1795 g_ais_alert_dialog_sx, g_ais_alert_dialog_sy);
1796 }
1797#else
1798 if (alert_window) {
1799 alert_window->Centre();
1800 }
1801
1802#endif
1803}
1804
1805wxDirDialog *OCPNPlatform::AdjustDirDialogFont(wxWindow *container,
1806 wxDirDialog *dlg) {
1807 wxDirDialog *ret_dlg = dlg;
1808#ifndef __WXGTK__
1809
1810 dlg->Show();
1811 dlg->SetSize(container->GetSize());
1812 dlg->Centre();
1813
1814 wxSize sds = dlg->GetSize();
1815 wxSize ss = container->GetSize();
1816
1817 if (sds.x > ss.x) {
1818 dlg->Hide();
1819
1820 wxString msg = dlg->GetMessage();
1821 wxString default_dir = dlg->GetPath();
1822
1823 delete dlg;
1824
1825 ret_dlg = new wxDirDialog(NULL, msg, default_dir,
1826 wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST);
1827
1828 wxFont *dialogFont = GetOCPNScaledFont(_("Dialog"));
1829 wxFont *smallFont = new wxFont(*dialogFont);
1830 smallFont->SetPointSize((smallFont->GetPointSize() / 2) +
1831 0.5); // + 0.5 to round instead of truncate
1832 ret_dlg->SetFont(*smallFont);
1833
1834 ret_dlg->SetSize(container->GetSize());
1835 ret_dlg->Centre();
1836 }
1837 ret_dlg->Hide();
1838#endif
1839 return ret_dlg;
1840}
1841
1842wxFileDialog *OCPNPlatform::AdjustFileDialogFont(wxWindow *container,
1843 wxFileDialog *dlg) {
1844 wxFileDialog *ret_dlg = dlg;
1845#ifndef __WXGTK__
1846
1847 dlg->Show();
1848 dlg->SetSize(container->GetSize());
1849 dlg->Centre();
1850
1851 wxSize sds = dlg->GetSize();
1852 wxSize ss = container->GetSize();
1853
1854 if (sds.x > ss.x) {
1855 dlg->Hide();
1856
1857 wxString msg = dlg->GetMessage();
1858 wxString default_dir = dlg->GetDirectory();
1859 wxString default_file = dlg->GetFilename();
1860 wxString wildcard = dlg->GetWildcard();
1861
1862 delete dlg;
1863
1864 ret_dlg = new wxFileDialog(NULL, msg, default_dir, default_file, wildcard,
1865 wxFD_OPEN);
1866
1867 wxFont *dialogFont = GetOCPNScaledFont(_("Dialog"));
1868 wxFont *smallFont = new wxFont(*dialogFont);
1869 smallFont->SetPointSize((smallFont->GetPointSize() / 2) +
1870 0.5); // + 0.5 to round instead of truncate
1871 ret_dlg->SetFont(*smallFont);
1872
1873 ret_dlg->SetSize(container->GetSize());
1874 ret_dlg->Centre();
1875 }
1876 ret_dlg->Hide();
1877#endif
1878 return ret_dlg;
1879}
1880
1881double OCPNPlatform::GetToolbarScaleFactor(int GUIScaleFactor) {
1882 double rv = 1.0;
1883#ifdef __ANDROID__
1884
1885 // We try to arrange matters so that at GUIScaleFactor=0, the tool icons are
1886 // approximately 9 mm in size and that the value may range from 0.5 -> 2.0
1887
1888 // Get the basic size of a tool icon
1889 wxSize style_tool_size(32, 32);
1890
1891 if (g_StyleManager) {
1892 ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
1893 if (style) style_tool_size = style->GetToolSize();
1894 }
1895 double tool_size = style_tool_size.x;
1896
1897 // unless overridden by user, we declare the "best" tool size
1898 // to be roughly the same as the ActionBar height.
1899 // This may be approximated in a device orientation-independent way as:
1900 // 45pixels * DENSITY
1901 double premult = 1.0;
1902 if (g_config_display_size_manual && g_config_display_size_mm[0] > 0) {
1903 double target_size = 9.0; // mm
1904
1905 double basic_tool_size_mm = tool_size / GetDisplayDPmm();
1906 premult = target_size / basic_tool_size_mm;
1907
1908 } else {
1909 premult = wxMax(45 * getAndroidDisplayDensity(), 45) /
1910 tool_size; // make sure not too small
1911 }
1912
1913 // Adjust the scale factor using the global GUI scale parameter, ranging from
1914 // 0.5 -> 2.0
1915 double postmult = exp(GUIScaleFactor * (log(2.0) / 5.0));
1916
1917 // qDebug() << "parmsF" << GUIScaleFactor << premult << postmult;
1918
1919 rv = premult * postmult;
1920 rv = wxMin(rv, getAndroidDisplayDensity() *
1921 3); // Clamp at density * arbitrary limit factor
1922
1923#else
1924 double premult = 1.0;
1925
1926 if (g_bresponsive) {
1927 // Get the basic size of a tool icon
1928 ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
1929 wxSize style_tool_size = style->GetToolSize();
1930 double tool_size = style_tool_size.x;
1931
1932 // unless overridden by user, we declare the "best" tool size
1933 // to be roughly 9 mm square.
1934 double target_size = 9.0; // mm
1935
1936 double basic_tool_size_mm = tool_size / GetDisplayDPmm();
1937 premult = target_size / basic_tool_size_mm;
1938 }
1939
1940 // Adjust the scale factor using the global GUI scale parameter
1941 double postmult = exp(GUIScaleFactor * (0.693 / 5.0)); // exp(2)
1942
1943 rv = premult * postmult;
1944 rv = wxMin(rv, 3.0); // Clamp at 3.0
1945 rv = wxMax(rv, 0.5); // and at 0.5
1946
1947 rv /= g_BasePlatform->GetDisplayDIPMult(wxTheApp->GetTopWindow());
1948
1949#endif
1950
1951 return rv;
1952}
1953
1954double OCPNPlatform::GetCompassScaleFactor(int GUIScaleFactor) {
1955 double rv = 1.0;
1956#ifdef __ANDROID__
1957
1958 // We try to arrange matters so that at GUIScaleFactor=0, the compass icon is
1959 // approximately 9 mm in size and that the value may range from 0.5 -> 2.0
1960
1961 if (g_bresponsive) {
1962 // Get the basic size of a tool icon
1963 ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
1964 wxSize style_tool_size = style->GetToolSize();
1965 double compass_size = style_tool_size.x;
1966
1967 // We declare the "nominal best" icon size to be a bit smaller than the
1968 // ActionBar height.
1969 // This may be approximated in a device orientation-independent way as:
1970 // 28pixels * DENSITY
1971 double premult = wxMax(28 * getAndroidDisplayDensity(), 50) / compass_size;
1972
1973 // Adjust the scale factor using the global GUI scale parameter
1974 double postmult = exp(GUIScaleFactor * (log(2.0) / 5.0));
1975 // rv = wxMin(rv, 1.5); // Clamp at 1.5
1976
1977 rv = premult * postmult;
1978 rv = wxMin(rv, getAndroidDisplayDensity() *
1979 3); // Clamp at density * arbitrary limit factor
1980 }
1981
1982#else
1983 double premult = 1.0;
1984
1985 if (g_bresponsive) {
1986 ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
1987 wxSize style_tool_size = style->GetToolSize();
1988 double compass_size = style_tool_size.x;
1989
1990 // We declare the "best" tool size to be roughly 6 mm.
1991 double target_size = 6.0; // mm
1992
1993 double basic_tool_size_mm = compass_size / GetDisplayDPmm();
1994 premult = target_size / basic_tool_size_mm;
1995 }
1996
1997 double postmult = exp(GUIScaleFactor * (0.693 / 5.0)); // exp(2)
1998
1999 rv = premult * postmult;
2000
2001 rv = wxMin(rv, 3.0); // Clamp at 3.0
2002 rv = wxMax(rv, 0.5);
2003
2004#if defined(__WXOSX__) || defined(__WXGTK3__)
2005 // Support scaled HDPI displays.
2006
2007 if (top_frame::Get()) rv *= top_frame::Get()->GetContentScaleFactor();
2008#endif
2009
2010 rv /= g_BasePlatform->GetDisplayDIPMult(wxTheApp->GetTopWindow());
2011
2012#endif
2013
2014 return rv;
2015}
2016
2017float OCPNPlatform::GetChartScaleFactorExp(float scale_linear) {
2018 double factor = 1.0;
2019#ifndef __ANDROID__
2020 factor = exp(scale_linear * (log(3.0) / 5.0));
2021
2022#else
2023 // the idea here is to amplify the scale factor for higher density displays,
2024 // in a measured way....
2025 factor = exp(scale_linear * (0.693 / 5.0));
2026// factor *= getAndroidDisplayDensity();
2027#endif
2028
2029 factor = wxMax(factor, .5);
2030 factor = wxMin(factor, 6.);
2031
2032 return factor;
2033}
2034
2035float OCPNPlatform::GetMarkScaleFactorExp(float scale_linear) {
2036 if (scale_linear <= 0)
2037 return GetChartScaleFactorExp(scale_linear);
2038 else
2039 return GetChartScaleFactorExp(scale_linear - 1);
2040}
2041
2042// float OCPNPlatform::GetDIPScaleFactor() {
2043// float rv = 1.0;
2044// #ifdef __WXMSW__
2045// if (gFrame)
2046// rv = (double)(gFrame->FromDIP(100))/100.;
2047// #endif
2048//
2049// return rv;
2050// }
2051
2052//--------------------------------------------------------------------------
2053// Internal Bluetooth Support
2054//--------------------------------------------------------------------------
2055
2056bool OCPNPlatform::hasInternalBT(wxString profile) {
2057#ifdef __ANDROID__
2058 bool t = androidDeviceHasBlueTooth();
2059 // qDebug() << "androidDeviceHasBluetooth" << t;
2060 return t;
2061#else
2062
2063 return false;
2064#endif
2065}
2066
2067bool OCPNPlatform::startBluetoothScan() {
2068#ifdef __ANDROID__
2069 return androidStartBluetoothScan();
2070#else
2071
2072 return false;
2073#endif
2074}
2075
2076bool OCPNPlatform::stopBluetoothScan() {
2077#ifdef __ANDROID__
2078 return androidStopBluetoothScan();
2079#else
2080
2081 return false;
2082#endif
2083}
2084
2085wxArrayString OCPNPlatform::getBluetoothScanResults() {
2086 wxArrayString ret_val;
2087#ifdef __ANDROID__
2088 return androidGetBluetoothScanResults();
2089#else
2090
2091 ret_val.Add("line 1");
2092 ret_val.Add("line 2");
2093 ret_val.Add("line 3");
2094 return ret_val;
2095
2096#endif
2097}
2098
2099//--------------------------------------------------------------------------
2100// Per-Platform Utility support
2101//--------------------------------------------------------------------------
2102
2103bool OCPNPlatform::AllowAlertDialog(const wxString &class_name) {
2104#ifdef __ANDROID__
2105 // allow if TopLevelWindow count is <=4, implying normal runtime screen
2106 // layout
2107 int nTLW = 0;
2108 wxWindowList::compatibility_iterator node = wxTopLevelWindows.GetFirst();
2109 while (node) {
2110 wxWindow *win = node->GetData();
2111 if (win->IsShown()) nTLW++;
2112
2113 node = node->GetNext();
2114 }
2115
2116 // qDebug() << "AllowAlertDialog" << g_boptionsactive << g_running << nTLW;
2117 if (g_options)
2118 return (g_running && !g_options->IsShown() && (nTLW <= 4));
2119 else
2120 return (g_running && (nTLW <= 4));
2121
2122#else
2123 return true;
2124#endif
2125}
2126
2127void OCPNPlatform::setChartTypeMaskSel(int mask, wxString &indicator) {
2128#ifdef __ANDROID__
2129 return androidSetChartTypeMaskSel(mask, indicator);
2130#endif
2131}
2132
2133#ifdef __ANDROID__
2134QString g_qtStyleSheet;
2135
2136bool LoadQtStyleSheet(wxString &sheet_file) {
2137 if (wxFileExists(sheet_file)) {
2138 // QApplication qApp = getqApp();
2139 if (qApp) {
2140 QString file(sheet_file.c_str());
2141 QFile File(file);
2142 File.open(QFile::ReadOnly);
2143 g_qtStyleSheet = QLatin1String(File.readAll());
2144
2145 // qApp->setStyleSheet(g_qtStyleSheet);
2146 return true;
2147 } else
2148 return false;
2149 } else
2150 return false;
2151}
2152
2153QString getQtStyleSheet() { return g_qtStyleSheet; }
2154
2155#endif
2156
2157bool OCPNPlatform::isPlatformCapable(int flag) {
2158#ifndef __ANDROID__
2159 return true;
2160#else
2161 if (flag == PLATFORM_CAP_PLUGINS) {
2162 long platver;
2163 wxString tsdk(android_plat_spc.msdk);
2164 if (tsdk.ToLong(&platver)) {
2165 if (platver >= 11) return true;
2166 }
2167 } else if (flag == PLATFORM_CAP_FASTPAN) {
2168 long platver;
2169 wxString tsdk(android_plat_spc.msdk);
2170 if (tsdk.ToLong(&platver)) {
2171 if (platver >= 14) return true;
2172 }
2173 }
2174
2175 return false;
2176#endif
2177}
2178
2179void OCPNPlatform::platformLaunchDefaultBrowser(wxString URL) {
2180#ifdef __ANDROID__
2181 androidLaunchBrowser(URL);
2182#else
2183 ::wxLaunchDefaultBrowser(URL);
2184#endif
2185}
2186
2187// ============================================================================
2188// OCPNColourPickerCtrl implementation
2189// ============================================================================
2190
2191BEGIN_EVENT_TABLE(OCPNColourPickerCtrl, wxButton)
2192#ifdef __WXMSW__
2193EVT_PAINT(OCPNColourPickerCtrl::OnPaint)
2194#endif
2195END_EVENT_TABLE()
2196
2197// ----------------------------------------------------------------------------
2198// OCPNColourPickerCtrl
2199// ----------------------------------------------------------------------------
2200
2201OCPNColourPickerCtrl::OCPNColourPickerCtrl(wxWindow *parent, wxWindowID id,
2202 const wxColour &initial,
2203 const wxPoint &pos,
2204 const wxSize &size, long style,
2205 const wxValidator &validator,
2206 const wxString &name) {
2207 Create(parent, id, initial, pos, size, style, validator, name);
2208}
2209
2210bool OCPNColourPickerCtrl::Create(wxWindow *parent, wxWindowID id,
2211 const wxColour &col, const wxPoint &pos,
2212 const wxSize &size, long style,
2213 const wxValidator &validator,
2214 const wxString &name) {
2215 m_bitmap = wxBitmap(60, 13);
2216
2217 // create this button
2218 if (!wxBitmapButton::Create(parent, id, m_bitmap, pos, size,
2219 style | wxBU_AUTODRAW, validator, name)) {
2220 wxFAIL_MSG("OCPNColourPickerCtrl creation failed");
2221 return false;
2222 }
2223
2224 // and handle user clicks on it
2225 Connect(GetId(), wxEVT_BUTTON,
2226 wxCommandEventHandler(OCPNColourPickerCtrl::OnButtonClick), NULL,
2227 this);
2228
2229 m_colour = col;
2230 UpdateColour();
2231 InitColourData();
2232
2233 return true;
2234}
2235
2236void OCPNColourPickerCtrl::InitColourData() {
2237#if 0
2238 ms_data.SetChooseFull(true);
2239 unsigned char grey = 0;
2240 for (int i = 0; i < 16; i++, grey += 16)
2241 {
2242 // fill with grey tones the custom colors palette
2243 wxColour colour(grey, grey, grey);
2244 ms_data.SetCustomColour(i, colour);
2245 }
2246#endif
2247}
2248
2249void OCPNColourPickerCtrl::OnButtonClick(wxCommandEvent &WXUNUSED(ev)) {
2250#ifdef __ANDROID__
2251 unsigned int cco = 0;
2252 cco |= 0xff;
2253 cco = cco << 8;
2254 cco |= m_colour.Red();
2255 cco = cco << 8;
2256 cco |= m_colour.Green();
2257 cco = cco << 8;
2258 cco |= m_colour.Blue();
2259 unsigned int cc = androidColorPicker(cco);
2260
2261 wxColour cnew;
2262 unsigned char blue = (unsigned char)cc % 256;
2263 unsigned char green = (unsigned char)(cc >> 8) % 256;
2264 ;
2265 unsigned char red = (unsigned char)(cc >> 16) % 256;
2266 cnew.Set(red, green, blue);
2267
2268 SetColour(cnew);
2269
2270#else
2271 // update the wxColouData to be shown in the dialog
2272 ms_data.SetColour(m_colour);
2273
2274 // create the colour dialog and display it
2275 wxColourDialog dlg(this, &ms_data);
2276 if (dlg.ShowModal() == wxID_OK) {
2277 ms_data = dlg.GetColourData();
2278 SetColour(ms_data.GetColour());
2279 }
2280#endif
2281}
2282
2283void OCPNColourPickerCtrl::UpdateColour() {
2284#ifndef __ANDROID__
2285 SetBitmapLabel(wxBitmap());
2286#endif
2287
2288 wxMemoryDC dc(m_bitmap);
2289 dc.SetPen(*wxTRANSPARENT_PEN);
2290 dc.SetBrush(wxBrush(m_colour));
2291 dc.DrawRectangle(0, 0, m_bitmap.GetWidth(), m_bitmap.GetHeight());
2292
2293 dc.SelectObject(wxNullBitmap);
2294 SetBitmapLabel(m_bitmap);
2295}
2296
2297void OCPNColourPickerCtrl::SetColour(wxColour &c) {
2298 m_colour = c;
2299 m_bitmap = wxBitmap(GetSize().x - 20, GetSize().y - 20);
2300 UpdateColour();
2301}
2302
2303wxColour OCPNColourPickerCtrl::GetColour() { return m_colour; }
2304
2305wxSize OCPNColourPickerCtrl::DoGetBestSize() const {
2306 wxSize sz(wxBitmapButton::DoGetBestSize());
2307#ifdef __WXMAC__
2308 sz.y += 6;
2309#else
2310 sz.y += 2;
2311#endif
2312 sz.x += 30;
2313 if (HasFlag(wxCLRP_SHOW_LABEL)) return sz;
2314
2315 // if we have no label, then make this button a square
2316 // (like e.g. native GTK version of this control) ???
2317 // sz.SetWidth(sz.GetHeight());
2318 return sz;
2319}
2320
2321void OCPNColourPickerCtrl::OnPaint(wxPaintEvent &event) {
2322 wxPaintDC dc(this);
2323
2324 int offset_x = (GetSize().x - m_bitmap.GetWidth()) / 2;
2325 int offset_y = (GetSize().y - m_bitmap.GetHeight()) / 2;
2326
2327 dc.SetPen(*wxTRANSPARENT_PEN);
2328 dc.SetBrush(wxBrush(m_colour));
2329 dc.DrawRectangle(offset_x, offset_y, m_bitmap.GetWidth(),
2330 m_bitmap.GetHeight());
2331
2332 event.Skip();
2333}
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:181
Options dialog.
Downloaded plugins cache.
Plugin installation and data paths support.
wxString GetWritableDocumentsDir()
Returns the platform-specific default documents directory.
Select * pSelect
Global instance.
Definition select.cpp:36
Select * pSelectTC
Global instance.
Definition select.cpp:37
Selected route, segment, waypoint, etc.
Chart Symbols.
Abstract gFrame/MyFrame interface.