about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt1
-rw-r--r--src/ipc_dialog.cpp53
-rw-r--r--src/ipc_dialog.h24
-rw-r--r--src/ipc_state.cpp164
-rw-r--r--src/ipc_state.h4
-rw-r--r--src/tracker_config.cpp4
-rw-r--r--src/tracker_config.h1
-rw-r--r--src/tracker_frame.cpp38
-rw-r--r--src/tracker_frame.h29
9 files changed, 281 insertions, 37 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index fd943b3..f46c158 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -50,6 +50,7 @@ add_executable(lingo_ap_tracker
50 "src/logger.cpp" 50 "src/logger.cpp"
51 "src/godot_variant.cpp" 51 "src/godot_variant.cpp"
52 "src/ipc_state.cpp" 52 "src/ipc_state.cpp"
53 "src/ipc_dialog.cpp"
53 "vendor/whereami/whereami.c" 54 "vendor/whereami/whereami.c"
54) 55)
55set_property(TARGET lingo_ap_tracker PROPERTY CXX_STANDARD 20) 56set_property(TARGET lingo_ap_tracker PROPERTY CXX_STANDARD 20)
diff --git a/src/ipc_dialog.cpp b/src/ipc_dialog.cpp new file mode 100644 index 0000000..f17c2d8 --- /dev/null +++ b/src/ipc_dialog.cpp
@@ -0,0 +1,53 @@
1#include "ipc_dialog.h"
2
3#include "tracker_config.h"
4
5constexpr const char* kDefaultIpcAddress = "ws://127.0.0.1:41253";
6
7IpcDialog::IpcDialog() : wxDialog(nullptr, wxID_ANY, "Connect to game") {
8 std::string address_value;
9 if (GetTrackerConfig().ipc_address.empty()) {
10 address_value = kDefaultIpcAddress;
11 } else {
12 address_value = GetTrackerConfig().ipc_address;
13 }
14
15 address_box_ =
16 new wxTextCtrl(this, -1, address_value, wxDefaultPosition, {300, -1});
17
18 wxButton* reset_button = new wxButton(this, -1, "Use Default");
19 reset_button->Bind(wxEVT_BUTTON, &IpcDialog::OnResetClicked, this);
20
21 wxFlexGridSizer* form_sizer = new wxFlexGridSizer(3, 10, 10);
22 form_sizer->Add(
23 new wxStaticText(this, -1, "Address:"),
24 wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT));
25 form_sizer->Add(address_box_, wxSizerFlags().Expand());
26 form_sizer->Add(reset_button);
27
28 wxBoxSizer* top_sizer = new wxBoxSizer(wxVERTICAL);
29 wxStaticText* top_text = new wxStaticText(this, -1, "");
30 top_sizer->Add(top_text, wxSizerFlags().Align(wxALIGN_LEFT).DoubleBorder().Expand());
31 top_sizer->Add(form_sizer, wxSizerFlags().DoubleBorder().Expand());
32 top_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL),
33 wxSizerFlags().Border().Center());
34
35 SetSizer(top_sizer);
36 Layout();
37 Fit();
38
39 int width = top_text->GetClientSize().GetWidth();
40 top_text->SetLabel(
41 "This allows you to connect to a running Lingo game and track non-multiworld "
42 "state, such as the player's position and what panels are solved. Unless "
43 "you are doing something weird, the default value for the address is "
44 "probably correct.");
45 top_text->Wrap(width);
46
47 Fit();
48 Center();
49}
50
51void IpcDialog::OnResetClicked(wxCommandEvent& event) {
52 address_box_->SetValue(kDefaultIpcAddress);
53}
diff --git a/src/ipc_dialog.h b/src/ipc_dialog.h new file mode 100644 index 0000000..1caed01 --- /dev/null +++ b/src/ipc_dialog.h
@@ -0,0 +1,24 @@
1#ifndef IPC_DIALOG_H_F4C5680C
2#define IPC_DIALOG_H_F4C5680C
3
4#include <wx/wxprec.h>
5
6#ifndef WX_PRECOMP
7#include <wx/wx.h>
8#endif
9
10#include <string>
11
12class IpcDialog : public wxDialog {
13 public:
14 IpcDialog();
15
16 std::string GetIpcAddress() { return address_box_->GetValue().ToStdString(); }
17
18 private:
19 void OnResetClicked(wxCommandEvent& event);
20
21 wxTextCtrl* address_box_;
22};
23
24#endif /* end of include guard: IPC_DIALOG_H_F4C5680C */
diff --git a/src/ipc_state.cpp b/src/ipc_state.cpp index c0bdc9b..24d0115 100644 --- a/src/ipc_state.cpp +++ b/src/ipc_state.cpp
@@ -15,18 +15,21 @@
15#include <tuple> 15#include <tuple>
16#include <wswrap.hpp> 16#include <wswrap.hpp>
17 17
18#include "ap_state.h"
18#include "logger.h" 19#include "logger.h"
19#include "tracker_frame.h" 20#include "tracker_frame.h"
20 21
21namespace { 22namespace {
22 23
23constexpr const char* kIpcAddress = "ws://127.0.0.1:41253";
24
25struct IPCState { 24struct IPCState {
26 std::mutex state_mutex; 25 std::mutex state_mutex;
27 TrackerFrame* tracker_frame = nullptr; 26 TrackerFrame* tracker_frame = nullptr;
28 27
29 // Protected state 28 // Protected state
29 bool initialized = false;
30 std::string address;
31 bool should_disconnect = false;
32
30 std::optional<std::string> status_message; 33 std::optional<std::string> status_message;
31 34
32 bool slot_matches = false; 35 bool slot_matches = false;
@@ -38,16 +41,27 @@ struct IPCState {
38 std::optional<std::tuple<int, int>> player_position; 41 std::optional<std::tuple<int, int>> player_position;
39 std::set<std::string> solved_panels; 42 std::set<std::string> solved_panels;
40 43
41 int backoff_amount = 0;
42
43 // Thread state 44 // Thread state
44 std::unique_ptr<wswrap::WS> ws; 45 std::unique_ptr<wswrap::WS> ws;
45 bool connected = false; 46 bool connected = false;
46 47
47 void Start(TrackerFrame* frame) { 48 void SetTrackerFrame(TrackerFrame* frame) { tracker_frame = frame; }
48 tracker_frame = frame; 49
50 void Connect(std::string a) {
51 // This is the main concurrency concern, as it mutates protected state in an
52 // important way. Thread() is documented with how it interacts with this
53 // function.
54 std::lock_guard state_guard(state_mutex);
55
56 if (!initialized) {
57 std::thread([this]() { Thread(); }).detach();
49 58
50 std::thread([this]() { Thread(); }).detach(); 59 initialized = true;
60 } else if (address != a) {
61 should_disconnect = true;
62 }
63
64 address = a;
51 } 65 }
52 66
53 std::optional<std::string> GetStatusMessage() { 67 std::optional<std::string> GetStatusMessage() {
@@ -57,6 +71,13 @@ struct IPCState {
57 } 71 }
58 72
59 void SetTrackerSlot(std::string server, std::string user) { 73 void SetTrackerSlot(std::string server, std::string user) {
74 // This is function is called from the APState thread, not the main thread,
75 // and it mutates protected state. It only really competes with OnMessage(),
76 // when a "Connect" message is received. If this is called right before,
77 // and the tracker slot does not match the old game slot, it will initiate a
78 // disconnect, and then the OnMessage() handler will see should_disconnect
79 // and stop processing the "Connect" message. If this is called right after
80 // and the slot does not match, IPC will disconnect, which is tolerable.
60 std::lock_guard state_guard(state_mutex); 81 std::lock_guard state_guard(state_mutex);
61 82
62 tracker_ap_server = std::move(server); 83 tracker_ap_server = std::move(server);
@@ -64,7 +85,10 @@ struct IPCState {
64 85
65 CheckIfSlotMatches(); 86 CheckIfSlotMatches();
66 87
67 backoff_amount = 0; 88 if (!slot_matches) {
89 should_disconnect = true;
90 address.clear();
91 }
68 } 92 }
69 93
70 bool IsConnected() { 94 bool IsConnected() {
@@ -88,54 +112,126 @@ struct IPCState {
88 private: 112 private:
89 void Thread() { 113 void Thread() {
90 for (;;) { 114 for (;;) {
91 player_position = std::nullopt; 115 SetStatusMessage("Disconnected from game.");
92
93 TrackerLog("Looking for game over IPC...");
94 116
117 // initialized is definitely true because it is set to true when the thread
118 // is created and only set to false within this block, when the thread is
119 // killed. Thus, a call to Connect would always at most set
120 // should_disconnect and address. If this happens before this block, it is
121 // as if we are starting from a new thread anyway because should_disconnect
122 // is immediately reset. If a call to Connect happens after this block,
123 // then a connection attempt will be made to the wrong address, but the
124 // thread will grab the mutex right after this and back out the wrong
125 // connection.
126 std::string ipc_address;
95 { 127 {
96 std::lock_guard state_guard(state_mutex); 128 std::lock_guard state_guard(state_mutex);
97 backoff_amount = 0;
98 }
99 129
100 while (!TryConnect() || !connected) { 130 should_disconnect = false;
101 int backoff_limit; 131
102 { 132 slot_matches = false;
103 std::lock_guard state_guard(state_mutex); 133 game_ap_server.clear();
104 backoff_limit = (backoff_amount + 1) * 10; 134 game_ap_user.clear();
135
136 player_position = std::nullopt;
137 solved_panels.clear();
138
139 if (address.empty()) {
140 initialized = false;
141 return;
105 } 142 }
106 143
144 ipc_address = address;
145 }
146
147 int backoff_amount = 0;
148
149 SetStatusMessage("Connecting to game...");
150 TrackerLog(fmt::format("Looking for game over IPC ({})...", ipc_address));
151
152 while (!TryConnect(ipc_address) || !connected) {
153 int backoff_limit = (backoff_amount + 1) * 10;
154
107 for (int i = 0; i < backoff_limit && !connected; i++) { 155 for (int i = 0; i < backoff_limit && !connected; i++) {
156 // If Connect is called right before this block, we will see and handle
157 // should_disconnect. If it is called right after, we will do one bad
158 // poll, one sleep, and then grab the mutex again right after.
159 {
160 std::lock_guard state_guard(state_mutex);
161 if (should_disconnect) {
162 break;
163 }
164 }
165
108 ws->poll(); 166 ws->poll();
109 167
110 // Back off 168 // Back off
111 std::this_thread::sleep_for(std::chrono::milliseconds(100)); 169 std::this_thread::sleep_for(std::chrono::milliseconds(100));
112 } 170 }
113 171
172 backoff_amount++;
173
174 // If Connect is called right before this block, we will see and handle
175 // should_disconnect. If it is called right after, and the connection
176 // was unsuccessful, we will grab the mutex after one bad connection
177 // attempt. If the connection was successful, we grab the mutex right
178 // after exiting the loop.
114 { 179 {
115 std::lock_guard state_guard(state_mutex); 180 std::lock_guard state_guard(state_mutex);
116 backoff_amount = std::min(9, backoff_amount + 1);
117 181
118 if (!connected) { 182 if (should_disconnect) {
119 TrackerLog(fmt::format("Retrying IPC in {} second(s)...", 183 break;
120 backoff_amount + 1)); 184 } else if (!connected) {
185 if (backoff_amount >= 10) {
186 should_disconnect = true;
187 address.clear();
188
189 TrackerLog("Giving up on IPC.");
190 SetStatusMessage("Disconnected from game.");
191
192 wxMessageBox("Connection to Lingo timed out.",
193 "Connection failed", wxOK | wxICON_ERROR);
194
195 break;
196 } else {
197 TrackerLog(fmt::format("Retrying IPC in {} second(s)...",
198 backoff_amount + 1));
199 }
121 } 200 }
122 } 201 }
123 } 202 }
124 203
204 // Pretty much every lock guard in the thread is the same. We check for
205 // should_disconnect, and if it gets set directly after the block, we do
206 // minimal bad work before checking for it again.
207 {
208 std::lock_guard state_guard(state_mutex);
209 if (should_disconnect) {
210 ws.reset();
211 continue;
212 }
213 }
214
125 while (connected) { 215 while (connected) {
126 ws->poll(); 216 ws->poll();
127 217
128 std::this_thread::sleep_for(std::chrono::milliseconds(100)); 218 std::this_thread::sleep_for(std::chrono::milliseconds(100));
129 }
130 219
131 SetStatusMessage("Disconnected from game."); 220 {
221 std::lock_guard state_guard(state_mutex);
222 if (should_disconnect) {
223 ws.reset();
224 break;
225 }
226 }
227 }
132 } 228 }
133 } 229 }
134 230
135 bool TryConnect() { 231 bool TryConnect(std::string ipc_address) {
136 try { 232 try {
137 ws = std::make_unique<wswrap::WS>( 233 ws = std::make_unique<wswrap::WS>(
138 kIpcAddress, [this]() { OnConnect(); }, [this]() { OnClose(); }, 234 ipc_address, [this]() { OnConnect(); }, [this]() { OnClose(); },
139 [this](const std::string& s) { OnMessage(s); }, 235 [this](const std::string& s) { OnMessage(s); },
140 [this](const std::string& s) { OnError(s); }); 236 [this](const std::string& s) { OnError(s); });
141 return true; 237 return true;
@@ -174,11 +270,19 @@ struct IPCState {
174 270
175 if (msg["cmd"] == "Connect") { 271 if (msg["cmd"] == "Connect") {
176 std::lock_guard state_guard(state_mutex); 272 std::lock_guard state_guard(state_mutex);
273 if (should_disconnect) {
274 return;
275 }
177 276
178 game_ap_server = msg["slot"]["server"]; 277 game_ap_server = msg["slot"]["server"];
179 game_ap_user = msg["slot"]["player"]; 278 game_ap_user = msg["slot"]["player"];
180 279
181 CheckIfSlotMatches(); 280 CheckIfSlotMatches();
281
282 if (!slot_matches) {
283 tracker_frame->ConnectToAp(game_ap_server, game_ap_user,
284 msg["slot"]["password"]);
285 }
182 } else if (msg["cmd"] == "UpdatePosition") { 286 } else if (msg["cmd"] == "UpdatePosition") {
183 std::lock_guard state_guard(state_mutex); 287 std::lock_guard state_guard(state_mutex);
184 288
@@ -207,8 +311,6 @@ struct IPCState {
207 status_message = "Connected to game."; 311 status_message = "Connected to game.";
208 312
209 Sync(); 313 Sync();
210 } else if (tracker_ap_server.empty()) {
211 status_message = std::nullopt;
212 } else if (connected) { 314 } else if (connected) {
213 status_message = "Local game doesn't match AP slot."; 315 status_message = "Local game doesn't match AP slot.";
214 } 316 }
@@ -240,7 +342,11 @@ IPCState& GetState() {
240 342
241} // namespace 343} // namespace
242 344
243void IPC_Start(TrackerFrame* tracker_frame) { GetState().Start(tracker_frame); } 345void IPC_SetTrackerFrame(TrackerFrame* tracker_frame) {
346 GetState().SetTrackerFrame(tracker_frame);
347}
348
349void IPC_Connect(std::string address) { GetState().Connect(address); }
244 350
245std::optional<std::string> IPC_GetStatusMessage() { 351std::optional<std::string> IPC_GetStatusMessage() {
246 return GetState().GetStatusMessage(); 352 return GetState().GetStatusMessage();
diff --git a/src/ipc_state.h b/src/ipc_state.h index a6bdc36..84f3d29 100644 --- a/src/ipc_state.h +++ b/src/ipc_state.h
@@ -8,7 +8,9 @@
8 8
9class TrackerFrame; 9class TrackerFrame;
10 10
11void IPC_Start(TrackerFrame* tracker_frame); 11void IPC_SetTrackerFrame(TrackerFrame* tracker_frame);
12
13void IPC_Connect(std::string address);
12 14
13std::optional<std::string> IPC_GetStatusMessage(); 15std::optional<std::string> IPC_GetStatusMessage();
14 16
diff --git a/src/tracker_config.cpp b/src/tracker_config.cpp index 85164d5..129dbbc 100644 --- a/src/tracker_config.cpp +++ b/src/tracker_config.cpp
@@ -27,6 +27,8 @@ void TrackerConfig::Load() {
27 }); 27 });
28 } 28 }
29 } 29 }
30
31 ipc_address = file["ipc_address"].as<std::string>();
30 } catch (const std::exception&) { 32 } catch (const std::exception&) {
31 // It's fine if the file can't be loaded. 33 // It's fine if the file can't be loaded.
32 } 34 }
@@ -52,6 +54,8 @@ void TrackerConfig::Save() {
52 output["connection_history"].push_back(connection); 54 output["connection_history"].push_back(connection);
53 } 55 }
54 56
57 output["ipc_address"] = ipc_address;
58
55 std::ofstream filewriter(filename_); 59 std::ofstream filewriter(filename_);
56 filewriter << output; 60 filewriter << output;
57} 61}
diff --git a/src/tracker_config.h b/src/tracker_config.h index a1a6c1d..9244b74 100644 --- a/src/tracker_config.h +++ b/src/tracker_config.h
@@ -29,6 +29,7 @@ class TrackerConfig {
29 bool hybrid_areas = false; 29 bool hybrid_areas = false;
30 bool show_hunt_panels = false; 30 bool show_hunt_panels = false;
31 std::deque<ConnectionDetails> connection_history; 31 std::deque<ConnectionDetails> connection_history;
32 std::string ipc_address;
32 33
33 private: 34 private:
34 std::string filename_; 35 std::string filename_;
diff --git a/src/tracker_frame.cpp b/src/tracker_frame.cpp index d0fd5a6..587d87b 100644 --- a/src/tracker_frame.cpp +++ b/src/tracker_frame.cpp
@@ -14,6 +14,7 @@
14#include "achievements_pane.h" 14#include "achievements_pane.h"
15#include "ap_state.h" 15#include "ap_state.h"
16#include "connection_dialog.h" 16#include "connection_dialog.h"
17#include "ipc_dialog.h"
17#include "ipc_state.h" 18#include "ipc_state.h"
18#include "settings_dialog.h" 19#include "settings_dialog.h"
19#include "subway_map.h" 20#include "subway_map.h"
@@ -38,18 +39,20 @@ std::string GetStatusMessage() {
38} // namespace 39} // namespace
39 40
40enum TrackerFrameIds { 41enum TrackerFrameIds {
41 ID_CONNECT = 1, 42 ID_AP_CONNECT = 1,
42 ID_CHECK_FOR_UPDATES = 2, 43 ID_CHECK_FOR_UPDATES = 2,
43 ID_SETTINGS = 3, 44 ID_SETTINGS = 3,
44 ID_ZOOM_IN = 4, 45 ID_ZOOM_IN = 4,
45 ID_ZOOM_OUT = 5, 46 ID_ZOOM_OUT = 5,
46 ID_OPEN_SAVE_FILE = 6, 47 ID_OPEN_SAVE_FILE = 6,
48 ID_IPC_CONNECT = 7,
47}; 49};
48 50
49wxDEFINE_EVENT(STATE_RESET, wxCommandEvent); 51wxDEFINE_EVENT(STATE_RESET, wxCommandEvent);
50wxDEFINE_EVENT(STATE_CHANGED, wxCommandEvent); 52wxDEFINE_EVENT(STATE_CHANGED, wxCommandEvent);
51wxDEFINE_EVENT(STATUS_CHANGED, wxCommandEvent); 53wxDEFINE_EVENT(STATUS_CHANGED, wxCommandEvent);
52wxDEFINE_EVENT(REDRAW_POSITION, wxCommandEvent); 54wxDEFINE_EVENT(REDRAW_POSITION, wxCommandEvent);
55wxDEFINE_EVENT(CONNECT_TO_AP, ApConnectEvent);
53 56
54TrackerFrame::TrackerFrame() 57TrackerFrame::TrackerFrame()
55 : wxFrame(nullptr, wxID_ANY, "Lingo Archipelago Tracker", wxDefaultPosition, 58 : wxFrame(nullptr, wxID_ANY, "Lingo Archipelago Tracker", wxDefaultPosition,
@@ -57,9 +60,11 @@ TrackerFrame::TrackerFrame()
57 ::wxInitAllImageHandlers(); 60 ::wxInitAllImageHandlers();
58 61
59 AP_SetTrackerFrame(this); 62 AP_SetTrackerFrame(this);
63 IPC_SetTrackerFrame(this);
60 64
61 wxMenu *menuFile = new wxMenu(); 65 wxMenu *menuFile = new wxMenu();
62 menuFile->Append(ID_CONNECT, "&Connect"); 66 menuFile->Append(ID_AP_CONNECT, "&Connect to Archipelago");
67 menuFile->Append(ID_IPC_CONNECT, "&Connect to Lingo");
63 menuFile->Append(ID_OPEN_SAVE_FILE, "&Open Save Data\tCtrl-O"); 68 menuFile->Append(ID_OPEN_SAVE_FILE, "&Open Save Data\tCtrl-O");
64 menuFile->Append(ID_SETTINGS, "&Settings"); 69 menuFile->Append(ID_SETTINGS, "&Settings");
65 menuFile->Append(wxID_EXIT); 70 menuFile->Append(wxID_EXIT);
@@ -86,7 +91,8 @@ TrackerFrame::TrackerFrame()
86 91
87 Bind(wxEVT_MENU, &TrackerFrame::OnAbout, this, wxID_ABOUT); 92 Bind(wxEVT_MENU, &TrackerFrame::OnAbout, this, wxID_ABOUT);
88 Bind(wxEVT_MENU, &TrackerFrame::OnExit, this, wxID_EXIT); 93 Bind(wxEVT_MENU, &TrackerFrame::OnExit, this, wxID_EXIT);
89 Bind(wxEVT_MENU, &TrackerFrame::OnConnect, this, ID_CONNECT); 94 Bind(wxEVT_MENU, &TrackerFrame::OnApConnect, this, ID_AP_CONNECT);
95 Bind(wxEVT_MENU, &TrackerFrame::OnIpcConnect, this, ID_IPC_CONNECT);
90 Bind(wxEVT_MENU, &TrackerFrame::OnSettings, this, ID_SETTINGS); 96 Bind(wxEVT_MENU, &TrackerFrame::OnSettings, this, ID_SETTINGS);
91 Bind(wxEVT_MENU, &TrackerFrame::OnCheckForUpdates, this, 97 Bind(wxEVT_MENU, &TrackerFrame::OnCheckForUpdates, this,
92 ID_CHECK_FOR_UPDATES); 98 ID_CHECK_FOR_UPDATES);
@@ -98,6 +104,7 @@ TrackerFrame::TrackerFrame()
98 Bind(STATE_CHANGED, &TrackerFrame::OnStateChanged, this); 104 Bind(STATE_CHANGED, &TrackerFrame::OnStateChanged, this);
99 Bind(STATUS_CHANGED, &TrackerFrame::OnStatusChanged, this); 105 Bind(STATUS_CHANGED, &TrackerFrame::OnStatusChanged, this);
100 Bind(REDRAW_POSITION, &TrackerFrame::OnRedrawPosition, this); 106 Bind(REDRAW_POSITION, &TrackerFrame::OnRedrawPosition, this);
107 Bind(CONNECT_TO_AP, &TrackerFrame::OnConnectToAp, this);
101 108
102 wxChoicebook *choicebook = new wxChoicebook(this, wxID_ANY); 109 wxChoicebook *choicebook = new wxChoicebook(this, wxID_ANY);
103 achievements_pane_ = new AchievementsPane(choicebook); 110 achievements_pane_ = new AchievementsPane(choicebook);
@@ -134,11 +141,15 @@ TrackerFrame::TrackerFrame()
134 CheckForUpdates(/*manual=*/false); 141 CheckForUpdates(/*manual=*/false);
135 } 142 }
136 143
137 IPC_Start(this);
138
139 SetStatusText(GetStatusMessage()); 144 SetStatusText(GetStatusMessage());
140} 145}
141 146
147void TrackerFrame::ConnectToAp(std::string server, std::string user,
148 std::string pass) {
149 QueueEvent(new ApConnectEvent(CONNECT_TO_AP, GetId(), std::move(server),
150 std::move(user), std::move(pass)));
151}
152
142void TrackerFrame::UpdateStatusMessage() { 153void TrackerFrame::UpdateStatusMessage() {
143 QueueEvent(new wxCommandEvent(STATUS_CHANGED)); 154 QueueEvent(new wxCommandEvent(STATUS_CHANGED));
144} 155}
@@ -172,7 +183,7 @@ void TrackerFrame::OnAbout(wxCommandEvent &event) {
172 183
173void TrackerFrame::OnExit(wxCommandEvent &event) { Close(true); } 184void TrackerFrame::OnExit(wxCommandEvent &event) { Close(true); }
174 185
175void TrackerFrame::OnConnect(wxCommandEvent &event) { 186void TrackerFrame::OnApConnect(wxCommandEvent &event) {
176 ConnectionDialog dlg; 187 ConnectionDialog dlg;
177 188
178 if (dlg.ShowModal() == wxID_OK) { 189 if (dlg.ShowModal() == wxID_OK) {
@@ -202,6 +213,17 @@ void TrackerFrame::OnConnect(wxCommandEvent &event) {
202 } 213 }
203} 214}
204 215
216void TrackerFrame::OnIpcConnect(wxCommandEvent &event) {
217 IpcDialog dlg;
218
219 if (dlg.ShowModal() == wxID_OK) {
220 GetTrackerConfig().ipc_address = dlg.GetIpcAddress();
221 GetTrackerConfig().Save();
222
223 IPC_Connect(dlg.GetIpcAddress());
224 }
225}
226
205void TrackerFrame::OnSettings(wxCommandEvent &event) { 227void TrackerFrame::OnSettings(wxCommandEvent &event) {
206 SettingsDialog dlg; 228 SettingsDialog dlg;
207 229
@@ -306,6 +328,10 @@ void TrackerFrame::OnRedrawPosition(wxCommandEvent &event) {
306 } 328 }
307} 329}
308 330
331void TrackerFrame::OnConnectToAp(ApConnectEvent &event) {
332 AP_Connect(event.GetServer(), event.GetUser(), event.GetPass());
333}
334
309void TrackerFrame::CheckForUpdates(bool manual) { 335void TrackerFrame::CheckForUpdates(bool manual) {
310 wxWebRequest request = wxWebSession::GetDefault().CreateRequest( 336 wxWebRequest request = wxWebSession::GetDefault().CreateRequest(
311 this, "https://code.fourisland.com/lingo-ap-tracker/plain/VERSION"); 337 this, "https://code.fourisland.com/lingo-ap-tracker/plain/VERSION");
diff --git a/src/tracker_frame.h b/src/tracker_frame.h index 9d18dc7..e9fec17 100644 --- a/src/tracker_frame.h +++ b/src/tracker_frame.h
@@ -13,10 +13,34 @@ class TrackerPanel;
13class wxBookCtrlEvent; 13class wxBookCtrlEvent;
14class wxNotebook; 14class wxNotebook;
15 15
16class ApConnectEvent : public wxEvent {
17 public:
18 ApConnectEvent(wxEventType eventType, int winid, std::string server,
19 std::string user, std::string pass)
20 : wxEvent(winid, eventType),
21 ap_server_(std::move(server)),
22 ap_user_(std::move(user)),
23 ap_pass_(std::move(pass)) {}
24
25 const std::string &GetServer() const { return ap_server_; }
26
27 const std::string &GetUser() const { return ap_user_; }
28
29 const std::string &GetPass() const { return ap_pass_; }
30
31 virtual wxEvent *Clone() const { return new ApConnectEvent(*this); }
32
33 private:
34 std::string ap_server_;
35 std::string ap_user_;
36 std::string ap_pass_;
37};
38
16wxDECLARE_EVENT(STATE_RESET, wxCommandEvent); 39wxDECLARE_EVENT(STATE_RESET, wxCommandEvent);
17wxDECLARE_EVENT(STATE_CHANGED, wxCommandEvent); 40wxDECLARE_EVENT(STATE_CHANGED, wxCommandEvent);
18wxDECLARE_EVENT(STATUS_CHANGED, wxCommandEvent); 41wxDECLARE_EVENT(STATUS_CHANGED, wxCommandEvent);
19wxDECLARE_EVENT(REDRAW_POSITION, wxCommandEvent); 42wxDECLARE_EVENT(REDRAW_POSITION, wxCommandEvent);
43wxDECLARE_EVENT(CONNECT_TO_AP, ApConnectEvent);
20 44
21enum UpdateIndicatorsMode { 45enum UpdateIndicatorsMode {
22 kUPDATE_ALL_INDICATORS = 0, 46 kUPDATE_ALL_INDICATORS = 0,
@@ -27,6 +51,7 @@ class TrackerFrame : public wxFrame {
27 public: 51 public:
28 TrackerFrame(); 52 TrackerFrame();
29 53
54 void ConnectToAp(std::string server, std::string user, std::string pass);
30 void UpdateStatusMessage(); 55 void UpdateStatusMessage();
31 56
32 void ResetIndicators(); 57 void ResetIndicators();
@@ -36,7 +61,8 @@ class TrackerFrame : public wxFrame {
36 private: 61 private:
37 void OnExit(wxCommandEvent &event); 62 void OnExit(wxCommandEvent &event);
38 void OnAbout(wxCommandEvent &event); 63 void OnAbout(wxCommandEvent &event);
39 void OnConnect(wxCommandEvent &event); 64 void OnApConnect(wxCommandEvent &event);
65 void OnIpcConnect(wxCommandEvent &event);
40 void OnSettings(wxCommandEvent &event); 66 void OnSettings(wxCommandEvent &event);
41 void OnCheckForUpdates(wxCommandEvent &event); 67 void OnCheckForUpdates(wxCommandEvent &event);
42 void OnZoomIn(wxCommandEvent &event); 68 void OnZoomIn(wxCommandEvent &event);
@@ -48,6 +74,7 @@ class TrackerFrame : public wxFrame {
48 void OnStateChanged(wxCommandEvent &event); 74 void OnStateChanged(wxCommandEvent &event);
49 void OnStatusChanged(wxCommandEvent &event); 75 void OnStatusChanged(wxCommandEvent &event);
50 void OnRedrawPosition(wxCommandEvent &event); 76 void OnRedrawPosition(wxCommandEvent &event);
77 void OnConnectToAp(ApConnectEvent &event);
51 78
52 void CheckForUpdates(bool manual); 79 void CheckForUpdates(bool manual);
53 80