| 48 static int g_pan = 0; |
48 static int g_pan = 0; |
| 49 static bool g_needRefresh = false; |
49 static bool g_needRefresh = false; |
| 50 static bool g_needStatusBarRender = false; |
50 static bool g_needStatusBarRender = false; |
| 51 static bool g_needInputRender = false; |
51 static bool g_needInputRender = false; |
| 52 static bool g_needOutputRender = false; |
52 static bool g_needOutputRender = false; |
| 53 static String g_statusBarText; |
|
| 54 static struct { char ch; int x; } g_cursorChar; |
53 static struct { char ch; int x; } g_cursorChar; |
| 55 static Vector<String> g_output = {""}; |
54 static Vector<String> g_output = {""}; |
| 56 static int g_outputScroll = 0; |
55 static int g_outputScroll = 0; |
| 57 static String g_title; |
56 static String g_title; |
| 58 static InputState g_inputState = INPUTSTATE_NORMAL; |
57 static InputState g_inputState = INPUTSTATE_NORMAL; |
| |
58 static Function<void (void)> g_disconnectConfirmFunction = nullptr; |
| 59 static IPAddress g_address; |
59 static IPAddress g_address; |
| |
60 static String g_statusBarText; |
| 60 |
61 |
| 61 // ------------------------------------------------------------------------------------------------- |
62 // ------------------------------------------------------------------------------------------------- |
| 62 // |
63 // |
| 63 static FUNCTION |
64 static FUNCTION |
| 64 interface_color_pair (Color fg, Color bg) -> int |
65 interface_color_pair (Color fg, Color bg) -> int |
| 80 case INPUTSTATE_PASSWORD: prompt = "password:"; break; |
81 case INPUTSTATE_PASSWORD: prompt = "password:"; break; |
| 81 case INPUTSTATE_CONFIRM_DISCONNECTION: break; |
82 case INPUTSTATE_CONFIRM_DISCONNECTION: break; |
| 82 } |
83 } |
| 83 |
84 |
| 84 return prompt; |
85 return prompt; |
| |
86 } |
| |
87 |
| |
88 // ------------------------------------------------------------------------------------------------- |
| |
89 // |
| |
90 static FUNCTION |
| |
91 set_input_state (InputState newstate) -> void |
| |
92 { |
| |
93 // Clear the input row (unless going to or from confirm state) |
| |
94 if (newstate != INPUTSTATE_CONFIRM_DISCONNECTION |
| |
95 and g_inputState != INPUTSTATE_CONFIRM_DISCONNECTION) |
| |
96 { |
| |
97 g_input.clear(); |
| |
98 } |
| |
99 |
| |
100 switch (newstate) |
| |
101 { |
| |
102 case INPUTSTATE_ADDRESS: |
| |
103 if (g_address.host != 0) |
| |
104 g_input = g_address.to_string (IP_WITH_PORT); |
| |
105 break; |
| |
106 |
| |
107 default: |
| |
108 break; |
| |
109 } |
| |
110 |
| |
111 g_inputState = newstate; |
| |
112 g_needInputRender = true; |
| 85 } |
113 } |
| 86 |
114 |
| 87 // ------------------------------------------------------------------------------------------------- |
115 // ------------------------------------------------------------------------------------------------- |
| 88 // |
116 // |
| 89 FUNCTION |
117 FUNCTION |
| 149 } |
170 } |
| 150 |
171 |
| 151 // ------------------------------------------------------------------------------------------------- |
172 // ------------------------------------------------------------------------------------------------- |
| 152 // |
173 // |
| 153 static FUNCTION |
174 static FUNCTION |
| |
175 safe_disconnect (Function<void()> afterwards) -> void |
| |
176 { |
| |
177 if (RCONSession::get_session() != nullptr |
| |
178 and RCONSession::get_session()->state() != RCON_DISCONNECTED) |
| |
179 { |
| |
180 g_disconnectConfirmFunction = afterwards; |
| |
181 set_input_state (INPUTSTATE_CONFIRM_DISCONNECTION); |
| |
182 } |
| |
183 else |
| |
184 afterwards(); |
| |
185 } |
| |
186 |
| |
187 // ------------------------------------------------------------------------------------------------- |
| |
188 // |
| |
189 static FUNCTION |
| 154 interface_render_output() -> void |
190 interface_render_output() -> void |
| 155 { |
191 { |
| 156 int height = LINES - 3; |
192 int height = LINES - 3; |
| 157 |
193 |
| 158 // ensure we're within bounds |
194 // ensure we're within bounds |
| 231 // ------------------------------------------------------------------------------------------------- |
267 // ------------------------------------------------------------------------------------------------- |
| 232 // |
268 // |
| 233 static FUNCTION |
269 static FUNCTION |
| 234 interface_render_statusbar() -> void |
270 interface_render_statusbar() -> void |
| 235 { |
271 { |
| |
272 int color = interface_color_pair (WHITE, BLUE); |
| 236 int y = LINES - 1; |
273 int y = LINES - 1; |
| |
274 attron (color); |
| 237 mvhline (y, 0, ' ', COLS); |
275 mvhline (y, 0, ' ', COLS); |
| 238 mvprintw (y, 0, "%s", g_statusBarText.chars()); |
276 mvprintw (y, 0, "%s", g_statusBarText.chars()); |
| |
277 attroff (color); |
| 239 g_needRefresh = true; |
278 g_needRefresh = true; |
| 240 g_needStatusBarRender = false; |
279 g_needStatusBarRender = false; |
| 241 } |
280 } |
| 242 |
281 |
| 243 // ------------------------------------------------------------------------------------------------- |
282 // ------------------------------------------------------------------------------------------------- |
| 244 // |
283 // |
| 245 static FUNCTION |
284 FUNCTION |
| 246 set_statusbar_text (const String& a) -> void |
285 Interface::update_statusbar() -> void |
| 247 { |
286 { |
| 248 g_statusBarText = a; |
287 String text; |
| 249 g_needStatusBarRender = true; |
288 RCONSession* session = RCONSession::get_session(); |
| |
289 |
| |
290 if (session == nullptr) |
| |
291 { |
| |
292 text = "[DISCONNECTED]"; |
| |
293 } |
| |
294 else |
| |
295 { |
| |
296 switch (session->state()) |
| |
297 { |
| |
298 case RCON_DISCONNECTED: |
| |
299 text = "[DISCONNECTED]"; |
| |
300 break; |
| |
301 |
| |
302 case RCON_CONNECTING: |
| |
303 case RCON_AUTHENTICATING: |
| |
304 text = "Connecting to " + session->address().to_string (IP_WITH_PORT) + "..."; |
| |
305 break; |
| |
306 |
| |
307 case RCON_CONNECTED: |
| |
308 { |
| |
309 String adminText; |
| |
310 |
| |
311 if (session->num_admins() == 0) |
| |
312 adminText = "No other admins"; |
| |
313 else |
| |
314 adminText = format ("%1 other admin%s1", session->num_admins()); |
| |
315 |
| |
316 text = format ("%1 | %2 | %3", session->address().to_string (IP_WITH_PORT), |
| |
317 session->level(), adminText); |
| |
318 } |
| |
319 break; |
| |
320 } |
| |
321 } |
| |
322 |
| |
323 if (text != g_statusBarText) |
| |
324 { |
| |
325 g_statusBarText = text; |
| |
326 g_needStatusBarRender = true; |
| |
327 } |
| 250 } |
328 } |
| 251 |
329 |
| 252 // ------------------------------------------------------------------------------------------------- |
330 // ------------------------------------------------------------------------------------------------- |
| 253 // |
331 // |
| 254 FUNCTION |
332 FUNCTION |
| 255 Interface::render_full() -> void |
333 Interface::render_full() -> void |
| 256 { |
334 { |
| |
335 update_statusbar(); |
| 257 interface_render_titlebar(); |
336 interface_render_titlebar(); |
| 258 interface_render_output(); |
337 interface_render_output(); |
| 259 interface_render_statusbar(); |
338 interface_render_statusbar(); |
| 260 interface_render_input(); |
339 interface_render_input(); |
| 261 } |
340 } |
| 277 mvprintw (y, interface_prompt_string().length(), " "); |
356 mvprintw (y, interface_prompt_string().length(), " "); |
| 278 } |
357 } |
| 279 |
358 |
| 280 // ------------------------------------------------------------------------------------------------- |
359 // ------------------------------------------------------------------------------------------------- |
| 281 // |
360 // |
| 282 static FUNCTION |
|
| 283 set_input_state (InputState newstate) -> void |
|
| 284 { |
|
| 285 // Clear the input row (unless going to or from confirm state) |
|
| 286 if (newstate != INPUTSTATE_CONFIRM_DISCONNECTION |
|
| 287 and g_inputState != INPUTSTATE_CONFIRM_DISCONNECTION) |
|
| 288 { |
|
| 289 g_input.clear(); |
|
| 290 } |
|
| 291 |
|
| 292 switch (newstate) |
|
| 293 { |
|
| 294 case INPUTSTATE_ADDRESS: |
|
| 295 if (g_address.host != 0) |
|
| 296 g_input = g_address.to_string (IP_WITH_PORT); |
|
| 297 break; |
|
| 298 |
|
| 299 default: |
|
| 300 break; |
|
| 301 } |
|
| 302 |
|
| 303 g_inputState = newstate; |
|
| 304 g_needInputRender = true; |
|
| 305 } |
|
| 306 |
|
| 307 // ------------------------------------------------------------------------------------------------- |
|
| 308 // |
|
| 309 FUNCTION |
361 FUNCTION |
| 310 Interface::handle_input() -> void |
362 Interface::handle_input() -> void |
| 311 { |
363 { |
| 312 int ch = ::getch(); |
364 int ch = ::getch(); |
| 313 set_statusbar_text (String::from_number (ch)); |
|
| 314 |
365 |
| 315 if (g_inputState == INPUTSTATE_CONFIRM_DISCONNECTION) |
366 if (g_inputState == INPUTSTATE_CONFIRM_DISCONNECTION) |
| 316 { |
367 { |
| 317 if (ch == 'y' or ch == 'Y') |
368 if (ch == 'y' or ch == 'Y') |
| 318 set_input_state (INPUTSTATE_ADDRESS); |
369 g_disconnectConfirmFunction(); |
| 319 else if (ch == 'n' or ch == 'N') |
370 else if (ch == 'n' or ch == 'N') |
| 320 set_input_state (INPUTSTATE_NORMAL); |
371 set_input_state (INPUTSTATE_NORMAL); |
| 321 return; |
372 return; |
| 322 } |
373 } |
| 323 |
374 |
| 326 g_input.insert (g_cursor++, char (ch)); |
377 g_input.insert (g_cursor++, char (ch)); |
| 327 g_needInputRender = true; |
378 g_needInputRender = true; |
| 328 } |
379 } |
| 329 else switch (ch) |
380 else switch (ch) |
| 330 { |
381 { |
| 331 case KEY_F(1): |
382 case 'Q' - 'A' + 1: // ^Q |
| 332 endwin(); |
383 switch (g_inputState) |
| 333 exit (EXIT_SUCCESS); |
384 { |
| |
385 case INPUTSTATE_CONFIRM_DISCONNECTION: |
| |
386 break; |
| |
387 |
| |
388 case INPUTSTATE_NORMAL: |
| |
389 safe_disconnect ([]() |
| |
390 { |
| |
391 endwin(); |
| |
392 throw Exitception(); |
| |
393 }); |
| |
394 break; |
| |
395 |
| |
396 case INPUTSTATE_PASSWORD: |
| |
397 set_input_state (INPUTSTATE_ADDRESS); |
| |
398 break; |
| |
399 |
| |
400 case INPUTSTATE_ADDRESS: |
| |
401 set_input_state (INPUTSTATE_NORMAL); |
| |
402 } |
| |
403 break; |
| |
404 |
| |
405 case '\e': |
| |
406 if (g_inputState == INPUTSTATE_PASSWORD) |
| |
407 set_input_state (INPUTSTATE_ADDRESS); |
| |
408 else if (g_inputState == INPUTSTATE_ADDRESS) |
| |
409 set_input_state (INPUTSTATE_NORMAL); |
| 334 break; |
410 break; |
| 335 |
411 |
| 336 case KEY_LEFT: |
412 case KEY_LEFT: |
| 337 if (g_cursor > 0) |
413 if (g_cursor > 0) |
| 338 { |
414 { |
| 436 } |
512 } |
| 437 break; |
513 break; |
| 438 |
514 |
| 439 case 'N' - 'A' + 1: // ^N |
515 case 'N' - 'A' + 1: // ^N |
| 440 if (g_inputState == INPUTSTATE_NORMAL) |
516 if (g_inputState == INPUTSTATE_NORMAL) |
| 441 { |
517 safe_disconnect ([]() {set_input_state (INPUTSTATE_ADDRESS);}); |
| 442 if (RCONSession::get_session() != nullptr |
|
| 443 and RCONSession::get_session()->state() != RCON_DISCONNECTED) |
|
| 444 { |
|
| 445 set_input_state (INPUTSTATE_CONFIRM_DISCONNECTION); |
|
| 446 } |
|
| 447 else |
|
| 448 { |
|
| 449 set_input_state (INPUTSTATE_ADDRESS); |
|
| 450 } |
|
| 451 } |
|
| 452 break; |
518 break; |
| 453 } |
519 } |
| 454 } |
520 } |
| 455 |
521 |
| 456 // ------------------------------------------------------------------------------------------------- |
522 // ------------------------------------------------------------------------------------------------- |