sources/interface.cpp

changeset 31
b5b5a6a96d91
parent 30
21fba5183768
child 32
ee770597a281
equal deleted inserted replaced
30:21fba5183768 31:b5b5a6a96d91
41 INPUTSTATE_ADDRESS, 41 INPUTSTATE_ADDRESS,
42 INPUTSTATE_PASSWORD, 42 INPUTSTATE_PASSWORD,
43 INPUTSTATE_CONFIRM_DISCONNECTION, 43 INPUTSTATE_CONFIRM_DISCONNECTION,
44 }; 44 };
45 45
46 static String g_input; 46 static StringList g_input;
47 static int g_inputCursor = 0;
47 static int g_cursor = 0; 48 static int g_cursor = 0;
48 static int g_pan = 0; 49 static int g_pan = 0;
49 static bool g_needRefresh = false; 50 static bool g_needRefresh = false;
50 static bool g_needStatusBarRender = false; 51 static bool g_needStatusBarRender = false;
51 static bool g_needInputRender = false; 52 static bool g_needInputRender = false;
68 } 69 }
69 70
70 // ------------------------------------------------------------------------------------------------- 71 // -------------------------------------------------------------------------------------------------
71 // 72 //
72 static FUNCTION 73 static FUNCTION
74 current_input() -> const String&
75 {
76 return g_input[g_inputCursor];
77 }
78
79 // -------------------------------------------------------------------------------------------------
80 //
81 // Makes current_input() the lastmost input (so that we won't modify history)
82 //
83 static FUNCTION
84 detach_input() -> void
85 {
86 if (g_inputCursor > 0)
87 {
88 g_input[0] = current_input();
89 g_inputCursor = 0;
90 }
91 }
92
93 // -------------------------------------------------------------------------------------------------
94 // A version of current_input() that allows changing the contents of it.
95 //
96 static FUNCTION
97 mutable_current_input() -> String&
98 {
99 detach_input();
100 return g_input[g_inputCursor];
101 }
102
103 // -------------------------------------------------------------------------------------------------
104 //
105 static FUNCTION
106 move_input_cursor (int delta) -> void
107 {
108 // No input history when inputting addresses or passwords
109 if (g_inputState != INPUTSTATE_NORMAL)
110 {
111 g_inputCursor = 0;
112 return;
113 }
114
115 int oldcursor = g_inputCursor;
116 g_inputCursor = clamp (g_inputCursor + delta, 0, g_input.size() - 1);
117
118 if (g_inputCursor != oldcursor)
119 {
120 g_cursor = current_input().length();
121 g_needInputRender = true;
122 }
123 }
124
125 // -------------------------------------------------------------------------------------------------
126 //
127 static FUNCTION
73 interface_prompt_string() -> String 128 interface_prompt_string() -> String
74 { 129 {
75 String prompt; 130 String prompt;
76 131
77 switch (g_inputState) 132 switch (g_inputState)
92 { 147 {
93 // Clear the input row (unless going to or from confirm state) 148 // Clear the input row (unless going to or from confirm state)
94 if (newstate != INPUTSTATE_CONFIRM_DISCONNECTION 149 if (newstate != INPUTSTATE_CONFIRM_DISCONNECTION
95 and g_inputState != INPUTSTATE_CONFIRM_DISCONNECTION) 150 and g_inputState != INPUTSTATE_CONFIRM_DISCONNECTION)
96 { 151 {
97 g_input.clear(); 152 g_inputCursor = 0;
153 mutable_current_input().clear();
98 } 154 }
99 155
100 switch (newstate) 156 switch (newstate)
101 { 157 {
102 case INPUTSTATE_ADDRESS: 158 case INPUTSTATE_ADDRESS:
103 if (g_address.host != 0) 159 if (g_address.host != 0)
104 g_input = g_address.to_string (IP_WITH_PORT); 160 mutable_current_input() = g_address.to_string (IP_WITH_PORT);
105 break; 161 break;
106 162
107 default: 163 default:
108 break; 164 break;
109 } 165 }
123 ::keypad (stdscr, true); 179 ::keypad (stdscr, true);
124 ::noecho(); 180 ::noecho();
125 ::refresh(); 181 ::refresh();
126 ::timeout (0); 182 ::timeout (0);
127 ::use_default_colors(); 183 ::use_default_colors();
184 g_input.clear();
185 g_input << "";
128 g_title = format (APPNAME " %1 (%2)", full_version_string(), changeset_date_string()); 186 g_title = format (APPNAME " %1 (%2)", full_version_string(), changeset_date_string());
129 187
130 for (int i = 0; i < NUM_COLORS; ++i) 188 for (int i = 0; i < NUM_COLORS; ++i)
131 for (int j = 0; j < NUM_COLORS; ++j) 189 for (int j = 0; j < NUM_COLORS; ++j)
132 { 190 {
136 } 194 }
137 195
138 render_full(); 196 render_full();
139 refresh(); 197 refresh();
140 g_needRefresh = false; 198 g_needRefresh = false;
141 print ("Interface initialized.\n");
142 } 199 }
143 200
144 // ------------------------------------------------------------------------------------------------- 201 // -------------------------------------------------------------------------------------------------
145 // 202 //
146 static FUNCTION 203 static FUNCTION
173 // ------------------------------------------------------------------------------------------------- 230 // -------------------------------------------------------------------------------------------------
174 // 231 //
175 static FUNCTION 232 static FUNCTION
176 safe_disconnect (Function<void()> afterwards) -> void 233 safe_disconnect (Function<void()> afterwards) -> void
177 { 234 {
178 RCONSession* session = RCONSession::get_session(); 235 if (RCONSession::get_session()->is_active())
179
180 if (session and session->state() != RCON_DISCONNECTED)
181 { 236 {
182 g_disconnectConfirmFunction = afterwards; 237 g_disconnectConfirmFunction = afterwards;
183 set_input_state (INPUTSTATE_CONFIRM_DISCONNECTION); 238 set_input_state (INPUTSTATE_CONFIRM_DISCONNECTION);
184 } 239 }
185 else 240 else
230 attroff (promptColor); 285 attroff (promptColor);
231 return; 286 return;
232 } 287 }
233 288
234 String prompt = interface_prompt_string(); 289 String prompt = interface_prompt_string();
235 int displaylength = COLS - prompt.length() - 2; 290 int displayLength = COLS - prompt.length() - 2;
291 String displayString = current_input();
236 int y = LINES - 2; 292 int y = LINES - 2;
237 293
294 // If we're inputting a password, replace it with asterisks
295 if (g_inputState == INPUTSTATE_PASSWORD)
296 {
297 for (char& ch : displayString)
298 ch = '*';
299 }
300
238 // Ensure the cursor is within bounds 301 // Ensure the cursor is within bounds
239 g_cursor = clamp (g_cursor, 0, g_input.length()); 302 g_cursor = clamp (g_cursor, 0, displayString.length());
240 303
241 // Ensure that the cursor is always in view, adjust panning if this is not the case 304 // Ensure that the cursor is always in view, adjust panning if this is not the case
242 if (g_cursor > g_pan + displaylength) 305 if (g_cursor > g_pan + displayLength)
243 g_pan = g_cursor - displaylength; // cursor went too far right 306 g_pan = g_cursor - displayLength; // cursor went too far right
244 else if (g_cursor < g_pan) 307 else if (g_cursor < g_pan)
245 g_pan = g_cursor; // cursor went past the pan value to the left 308 g_pan = g_cursor; // cursor went past the pan value to the left
246 309
247 // What part of the string to draw? 310 // What part of the string to draw?
248 int start = g_pan; 311 int start = g_pan;
249 int end = min<int> (g_input.length(), start + displaylength); 312 int end = min<int> (displayString.length(), start + displayLength);
250 assert (g_cursor >= start and g_cursor <= end); 313 assert (g_cursor >= start and g_cursor <= end);
251
252 String displayinput = g_input;
253
254 // If we're inputting a password, replace it with asterisks
255 if (g_inputState == INPUTSTATE_PASSWORD)
256 {
257 for (char& ch : displayinput)
258 ch = '*';
259 }
260 314
261 // Render the input string 315 // Render the input string
262 mvhline (LINES - 2, 0, ' ', COLS); 316 mvhline (LINES - 2, 0, ' ', COLS);
263 mvprintw (y, prompt.length() + 1, "%s", displayinput.chars()); 317 mvprintw (y, prompt.length() + 1, "%s", displayString.chars());
264 318
265 // Render the prompt 319 // Render the prompt
266 attron (promptColor); 320 attron (promptColor);
267 mvprintw (y, 0, "%s", prompt.chars()); 321 mvprintw (y, 0, "%s", prompt.chars());
268 attroff (promptColor); 322 attroff (promptColor);
269 323
270 // Store in memory where the cursor is now (so that we can re-draw it to position the terminal 324 // Store in memory where the cursor is now (so that we can re-draw it to position the terminal
271 // cursor). 325 // cursor).
272 g_cursorChar.ch = g_cursor != 0 ? displayinput[g_cursor - 1] : '\0'; 326 g_cursorChar.ch = g_cursor != 0 ? displayString[g_cursor - 1] : '\0';
273 g_cursorChar.x = prompt.length() + (g_cursor - g_pan); 327 g_cursorChar.x = prompt.length() + (g_cursor - g_pan);
274 g_needRefresh = true; 328 g_needRefresh = true;
275 g_needInputRender = false; 329 g_needInputRender = false;
276 } 330 }
277 331
296 Interface::update_statusbar() -> void 350 Interface::update_statusbar() -> void
297 { 351 {
298 String text; 352 String text;
299 RCONSession* session = RCONSession::get_session(); 353 RCONSession* session = RCONSession::get_session();
300 354
301 if (session == nullptr) 355 switch (session->state())
302 { 356 {
303 text = ""; 357 case RCON_DISCONNECTED:
304 } 358 text = "Disconnected.";
305 else 359 break;
306 { 360
307 switch (session->state()) 361 case RCON_CONNECTING:
308 { 362 case RCON_AUTHENTICATING:
309 case RCON_DISCONNECTED: 363 text = "Connecting to " + session->address().to_string (IP_WITH_PORT) + "...";
310 text = "Disconnected."; 364 break;
311 break; 365
312 366 case RCON_CONNECTED:
313 case RCON_CONNECTING: 367 {
314 case RCON_AUTHENTICATING: 368 String adminText = (session->num_admins() == 0) ? "No other admins"
315 text = "Connecting to " + session->address().to_string (IP_WITH_PORT) + "..."; 369 : format ("%1 other admin%s1", session->num_admins());
316 break; 370 text = format ("%1 | %2 | %3", session->address().to_string (IP_WITH_PORT),
317 371 session->level(), adminText);
318 case RCON_CONNECTED: 372 }
319 { 373 break;
320 String adminText = (session->num_admins() == 0) ? "No other admins"
321 : format ("%1 other admin%s1", session->num_admins());
322 text = format ("%1 | %2 | %3", session->address().to_string (IP_WITH_PORT),
323 session->level(), adminText);
324 }
325 break;
326 }
327 } 374 }
328 375
329 if (not text.is_empty()) 376 if (not text.is_empty())
330 text += " | "; 377 text += " | ";
331 378
387 return; 434 return;
388 } 435 }
389 436
390 if (ch >= 0x20 and ch <= 0x7E) 437 if (ch >= 0x20 and ch <= 0x7E)
391 { 438 {
392 g_input.insert (g_cursor++, char (ch)); 439 mutable_current_input().insert (g_cursor++, char (ch));
393 g_needInputRender = true; 440 g_needInputRender = true;
394 } 441 }
395 else switch (ch) 442 else switch (ch)
396 { 443 {
397 case 'Q' - 'A' + 1: // ^Q 444 case 'Q' - 'A' + 1: // ^Q
401 break; 448 break;
402 449
403 case INPUTSTATE_NORMAL: 450 case INPUTSTATE_NORMAL:
404 safe_disconnect ([]() 451 safe_disconnect ([]()
405 { 452 {
406 endwin(); 453 RCONSession* session = RCONSession::get_session();
407 throw Exitception(); 454
455 if (session->is_active())
456 {
457 session->disconnect();
458 set_input_state (INPUTSTATE_NORMAL);
459 }
460 else
461 {
462 endwin();
463 throw Exitception();
464 }
408 }); 465 });
409 break; 466 break;
410 467
411 case INPUTSTATE_PASSWORD: 468 case INPUTSTATE_PASSWORD:
412 set_input_state (INPUTSTATE_ADDRESS); 469 set_input_state (INPUTSTATE_ADDRESS);
431 g_needInputRender = true; 488 g_needInputRender = true;
432 } 489 }
433 break; 490 break;
434 491
435 case KEY_RIGHT: 492 case KEY_RIGHT:
436 if (g_cursor < g_input.length()) 493 if (g_cursor < current_input().length())
437 { 494 {
438 g_cursor++; 495 g_cursor++;
439 g_needInputRender = true; 496 g_needInputRender = true;
440 } 497 }
441 break; 498 break;
442 499
500 case KEY_DOWN:
501 case KEY_UP:
502 move_input_cursor (ch == KEY_DOWN ? -1 : 1);
503 break;
504
443 case KEY_HOME: 505 case KEY_HOME:
444 if (g_cursor != 0) 506 if (g_cursor != 0)
445 { 507 {
446 g_cursor = 0; 508 g_cursor = 0;
447 g_needInputRender = true; 509 g_needInputRender = true;
448 } 510 }
449 break; 511 break;
450 512
451 case KEY_END: 513 case KEY_END:
452 if (g_cursor != g_input.length()) 514 if (g_cursor != current_input().length())
453 { 515 {
454 g_cursor = g_input.length(); 516 g_cursor = current_input().length();
455 g_needInputRender = true; 517 g_needInputRender = true;
456 } 518 }
457 break; 519 break;
458 520
459 case KEY_BACKSPACE: 521 case KEY_BACKSPACE:
460 if (g_cursor > 0) 522 if (g_cursor > 0)
461 { 523 {
462 g_input.remove_at (--g_cursor); 524 mutable_current_input().remove_at (--g_cursor);
463 g_needInputRender = true; 525 g_needInputRender = true;
464 } 526 }
465 break; 527 break;
466 528
467 case KEY_DC: 529 case KEY_DC:
468 if (g_cursor < g_input.length()) 530 if (g_cursor < current_input().length())
469 { 531 {
470 g_input.remove_at (g_cursor); 532 mutable_current_input().remove_at (g_cursor);
471 g_needInputRender = true; 533 g_needInputRender = true;
472 } 534 }
473 break; 535 break;
474 536
475 case KEY_PPAGE: 537 case KEY_PPAGE:
490 break; // handled above 552 break; // handled above
491 553
492 case INPUTSTATE_ADDRESS: 554 case INPUTSTATE_ADDRESS:
493 try 555 try
494 { 556 {
495 g_address = IPAddress::from_string (g_input); 557 g_address = IPAddress::from_string (current_input());
496 } 558 }
497 catch (std::exception& e) 559 catch (std::exception& e)
498 { 560 {
499 print (e.what()); 561 print (e.what());
500 return; 562 return;
505 567
506 set_input_state (INPUTSTATE_PASSWORD); 568 set_input_state (INPUTSTATE_PASSWORD);
507 break; 569 break;
508 570
509 case INPUTSTATE_PASSWORD: 571 case INPUTSTATE_PASSWORD:
510 if (g_inputState == INPUTSTATE_PASSWORD and not g_input.is_empty()) 572 if (g_inputState == INPUTSTATE_PASSWORD and not current_input().is_empty())
511 { 573 {
512 RCONSession* session = RCONSession::new_session(); 574 RCONSession* session = RCONSession::new_session();
513 session->set_password (g_input); 575 session->set_password (current_input());
514 session->connect (g_address); 576 session->connect (g_address);
515 set_input_state (INPUTSTATE_NORMAL); 577 set_input_state (INPUTSTATE_NORMAL);
516 } 578 }
517 break; 579 break;
518 580
519 case INPUTSTATE_NORMAL: 581 case INPUTSTATE_NORMAL:
520 if (RCONSession::get_session() != nullptr 582 if (RCONSession::get_session()->send_command (current_input()))
521 and RCONSession::get_session()->send_command (g_input))
522 { 583 {
523 g_input.clear(); 584 g_input.insert (0, "");
524 g_needInputRender = true; 585 g_needInputRender = true;
525 } 586 }
526 break; 587 break;
527 } 588 }
528 break; 589 break;

mercurial