43 INPUTSTATE_ADDRESS, |
43 INPUTSTATE_ADDRESS, |
44 INPUTSTATE_PASSWORD, |
44 INPUTSTATE_PASSWORD, |
45 INPUTSTATE_CONFIRM_DISCONNECTION, |
45 INPUTSTATE_CONFIRM_DISCONNECTION, |
46 }; |
46 }; |
47 |
47 |
48 static StringList g_input; |
48 static StringList InputHistory; |
49 static int g_inputCursor = 0; |
49 static int InputCursor = 0; |
50 static int g_cursor = 0; |
50 static int CursorPosition = 0; |
51 static int g_pan = 0; |
51 static int InputPanning = 0; |
52 static bool g_needRefresh = false; |
52 static bool NeedRefresh = false; |
53 static bool g_needStatusBarRender = false; |
53 static bool NeedStatusBarRender = false; |
54 static bool g_needInputRender = false; |
54 static bool NeedInputRender = false; |
55 static bool g_needOutputRender = false; |
55 static bool NeedOutputRender = false; |
56 static bool g_needNicklistRender = false; |
56 static bool NeedNicklistRender = false; |
57 static struct { char ch; int x; } g_cursorChar; |
57 static struct { char ch; int x; } CursorCharacter; |
58 static Vector<ColoredLine> g_output;; |
58 static Vector<ColoredLine> OutputLines; |
59 static int g_outputScroll = 0; |
59 static int OutputScroll = 0; |
60 static String g_title; |
60 static String Title; |
61 static InputState g_inputState = INPUTSTATE_NORMAL; |
61 static InputState CurrentInputState = INPUTSTATE_NORMAL; |
62 static Function<void (void)> g_disconnectConfirmFunction = nullptr; |
62 static Function<void (void)> DisconnectConfirmFunction = nullptr; |
63 static IPAddress g_address; |
63 static IPAddress CurrentAddress; |
64 static String g_statusBarText; |
64 static String StatusBarText; |
65 static StringList g_playerNames; |
65 static StringList PlayerNames; |
66 static String g_pasteBuffer; |
66 static String PasteBuffer; |
67 |
67 |
68 // ------------------------------------------------------------------------------------------------- |
68 // ------------------------------------------------------------------------------------------------- |
69 // |
69 // |
70 static FUNCTION |
70 static FUNCTION |
71 interface_color_pair (Color fg, Color bg) -> int |
71 interface_color_pair (Color fg, Color bg) -> int |
76 // ------------------------------------------------------------------------------------------------- |
76 // ------------------------------------------------------------------------------------------------- |
77 // |
77 // |
78 static FUNCTION |
78 static FUNCTION |
79 current_input() -> const String& |
79 current_input() -> const String& |
80 { |
80 { |
81 return g_input[g_inputCursor]; |
81 return InputHistory[InputCursor]; |
82 } |
82 } |
83 |
83 |
84 // ------------------------------------------------------------------------------------------------- |
84 // ------------------------------------------------------------------------------------------------- |
85 // |
85 // |
86 // Makes current_input() the lastmost input (so that we won't modify history) |
86 // Makes current_input() the lastmost input (so that we won't modify history) |
87 // |
87 // |
88 static FUNCTION |
88 static FUNCTION |
89 detach_input() -> void |
89 detach_input() -> void |
90 { |
90 { |
91 if (g_inputCursor > 0) |
91 if (InputCursor > 0) |
92 { |
92 { |
93 g_input[0] = current_input(); |
93 InputHistory[0] = current_input(); |
94 g_inputCursor = 0; |
94 InputCursor = 0; |
95 } |
95 } |
96 } |
96 } |
97 |
97 |
98 // ------------------------------------------------------------------------------------------------- |
98 // ------------------------------------------------------------------------------------------------- |
99 // A version of current_input() that allows changing the contents of it. |
99 // A version of current_input() that allows changing the contents of it. |
100 // |
100 // |
101 static FUNCTION |
101 static FUNCTION |
102 mutable_current_input() -> String& |
102 mutable_current_input() -> String& |
103 { |
103 { |
104 detach_input(); |
104 detach_input(); |
105 return g_input[g_inputCursor]; |
105 return InputHistory[InputCursor]; |
106 } |
106 } |
107 |
107 |
108 // ------------------------------------------------------------------------------------------------- |
108 // ------------------------------------------------------------------------------------------------- |
109 // |
109 // |
110 static FUNCTION |
110 static FUNCTION |
111 move_input_cursor (int delta) -> void |
111 move_input_cursor (int delta) -> void |
112 { |
112 { |
113 // No input history when inputting addresses or passwords |
113 // No input history when inputting addresses or passwords |
114 if (g_inputState != INPUTSTATE_NORMAL) |
114 if (CurrentInputState != INPUTSTATE_NORMAL) |
115 { |
115 { |
116 g_inputCursor = 0; |
116 InputCursor = 0; |
117 return; |
117 return; |
118 } |
118 } |
119 |
119 |
120 int oldcursor = g_inputCursor; |
120 int oldcursor = InputCursor; |
121 g_inputCursor = clamp (g_inputCursor + delta, 0, g_input.size() - 1); |
121 InputCursor = clamp (InputCursor + delta, 0, InputHistory.size() - 1); |
122 |
122 |
123 if (g_inputCursor != oldcursor) |
123 if (InputCursor != oldcursor) |
124 { |
124 { |
125 g_cursor = current_input().length(); |
125 CursorPosition = current_input().length(); |
126 g_needInputRender = true; |
126 NeedInputRender = true; |
127 } |
127 } |
128 } |
128 } |
129 |
129 |
130 // ------------------------------------------------------------------------------------------------- |
130 // ------------------------------------------------------------------------------------------------- |
131 // |
131 // |
132 static FUNCTION |
132 static FUNCTION |
133 interface_prompt_string() -> String |
133 interface_prompt_string() -> String |
134 { |
134 { |
135 String prompt; |
135 String prompt; |
136 |
136 |
137 switch (g_inputState) |
137 switch (CurrentInputState) |
138 { |
138 { |
139 case INPUTSTATE_NORMAL: prompt = ">"; break; |
139 case INPUTSTATE_NORMAL: prompt = ">"; break; |
140 case INPUTSTATE_ADDRESS: prompt = "address:"; break; |
140 case INPUTSTATE_ADDRESS: prompt = "address:"; break; |
141 case INPUTSTATE_PASSWORD: prompt = "password:"; break; |
141 case INPUTSTATE_PASSWORD: prompt = "password:"; break; |
142 case INPUTSTATE_CONFIRM_DISCONNECTION: break; |
142 case INPUTSTATE_CONFIRM_DISCONNECTION: break; |
200 (j == DEFAULT) ? -1 : j); |
200 (j == DEFAULT) ? -1 : j); |
201 } |
201 } |
202 |
202 |
203 render_full(); |
203 render_full(); |
204 refresh(); |
204 refresh(); |
205 g_needRefresh = false; |
205 NeedRefresh = false; |
206 } |
206 } |
207 |
207 |
208 // ------------------------------------------------------------------------------------------------- |
208 // ------------------------------------------------------------------------------------------------- |
209 // |
209 // |
210 static FUNCTION |
210 static FUNCTION |
211 interface_render_titlebar() -> void |
211 interface_render_titlebar() -> void |
212 { |
212 { |
213 if (g_title.length() <= COLS) |
213 if (Title.length() <= COLS) |
214 { |
214 { |
215 int pair = interface_color_pair (WHITE, BLUE); |
215 int pair = interface_color_pair (WHITE, BLUE); |
216 int startx = (COLS - g_title.length()) / 2; |
216 int startx = (COLS - Title.length()) / 2; |
217 int endx = startx + g_title.length(); |
217 int endx = startx + Title.length(); |
218 attron (pair); |
218 attron (pair); |
219 mvprintw (0, startx, "%s", g_title.chars()); |
219 mvprintw (0, startx, "%s", Title.chars()); |
220 mvhline (0, 0, ' ', startx); |
220 mvhline (0, 0, ' ', startx); |
221 mvhline (0, endx, ' ', COLS - endx); |
221 mvhline (0, endx, ' ', COLS - endx); |
222 attroff (pair); |
222 attroff (pair); |
223 } |
223 } |
224 |
224 |
225 g_needRefresh = true; |
225 NeedRefresh = true; |
226 } |
226 } |
227 |
227 |
228 // ------------------------------------------------------------------------------------------------- |
228 // ------------------------------------------------------------------------------------------------- |
229 // |
229 // |
230 FUNCTION |
230 FUNCTION |
231 Interface::set_title (const String& title) -> void |
231 Interface::set_title (const String& title) -> void |
232 { |
232 { |
233 g_title = title; |
233 Title = title; |
234 interface_render_titlebar(); |
234 interface_render_titlebar(); |
235 } |
235 } |
236 |
236 |
237 // ------------------------------------------------------------------------------------------------- |
237 // ------------------------------------------------------------------------------------------------- |
238 // |
238 // |
239 static FUNCTION |
239 static FUNCTION |
240 safe_disconnect (Function<void()> afterwards) -> void |
240 safe_disconnect (Function<void()> afterwards) -> void |
241 { |
241 { |
242 if (RCONSession::get_session()->is_active()) |
242 if (RCONSession::get_session()->is_active()) |
243 { |
243 { |
244 g_disconnectConfirmFunction = afterwards; |
244 DisconnectConfirmFunction = afterwards; |
245 set_input_state (INPUTSTATE_CONFIRM_DISCONNECTION); |
245 set_input_state (INPUTSTATE_CONFIRM_DISCONNECTION); |
246 } |
246 } |
247 else |
247 else |
248 afterwards(); |
248 afterwards(); |
249 } |
249 } |
447 { |
447 { |
448 int promptColor = interface_color_pair (WHITE, BLUE); |
448 int promptColor = interface_color_pair (WHITE, BLUE); |
449 |
449 |
450 // If we're asking the user if they want to disconnect, we don't render any input strings, |
450 // If we're asking the user if they want to disconnect, we don't render any input strings, |
451 // just the confirmation message. |
451 // just the confirmation message. |
452 if (g_inputState == INPUTSTATE_CONFIRM_DISCONNECTION) |
452 if (CurrentInputState == INPUTSTATE_CONFIRM_DISCONNECTION) |
453 { |
453 { |
454 attron (promptColor); |
454 attron (promptColor); |
455 mvhline (LINES - 2, 0, ' ', COLS); |
455 mvhline (LINES - 2, 0, ' ', COLS); |
456 mvprintw (LINES - 2, 0, "Are you sure you want to disconnect? y/n"); |
456 mvprintw (LINES - 2, 0, "Are you sure you want to disconnect? y/n"); |
457 attroff (promptColor); |
457 attroff (promptColor); |
458 g_needRefresh = true; |
458 NeedRefresh = true; |
459 return; |
459 return; |
460 } |
460 } |
461 |
461 |
462 String prompt = interface_prompt_string(); |
462 String prompt = interface_prompt_string(); |
463 int displayLength = COLS - prompt.length() - 2; |
463 int displayLength = COLS - prompt.length() - 2; |
464 String displayString = current_input(); |
464 String displayString = current_input(); |
465 int y = LINES - 2; |
465 int y = LINES - 2; |
466 |
466 |
467 // If we're inputting a password, replace it with asterisks |
467 // If we're inputting a password, replace it with asterisks |
468 if (g_inputState == INPUTSTATE_PASSWORD) |
468 if (CurrentInputState == INPUTSTATE_PASSWORD) |
469 { |
469 { |
470 for (char& ch : displayString) |
470 for (char& ch : displayString) |
471 ch = '*'; |
471 ch = '*'; |
472 } |
472 } |
473 |
473 |
474 // Ensure the cursor is within bounds |
474 // Ensure the cursor is within bounds |
475 g_cursor = clamp (g_cursor, 0, displayString.length()); |
475 CursorPosition = clamp (CursorPosition, 0, displayString.length()); |
476 |
476 |
477 // Ensure that the cursor is always in view, adjust panning if this is not the case |
477 // Ensure that the cursor is always in view, adjust panning if this is not the case |
478 if (g_cursor > g_pan + displayLength) |
478 if (CursorPosition > InputPanning + displayLength) |
479 g_pan = g_cursor - displayLength; // cursor went too far right |
479 InputPanning = CursorPosition - displayLength; // cursor went too far right |
480 else if (g_cursor < g_pan) |
480 else if (CursorPosition < InputPanning) |
481 g_pan = g_cursor; // cursor went past the pan value to the left |
481 InputPanning = CursorPosition; // cursor went past the pan value to the left |
482 |
482 |
483 // What part of the string to draw? |
483 // What part of the string to draw? |
484 int start = g_pan; |
484 int start = InputPanning; |
485 int end = min<int> (displayString.length(), start + displayLength); |
485 int end = min<int> (displayString.length(), start + displayLength); |
486 assert (g_cursor >= start and g_cursor <= end); |
486 assert (CursorPosition >= start and CursorPosition <= end); |
487 |
487 |
488 // Render the input string |
488 // Render the input string |
489 mvhline (LINES - 2, 0, ' ', COLS); |
489 mvhline (LINES - 2, 0, ' ', COLS); |
490 mvprintw (y, prompt.length() + 1, "%s", displayString.mid (start, end).chars()); |
490 mvprintw (y, prompt.length() + 1, "%s", displayString.mid (start, end).chars()); |
491 |
491 |
575 // |
575 // |
576 static FUNCTION |
576 static FUNCTION |
577 interface_position_cursor() -> void |
577 interface_position_cursor() -> void |
578 { |
578 { |
579 // This is only relevant if the input string is being drawn |
579 // This is only relevant if the input string is being drawn |
580 if (g_inputState == INPUTSTATE_CONFIRM_DISCONNECTION) |
580 if (CurrentInputState == INPUTSTATE_CONFIRM_DISCONNECTION) |
581 return; |
581 return; |
582 |
582 |
583 int y = LINES - 2; |
583 int y = LINES - 2; |
584 |
584 |
585 if (g_cursorChar.ch != '\0') |
585 if (CursorCharacter.ch != '\0') |
586 mvprintw (y, g_cursorChar.x, "%c", g_cursorChar.ch); |
586 mvprintw (y, CursorCharacter.x, "%c", CursorCharacter.ch); |
587 else |
587 else |
588 mvprintw (y, interface_prompt_string().length(), " "); |
588 mvprintw (y, interface_prompt_string().length(), " "); |
589 } |
589 } |
590 |
590 |
591 // ------------------------------------------------------------------------------------------------- |
591 // ------------------------------------------------------------------------------------------------- |
592 // |
592 // |
593 static FUNCTION |
593 static FUNCTION |
594 interface_find_previous_word() -> int |
594 interface_find_previous_word() -> int |
595 { |
595 { |
596 const String& input = current_input(); |
596 const String& input = current_input(); |
597 int pos = g_cursor; |
597 int pos = CursorPosition; |
598 |
598 |
599 // Move past whitespace |
599 // Move past whitespace |
600 while (pos > 0 and isspace (input[pos - 1])) |
600 while (pos > 0 and isspace (input[pos - 1])) |
601 pos--; |
601 pos--; |
602 |
602 |
655 ::clear(); |
655 ::clear(); |
656 render_full(); |
656 render_full(); |
657 return; |
657 return; |
658 } |
658 } |
659 |
659 |
660 if (g_inputState == INPUTSTATE_CONFIRM_DISCONNECTION) |
660 if (CurrentInputState == INPUTSTATE_CONFIRM_DISCONNECTION) |
661 { |
661 { |
662 if (ch == 'y' or ch == 'Y') |
662 if (ch == 'y' or ch == 'Y') |
663 { |
663 { |
664 RCONSession::get_session()->disconnect(); |
664 RCONSession::get_session()->disconnect(); |
665 g_disconnectConfirmFunction(); |
665 DisconnectConfirmFunction(); |
666 } |
666 } |
667 else if (ch == 'n' or ch == 'N') |
667 else if (ch == 'n' or ch == 'N') |
668 set_input_state (INPUTSTATE_NORMAL); |
668 set_input_state (INPUTSTATE_NORMAL); |
669 |
669 |
670 return; |
670 return; |
671 } |
671 } |
672 |
672 |
673 if (ch >= 0x20 and ch <= 0x7E) |
673 if (ch >= 0x20 and ch <= 0x7E) |
674 { |
674 { |
675 mutable_current_input().insert (g_cursor++, char (ch)); |
675 mutable_current_input().insert (CursorPosition++, char (ch)); |
676 g_needInputRender = true; |
676 NeedInputRender = true; |
677 } |
677 } |
678 else switch (ch) |
678 else switch (ch) |
679 { |
679 { |
680 case 'Q' - 'A' + 1: // ^Q |
680 case 'Q' - 'A' + 1: // ^Q |
681 switch (g_inputState) |
681 switch (CurrentInputState) |
682 { |
682 { |
683 case INPUTSTATE_CONFIRM_DISCONNECTION: |
683 case INPUTSTATE_CONFIRM_DISCONNECTION: |
684 break; |
684 break; |
685 |
685 |
686 case INPUTSTATE_NORMAL: |
686 case INPUTSTATE_NORMAL: |
710 } |
710 } |
711 break; |
711 break; |
712 |
712 |
713 case KEY_LEFT: |
713 case KEY_LEFT: |
714 case 'B' - 'A' + 1: // readline ^B |
714 case 'B' - 'A' + 1: // readline ^B |
715 if (g_cursor > 0) |
715 if (CursorPosition > 0) |
716 { |
716 { |
717 g_cursor--; |
717 CursorPosition--; |
718 g_needInputRender = true; |
718 NeedInputRender = true; |
719 } |
719 } |
720 break; |
720 break; |
721 |
721 |
722 case KEY_RIGHT: |
722 case KEY_RIGHT: |
723 case 'F' - 'A' + 1: // readline ^F |
723 case 'F' - 'A' + 1: // readline ^F |
724 if (g_cursor < current_input().length()) |
724 if (CursorPosition < current_input().length()) |
725 { |
725 { |
726 g_cursor++; |
726 CursorPosition++; |
727 g_needInputRender = true; |
727 NeedInputRender = true; |
728 } |
728 } |
729 break; |
729 break; |
730 |
730 |
731 case KEY_DOWN: |
731 case KEY_DOWN: |
732 case KEY_UP: |
732 case KEY_UP: |
733 move_input_cursor (ch == KEY_DOWN ? -1 : 1); |
733 move_input_cursor (ch == KEY_DOWN ? -1 : 1); |
734 break; |
734 break; |
735 |
735 |
736 case KEY_HOME: |
736 case KEY_HOME: |
737 case 'A' - 'A' + 1: // readline ^A |
737 case 'A' - 'A' + 1: // readline ^A |
738 if (g_cursor != 0) |
738 if (CursorPosition != 0) |
739 { |
739 { |
740 g_cursor = 0; |
740 CursorPosition = 0; |
741 g_needInputRender = true; |
741 NeedInputRender = true; |
742 } |
742 } |
743 break; |
743 break; |
744 |
744 |
745 case KEY_END: |
745 case KEY_END: |
746 case 'E' - 'A' + 1: // readline ^E |
746 case 'E' - 'A' + 1: // readline ^E |
747 if (g_cursor != current_input().length()) |
747 if (CursorPosition != current_input().length()) |
748 { |
748 { |
749 g_cursor = current_input().length(); |
749 CursorPosition = current_input().length(); |
750 g_needInputRender = true; |
750 NeedInputRender = true; |
751 } |
751 } |
752 break; |
752 break; |
753 |
753 |
754 case KEY_BACKSPACE: |
754 case KEY_BACKSPACE: |
755 if (g_cursor > 0) |
755 if (CursorPosition > 0) |
756 { |
756 { |
757 mutable_current_input().remove_at (--g_cursor); |
757 mutable_current_input().remove_at (--CursorPosition); |
758 g_needInputRender = true; |
758 NeedInputRender = true; |
759 } |
759 } |
760 break; |
760 break; |
761 |
761 |
762 case KEY_DC: |
762 case KEY_DC: |
763 case 'D' - 'A' + 1: // readline ^D |
763 case 'D' - 'A' + 1: // readline ^D |
764 if (g_cursor < current_input().length()) |
764 if (CursorPosition < current_input().length()) |
765 { |
765 { |
766 mutable_current_input().remove_at (g_cursor); |
766 mutable_current_input().remove_at (CursorPosition); |
767 g_needInputRender = true; |
767 NeedInputRender = true; |
768 } |
768 } |
769 break; |
769 break; |
770 |
770 |
771 case KEY_PPAGE: |
771 case KEY_PPAGE: |
772 g_outputScroll += min (g_pageSize, LINES / 2); |
772 OutputScroll += min (g_pageSize, LINES / 2); |
773 g_needOutputRender = true; |
773 NeedOutputRender = true; |
774 break; |
774 break; |
775 |
775 |
776 case KEY_NPAGE: |
776 case KEY_NPAGE: |
777 g_outputScroll -= min (g_pageSize, LINES / 2); |
777 OutputScroll -= min (g_pageSize, LINES / 2); |
778 g_needOutputRender = true; |
778 NeedOutputRender = true; |
779 break; |
779 break; |
780 |
780 |
781 case 'U' - 'A' + 1: // readline ^U - delete from start to cursor |
781 case 'U' - 'A' + 1: // readline ^U - delete from start to cursor |
782 if (g_cursor > 0) |
782 if (CursorPosition > 0) |
783 { |
783 { |
784 yank (0, g_cursor); |
784 yank (0, CursorPosition); |
785 g_cursor = 0; |
785 CursorPosition = 0; |
786 } |
786 } |
787 break; |
787 break; |
788 |
788 |
789 case 'K' - 'A' + 1: // readline ^K - delete from cursor to end |
789 case 'K' - 'A' + 1: // readline ^K - delete from cursor to end |
790 yank (g_cursor, mutable_current_input().length()); |
790 yank (CursorPosition, mutable_current_input().length()); |
791 break; |
791 break; |
792 |
792 |
793 case 'W' - 'A' + 1: // readline ^W - delete from previous word bounary to current |
793 case 'W' - 'A' + 1: // readline ^W - delete from previous word bounary to current |
794 yank (interface_find_previous_word(), g_cursor); |
794 yank (interface_find_previous_word(), CursorPosition); |
795 break; |
795 break; |
796 |
796 |
797 case 'Y' - 'A' + 1: // readline ^Y - paste previously deleted text |
797 case 'Y' - 'A' + 1: // readline ^Y - paste previously deleted text |
798 if (not g_pasteBuffer.is_empty()) |
798 if (not PasteBuffer.is_empty()) |
799 { |
799 { |
800 mutable_current_input().insert (g_cursor, g_pasteBuffer); |
800 mutable_current_input().insert (CursorPosition, PasteBuffer); |
801 g_cursor += g_pasteBuffer.length(); |
801 CursorPosition += PasteBuffer.length(); |
802 g_needInputRender = true; |
802 NeedInputRender = true; |
803 } |
803 } |
804 break; |
804 break; |
805 |
805 |
806 case '\n': |
806 case '\n': |
807 case KEY_ENTER: |
807 case KEY_ENTER: |
808 switch (g_inputState) |
808 switch (CurrentInputState) |
809 { |
809 { |
810 case INPUTSTATE_CONFIRM_DISCONNECTION: |
810 case INPUTSTATE_CONFIRM_DISCONNECTION: |
811 break; // handled above |
811 break; // handled above |
812 |
812 |
813 case INPUTSTATE_ADDRESS: |
813 case INPUTSTATE_ADDRESS: |
814 try |
814 try |
815 { |
815 { |
816 g_address = IPAddress::from_string (current_input()); |
816 CurrentAddress = IPAddress::from_string (current_input()); |
817 } |
817 } |
818 catch (std::exception& e) |
818 catch (std::exception& e) |
819 { |
819 { |
820 print ("%1\n", e.what()); |
820 print ("%1\n", e.what()); |
821 return; |
821 return; |
822 } |
822 } |
823 |
823 |
824 if (g_address.port == 0) |
824 if (CurrentAddress.port == 0) |
825 g_address.port = 10666; |
825 CurrentAddress.port = 10666; |
826 |
826 |
827 set_input_state (INPUTSTATE_PASSWORD); |
827 set_input_state (INPUTSTATE_PASSWORD); |
828 break; |
828 break; |
829 |
829 |
830 case INPUTSTATE_PASSWORD: |
830 case INPUTSTATE_PASSWORD: |
831 if (g_inputState == INPUTSTATE_PASSWORD and not current_input().is_empty()) |
831 if (CurrentInputState == INPUTSTATE_PASSWORD and not current_input().is_empty()) |
832 { |
832 { |
833 RCONSession* session = RCONSession::get_session(); |
833 RCONSession* session = RCONSession::get_session(); |
834 session->disconnect(); |
834 session->disconnect(); |
835 session->set_password (current_input()); |
835 session->set_password (current_input()); |
836 session->connect (g_address); |
836 session->connect (CurrentAddress); |
837 set_input_state (INPUTSTATE_NORMAL); |
837 set_input_state (INPUTSTATE_NORMAL); |
838 } |
838 } |
839 break; |
839 break; |
840 |
840 |
841 case INPUTSTATE_NORMAL: |
841 case INPUTSTATE_NORMAL: |
842 if (RCONSession::get_session()->send_command (current_input())) |
842 if (RCONSession::get_session()->send_command (current_input())) |
843 { |
843 { |
844 g_input.insert (0, ""); |
844 InputHistory.insert (0, ""); |
845 g_needInputRender = true; |
845 NeedInputRender = true; |
846 } |
846 } |
847 break; |
847 break; |
848 } |
848 } |
849 break; |
849 break; |
850 |
850 |
851 case 'N' - 'A' + 1: // ^N |
851 case 'N' - 'A' + 1: // ^N |
852 if (g_inputState == INPUTSTATE_NORMAL) |
852 if (CurrentInputState == INPUTSTATE_NORMAL) |
853 safe_disconnect ([]() {set_input_state (INPUTSTATE_ADDRESS);}); |
853 safe_disconnect ([]() {set_input_state (INPUTSTATE_ADDRESS);}); |
854 break; |
854 break; |
855 |
855 |
856 case '\e': // Escape |
856 case '\e': // Escape |
857 // We may have an alt key coming |
857 // We may have an alt key coming |
862 switch (ch) |
862 switch (ch) |
863 { |
863 { |
864 case 'b': |
864 case 'b': |
865 case 'B': |
865 case 'B': |
866 // readline alt-b - move one word to the left |
866 // readline alt-b - move one word to the left |
867 g_cursor = interface_find_previous_word(); |
867 CursorPosition = interface_find_previous_word(); |
868 g_needInputRender = true; |
868 NeedInputRender = true; |
869 break; |
869 break; |
870 |
870 |
871 case 'f': |
871 case 'f': |
872 case 'F': |
872 case 'F': |
873 // readline alt-f - move one word to the right |
873 // readline alt-f - move one word to the right |
874 g_cursor = interface_find_next_word(); |
874 CursorPosition = interface_find_next_word(); |
875 g_needInputRender = true; |
875 NeedInputRender = true; |
876 break; |
876 break; |
877 |
877 |
878 case 'd': |
878 case 'd': |
879 case 'D': |
879 case 'D': |
880 // readline alt-d - delete from here till next word boundary |
880 // readline alt-d - delete from here till next word boundary |
881 yank (g_cursor, interface_find_next_word()); |
881 yank (CursorPosition, interface_find_next_word()); |
882 break; |
882 break; |
883 } |
883 } |
884 } |
884 } |
885 else |
885 else |
886 { |
886 { |
887 // No alt-key, handle pure escape |
887 // No alt-key, handle pure escape |
888 if (g_inputState == INPUTSTATE_PASSWORD) |
888 if (CurrentInputState == INPUTSTATE_PASSWORD) |
889 set_input_state (INPUTSTATE_ADDRESS); |
889 set_input_state (INPUTSTATE_ADDRESS); |
890 else if (g_inputState == INPUTSTATE_ADDRESS) |
890 else if (CurrentInputState == INPUTSTATE_ADDRESS) |
891 set_input_state (INPUTSTATE_NORMAL); |
891 set_input_state (INPUTSTATE_NORMAL); |
892 } |
892 } |
893 break; |
893 break; |
894 } |
894 } |
895 } |
895 } |
922 |
922 |
923 for (char ch : a) |
923 for (char ch : a) |
924 { |
924 { |
925 if (ch == '\n') |
925 if (ch == '\n') |
926 { |
926 { |
927 g_output.last().finalize(); |
927 OutputLines.last().finalize(); |
928 g_output << ColoredLine(); |
928 OutputLines << ColoredLine(); |
929 continue; |
929 continue; |
930 } |
930 } |
931 |
931 |
932 if (g_output.last().length() == 0) |
932 if (OutputLines.last().length() == 0) |
933 { |
933 { |
934 time_t now; |
934 time_t now; |
935 time (&now); |
935 time (&now); |
936 char timestamp[32]; |
936 char timestamp[32]; |
937 strftime (timestamp, sizeof timestamp, "[%H:%M:%S] ", localtime (&now)); |
937 strftime (timestamp, sizeof timestamp, "[%H:%M:%S] ", localtime (&now)); |
938 |
938 |
939 for (char* cp = timestamp; *cp != '\0'; ++cp) |
939 for (char* cp = timestamp; *cp != '\0'; ++cp) |
940 g_output.last().add_char (*cp); |
940 OutputLines.last().add_char (*cp); |
941 } |
941 } |
942 |
942 |
943 g_output.last().add_char (ch); |
943 OutputLines.last().add_char (ch); |
944 } |
944 } |
945 |
945 |
946 g_needOutputRender = true; |
946 NeedOutputRender = true; |
947 } |
947 } |
948 |
948 |
949 // ------------------------------------------------------------------------------------------------- |
949 // ------------------------------------------------------------------------------------------------- |
950 // |
950 // |
951 FUNCTION |
951 FUNCTION |
952 Interface::connect (String address, String password) -> void |
952 Interface::connect (String address, String password) -> void |
953 { |
953 { |
954 try |
954 try |
955 { |
955 { |
956 g_address = IPAddress::from_string (address); |
956 CurrentAddress = IPAddress::from_string (address); |
957 } |
957 } |
958 catch (std::exception& e) |
958 catch (std::exception& e) |
959 { |
959 { |
960 print ("%1\n", e.what()); |
960 print ("%1\n", e.what()); |
961 return; |
961 return; |
962 } |
962 } |
963 |
963 |
964 if (g_address.port == 0) |
964 if (CurrentAddress.port == 0) |
965 g_address.port = 10666; |
965 CurrentAddress.port = 10666; |
966 |
966 |
967 RCONSession* session = RCONSession::get_session(); |
967 RCONSession* session = RCONSession::get_session(); |
968 session->disconnect(); |
968 session->disconnect(); |
969 session->set_password (password); |
969 session->set_password (password); |
970 session->connect (g_address); |
970 session->connect (CurrentAddress); |
971 } |
971 } |
972 |
972 |
973 // ------------------------------------------------------------------------------------------------- |
973 // ------------------------------------------------------------------------------------------------- |
974 // |
974 // |
975 FUNCTION |
975 FUNCTION |
976 Interface::set_player_names (const StringList& names) -> void |
976 Interface::set_player_names (const StringList& names) -> void |
977 { |
977 { |
978 g_playerNames = names; |
978 PlayerNames = names; |
979 g_needNicklistRender = true; |
979 NeedNicklistRender = true; |
980 } |
980 } |