OpenCPN Partial API docs
instance_handler.cpp
1 /***************************************************************************
2  * Copyright (C) 2023 Alec Leamas *
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, write to the *
16  * Free Software Foundation, Inc., *
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
18  ***************************************************************************/
19 
20 #include <wx/filename.h>
21 #include <wx/ipc.h>
22 #include <wx/log.h>
23 #include <wx/snglinst.h>
24 #include <wx/string.h>
25 
26 #include "model/base_platform.h"
27 #include "model/nav_object_database.h"
28 
29 #include "bbox.h"
30 
31 class StConnection : public wxConnection {
32 public:
33  StConnection() {}
34  ~StConnection() {}
35  bool OnExec(const wxString &topic, const wxString &data);
36 
38  std::function<void()> update_route_mgr_dlg;
39  // if (pRouteManagerDialog && pRouteManagerDialog->IsShown())
40  // pRouteManagerDialog->UpdateLists();
41 
43  std::function<void(LLBBox)> gframe_center_view;
44  // gFrame->CenterView(gFrame->GetPrimaryCanvas(), box);
45 
47  std::function<void()> raise;
48  // gFrame->InvalidateAllGL();
49  // gFrame->RefreshAllCanvas(false);
50  // gFrame->Raise();
51 };
52 
53 // Client class, to be used by subsequent instances in OnInit
54 class StClient : public wxClient {
55 public:
56  StClient(){};
57  wxConnectionBase *OnMakeConnection() { return new StConnection; }
58 };
59 
60 
61 // Opens a file passed from another instance
62 bool StConnection::OnExec(const wxString &topic, const wxString &data) {
63  // not setup yet
64  // if (!gFrame) return false;
65 
66  wxString path(data);
67  if (path.IsEmpty()) {
68  raise();
69  } else {
71  pSet->load_file(path.fn_str());
72  int wpt_dups;
73  // Import with full vizibility of names and objects
74  pSet->LoadAllGPXObjects(!pSet->IsOpenCPN(), wpt_dups, true);
76  LLBBox box = pSet->GetBBox();
77  if (box.GetValid()) {
78  gframe_center_view(box);
79  }
80  delete pSet;
81  return true;
82  }
83  return true;
84 }
85 
86 // Server class, for listening to connection requests
87 class StServer : public wxServer {
88 public:
89  wxConnectionBase *OnAcceptConnection(const wxString &topic);
90 };
91 
93 static bool IsToplevelModal() {
94  for (auto w = wxTopLevelWindows.GetFirst(); w; w = w->GetNext()) {
95  wxDialog *dlg = dynamic_cast<wxDialog*>(w->GetData());
96  if (dlg && dlg->IsModal()) {
97  return true;
98  }
99  }
100  return false;
101 }
102 
103 // Accepts a connection from another instance
104 wxConnectionBase *StServer::OnAcceptConnection(const wxString& topic) {
105  if (topic.Lower() == "opencpn" && !IsToplevelModal()) {
106  return new StConnection();
107  }
108  return 0;
109 }
110 
111 
112 
113 class InstanceHandler : public wxSingleInstanceChecker {
114 public:
115  bool Init(const std::vector<std::string>& params) {
116  if (wxSingleInstanceChecker::IsAnotherRunning()) {
117  wxChar separator = wxFileName::GetPathSeparator();
118  wxString service_name =
119  g_BasePlatform->GetPrivateDataDir() + separator + _T("opencpn-ipc");
120 
121  auto checker = new wxSingleInstanceChecker(_T("_OpenCPN_SILock"),
122  g_BasePlatform->GetPrivateDataDir());
123  if (!checker->IsAnotherRunning()) {
124  StServer *m_server = new StServer;
125  if (!m_server->Create(service_name)) {
126  wxLogDebug(wxT("Failed to create an IPC service."));
127  return false;
128  }
129  } else {
130  wxLogNull logNull;
131  StClient *client = new StClient;
132  // ignored under DDE, host name in TCP/IP based classes
133  wxString hostName = wxT("localhost");
134  // Create the connection service, topic
135  wxConnectionBase *connection =
136  client->MakeConnection(hostName, service_name, _T("OpenCPN"));
137  if (connection) {
138  // Ask the other instance to open a file or raise itself
139  if (params.empty()) {
140  for (size_t n = 0; n < params.size(); n++) {
141  wxString path(params[n]);
142  if (::wxFileExists(path)) {
143  connection->Execute(path);
144  }
145  }
146  }
147  connection->Execute(wxT(""));
148  connection->Disconnect();
149  delete connection;
150  } else {
151  // If we get here, it means that the wxWidgets single-instance-detect
152  // logic found the lock file, And so thinks another instance is
153  // running. But that instance is not reachable, for some reason. So,
154  // the safe thing to do is delete the lockfile, and exit. Next start
155  // will proceed normally. This may leave a zombie OpenCPN, but at
156  // least O starts.
157  wxString lockFile = wxString(g_BasePlatform->GetPrivateDataDir() +
158  separator + _T("_OpenCPN_SILock"));
159  if (wxFileExists(lockFile)) wxRemoveFile(lockFile);
160 
161  wxMessageBox(
162  _("Sorry, an existing instance of OpenCPN may be too busy "
163  "to respond.\nPlease retry."),
164  wxT("OpenCPN"), wxICON_INFORMATION | wxOK);
165  }
166  delete client;
167  return false; // exit quietly
168  }
169  }
170  return true;
171  }
172 };
wxString & GetPrivateDataDir()
Return dir path for opencpn.log, etc., respecting -c cli option.
std::function< void(LLBBox)> gframe_center_view
Center global view to a given box callback.
std::function< void()> update_route_mgr_dlg
Update RouteManagerDialog callback.