34#include <wx/checkbox.h>
35#include <wx/dcclient.h>
40#include <wx/statline.h>
41#include <wx/stattext.h>
42#include <wx/textctrl.h>
44#include "libgui/expand_panel.h"
54#if !defined(__linux__) || defined(__ANDROID__)
65static bool hide_dongle_dialog;
66static bool hide_device_dialog;
68static const char*
const DONGLE_INTRO = _(R
"(
69An OpenCPN dongle is detected but cannot be used due to missing permissions.
71This problem can be fixed by installing a udev rules file. Once installed,
72it will ensure that the dongle permissions are OK.
75static const char*
const FLATPAK_INTRO_TRAILER = _(R
"(
77On flatpak, this must be done using the manual command instructions below
80static const char*
const DEVICE_INTRO = _(R
"(
81The device @DEVICE@ exists but cannot be used due to missing permissions.
83This problem can be fixed by installing a udev rules file. Once installed,
84the rules file will fix the permissions problem.
87static const char*
const DEVICE_LINK_INTRO = _(R
"(
89It will also create a new device called @SYMLINK@. It is recommended to use
90@SYMLINK@ instead of @DEVICE@ to avoid problems with changing device names,
91in particular on laptops.
94static const char*
const HIDE_DIALOG_LABEL =
95 _(
"Do not show this dialog next time");
97static const char*
const RULE_SUCCESS_TTYS_MSG = _(R
"(
98Rule successfully installed. To activate the new rule restart the system.
101static const char*
const RULE_SUCCESS_MSG = _(R
"(
102Rule successfully installed. To activate the new rule restart system or:
104- Unplug and re-insert the USB device.
108static const char*
const FLATPAK_INSTALL_MSG = _(R
"(
109To do after installing the rule according to instructions:
111- Unplug and re-insert the USB device.
115static const char*
const DEVICE_NOT_FOUND =
116 _(
"The device @device@ can not be found (disconnected?)");
118static const char*
const INSTRUCTIONS =
"@pkexec@ cp @PATH@ /etc/udev/rules.d";
121class DeviceNotFoundDlg :
public wxFrame {
124 static void Create(wxWindow* parent,
const std::string& device) {
125 wxWindow* dlg =
new DeviceNotFoundDlg(parent, device);
130 static void DestroyOpenWindows() {
131 for (
const auto& name : open_windows) {
132 auto window = wxWindow::FindWindowByName(name);
133 if (window) window->Destroy();
135 open_windows.clear();
139 static std::vector<std::string> open_windows;
143 ButtonsSizer(DeviceNotFoundDlg* parent) : wxStdDialogButtonSizer() {
144 auto button =
new wxButton(parent, wxID_OK);
150 DeviceNotFoundDlg(wxWindow* parent,
const std::string& device)
151 : wxFrame(parent, wxID_ANY, _(
"Opencpn: device not found"),
152 wxDefaultPosition, wxDefaultSize,
153 wxDEFAULT_FRAME_STYLE | wxFRAME_FLOAT_ON_PARENT) {
154 std::stringstream ss;
155 ss <<
"dlg-id-" << rand();
157 open_windows.push_back(ss.str());
159 Bind(wxEVT_CLOSE_WINDOW, [&](wxCloseEvent& e) {
163 Bind(wxEVT_COMMAND_BUTTON_CLICKED, [&](wxCommandEvent&) { OnClose(); });
165 auto vbox =
new wxBoxSizer(wxVERTICAL);
167 auto flags = wxSizerFlags().Expand().Border();
168 std::string txt(DEVICE_NOT_FOUND);
171 vbox->Add(
new wxStaticText(
this, wxID_ANY, txt), flags);
173 vbox->Add(
new wxStaticLine(
this), wxSizerFlags().Expand());
181 const std::string name(GetName().ToStdString());
183 std::find_if(open_windows.begin(), open_windows.end(),
184 [name](
const std::string& s) { return s == name; });
185 assert(found != std::end(open_windows) &&
186 "Cannot find dialog in window list");
187 open_windows.erase(found);
192std::vector<std::string> DeviceNotFoundDlg::open_windows;
197class HideCheckbox :
public wxCheckBox {
199 HideCheckbox(wxWindow* parent,
const char* label,
bool* state)
200 : wxCheckBox(parent, wxID_ANY, label, wxDefaultPosition, wxDefaultSize,
205 [&](wxCommandEvent& ev) { *m_state = ev.IsChecked(); });
213class HidePanel :
public wxPanel {
215 HidePanel(wxWindow* parent,
const char* label,
bool* state)
217 auto hbox =
new wxBoxSizer(wxHORIZONTAL);
218 hbox->Add(
new HideCheckbox(
this, label, state), wxSizerFlags().Expand());
228 ManualInstructions(wxWindow* parent,
const char* cmd)
230 Create(GetCmd(parent, cmd));
231 auto flags = wxSizerFlags().Expand();
232 auto hbox =
new wxBoxSizer(wxHORIZONTAL);
234 const char* label = _(
"Manual command line instructions");
235 hbox->Add(
new wxStaticText(
this, wxID_ANY, label), flags);
238 auto vbox =
new wxBoxSizer(wxVERTICAL);
240 flags = flags.Border(wxLEFT);
241 vbox->Add(
GetChild(), flags.ReserveSpaceEvenIfHidden());
249 wxTextCtrl* GetCmd(wxWindow* parent,
const char* tmpl) {
250 std::string cmd(tmpl);
253 ctrl->SetMinSize(parent->GetTextExtent(cmd +
"aaa"));
262 ReviewRule(wxWindow* parent,
const std::string& rule)
264 int from = rule[0] ==
'\n' ? 1 : 0;
265 Create(
new wxStaticText(
this, wxID_ANY, rule.substr(from)));
267 auto flags = wxSizerFlags().Expand();
268 auto hbox =
new wxBoxSizer(wxHORIZONTAL);
269 hbox->Add(
new wxStaticText(
this, wxID_ANY, _(
"Review rule")), flags);
271 auto vbox =
new wxBoxSizer(wxVERTICAL);
273 auto indent = parent->GetTextExtent(
"ABCDE").GetWidth();
274 flags = flags.Border(wxLEFT, indent);
276 vbox->Add(
GetChild(), flags.ReserveSpaceEvenIfHidden());
284static std::string GetRule(
const std::string& path) {
285 std::ifstream input(path.c_str());
286 std::ostringstream buf;
287 buf << input.rdbuf();
290 WARNING_LOG <<
"Cannot open rule file: " << path;
296class DongleInfoPanel :
public wxPanel {
298 DongleInfoPanel(wxWindow* parent) : wxPanel(parent) {
299 std::string cmd(INSTRUCTIONS);
303 auto vbox =
new wxBoxSizer(wxVERTICAL);
304 vbox->Add(
new ManualInstructions(
this, cmd.c_str()));
305 std::string rule_text = GetRule(rule_path);
306 vbox->Add(
new ReviewRule(
this, rule_text.c_str()));
313class DeviceInfoPanel :
public wxPanel {
315 DeviceInfoPanel(wxWindow* parent,
const std::string rule_path)
317 std::string cmd(INSTRUCTIONS);
320 auto vbox =
new wxBoxSizer(wxVERTICAL);
321 vbox->Add(
new ManualInstructions(
this, cmd.c_str()));
322 vbox->Add(
new ReviewRule(
this, GetRule(rule_path)));
329class Buttons :
public wxPanel {
331 Buttons(wxWindow* parent,
const char* rule_path)
332 : wxPanel(parent), m_rule_path(rule_path) {
333 auto sizer =
new wxBoxSizer(wxHORIZONTAL);
334 auto flags = wxSizerFlags().Bottom().Border(wxLEFT);
335 sizer->Add(1, 1, 100, wxEXPAND);
336 auto install =
new wxButton(
this, wxID_ANY, _(
"Install rule"));
337 install->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
338 [&](wxCommandEvent& ev) { DoInstall(); });
339 install->Enable(getenv(
"FLATPAK_ID") == NULL);
340 sizer->Add(install, flags);
341 auto quit =
new wxButton(
this, wxID_EXIT, _(
"Quit"));
342 quit->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [&](wxCommandEvent& ev) {
343 if (getenv(
"FLATPAK_ID")) {
344 auto flags = wxOK | wxICON_INFORMATION;
345 auto msg = FLATPAK_INSTALL_MSG;
346 OCPNMessageBox(
this, msg, _(
"OpenCPN"), flags);
348 dynamic_cast<wxDialog*
>(GetParent())->EndModal(0);
350 sizer->Add(quit, flags);
358 string cmd(INSTRUCTIONS);
361 ifstream f(m_rule_path);
363 string(istreambuf_iterator<char>(f), istreambuf_iterator<char>());
364 int sts = system(cmd.c_str());
365 int flags = wxOK | wxICON_WARNING;
366 const char* msg = _(
"Errors encountered installing rule.");
367 if (WIFEXITED(sts) && WEXITSTATUS(sts) == 0) {
368 if (rule.find(
"ttyS") != std::string::npos) {
369 msg = RULE_SUCCESS_TTYS_MSG;
371 msg = RULE_SUCCESS_MSG;
373 flags = wxOK | wxICON_INFORMATION;
375 OCPNMessageBox(
this, msg, _(
"OpenCPN Info"), flags);
379 std::string m_rule_path;
383class DongleRuleDialog :
public wxDialog {
385 DongleRuleDialog(wxWindow* parent)
386 : wxDialog(parent, wxID_ANY, _(
"Manage dongle udev rule")) {
387 auto sizer =
new wxBoxSizer(wxVERTICAL);
388 auto flags = wxSizerFlags().Expand().Border();
389 std::string intro(DONGLE_INTRO);
390 if (getenv(
"FLATPAK_ID")) {
391 intro += FLATPAK_INTRO_TRAILER;
393 sizer->Add(
new wxStaticText(
this, wxID_ANY, intro), flags);
394 sizer->Add(
new wxStaticLine(
this), flags);
395 sizer->Add(
new DongleInfoPanel(
this), flags);
396 sizer->Add(
new HidePanel(
this, HIDE_DIALOG_LABEL, &hide_dongle_dialog),
398 sizer->Add(
new wxStaticLine(
this), flags);
399 sizer->Add(
new Buttons(
this,
GetDongleRule().c_str()), flags);
407static std::string GetDeviceIntro(
const char* device, std::string
symlink) {
408 std::string intro(DEVICE_INTRO);
410 std::string dev_name(device);
413 intro += DEVICE_LINK_INTRO;
415 if (getenv(
"FLATPAK_ID")) {
416 intro += FLATPAK_INTRO_TRAILER;
419 while (intro.find(
"@SYMLINK@") != std::string::npos) {
422 while (intro.find(
"@DEVICE@") != std::string::npos) {
429class DeviceRuleDialog :
public wxDialog {
431 DeviceRuleDialog(wxWindow* parent,
const char* device_path)
432 : wxDialog(parent, wxID_ANY, _(
"Manage device udev rule")) {
433 auto sizer =
new wxBoxSizer(wxVERTICAL);
434 auto flags = wxSizerFlags().Expand().Border();
437 auto intro = GetDeviceIntro(device_path,
symlink.c_str());
439 sizer->Add(
new wxStaticText(
this, wxID_ANY, intro), flags);
440 sizer->Add(
new wxStaticLine(
this), flags);
441 sizer->Add(
new DeviceInfoPanel(
this, rule_path), flags);
442 sizer->Add(
new HidePanel(
this, HIDE_DIALOG_LABEL, &hide_device_dialog),
444 sizer->Add(
new wxStaticLine(
this), flags);
445 sizer->Add(
new Buttons(
this, rule_path.c_str()), flags);
454 if (hide_device_dialog) {
458 auto& noteman = NotificationManager::GetInstance();
459 std::string msg =
"Device not found: ";
461 noteman.AddNotification(NotificationSeverity::kInformational, msg, 60);
466 auto dialog =
new DeviceRuleDialog(parent, device.c_str());
467 result = dialog->ShowModal();
476 auto dialog =
new DongleRuleDialog(parent);
477 result = dialog->ShowModal();
Non-editable TextCtrl, used like wxStaticText but is copyable.
An ExpandIcon together with a child window.
void Create(wxWindow *child, std::function< void()> on_resize=[] {})
Second part of two step creation.
wxWindow * GetIcon() const
Return the ExpandableIcon reflecting expanded/collapsed state.
wxWindow * GetChild() const
Return the managed window which is shown or hidden.
General purpose GUI support.
std::string MakeUdevLink()
Get next available udev rule base name.
bool IsDevicePermissionsOk(const char *path)
Check device path permissions.
std::string GetDongleRule()
std::string GetDeviceRule(const char *device, const char *symlink)
Get device udev rule.
bool IsDonglePermissionsWrong()
Return true if an existing dongle cannot be accessed.
Implement linux_devices.h – low level udev usb device management (Linux only).
Enhanced logging interface on top of wx/log.h.
bool replace(std::string &str, const std::string &from, const std::string &to)
Perform in place substitution in str, replacing "from" with "to".
bool startswith(const std::string &str, const std::string &prefix)
Return true if s starts with given prefix.
bool exists(const std::string &name)
User notifications manager.
Miscellaneous utilities, many of which string related.
void DestroyDeviceNotFoundDialogs()
Destroy all open "Device not found" dialog windows.
bool CheckDongleAccess(wxWindow *parent)
Runs checks and if required dialogs to make dongle accessible.
bool CheckSerialAccess(wxWindow *parent, const std::string device)
Run checks and possible dialogs to ensure device is accessible.
Access checks for comm devices and dongle.