39 |
39 |
40 // ------------------------------------------------------------------------------------------------- |
40 // ------------------------------------------------------------------------------------------------- |
41 // |
41 // |
42 int Interface::color_pair (Color fg, Color bg) |
42 int Interface::color_pair (Color fg, Color bg) |
43 { |
43 { |
44 return COLOR_PAIR ((int (fg) * NUM_COLORS) + int (bg)); |
44 return COLOR_PAIR (1 + (int (fg) * NUM_COLORS) + int (bg)); |
45 } |
45 } |
46 |
46 |
47 // ------------------------------------------------------------------------------------------------- |
47 // ------------------------------------------------------------------------------------------------- |
48 // |
48 // |
49 const String& Interface::current_input() |
49 const String& Interface::current_input() |
141 // ------------------------------------------------------------------------------------------------- |
141 // ------------------------------------------------------------------------------------------------- |
142 // |
142 // |
143 Interface::Interface() : |
143 Interface::Interface() : |
144 Session (this) |
144 Session (this) |
145 { |
145 { |
146 ::initscr(); |
146 #ifdef XCURSES |
147 ::start_color(); |
147 ::Xinitscr(argc, argv); |
148 ::raw(); |
148 #else |
|
149 ::initscr(); |
|
150 #endif |
|
151 |
|
152 ::cbreak(); |
149 ::keypad (stdscr, true); |
153 ::keypad (stdscr, true); |
150 ::noecho(); |
154 ::noecho(); |
151 ::refresh(); |
155 ::refresh(); |
152 ::timeout (0); |
156 ::timeout (0); |
153 ::use_default_colors(); |
|
154 InputHistory.clear(); |
157 InputHistory.clear(); |
155 InputHistory << ""; |
158 InputHistory << ""; |
156 OutputLines.clear(); |
159 OutputLines.clear(); |
157 OutputLines << ColoredLine(); |
160 OutputLines << ColoredLine(); |
158 |
161 Title.sprintf (APPNAME " %s (%d)", full_version_string(), changeset_date_string()); |
159 for (int i = 0; i < NUM_COLORS; ++i) |
162 |
160 for (int j = 0; j < NUM_COLORS; ++j) |
163 if (::has_colors()) |
161 { |
164 { |
162 init_pair ((i * NUM_COLORS + j), |
165 ::start_color(); |
163 (i == DEFAULT) ? -1 : i, |
166 ::use_default_colors(); |
164 (j == DEFAULT) ? -1 : j); |
167 |
|
168 int defaultFg = (use_default_colors() == OK) ? -1 : COLOR_WHITE; |
|
169 int defaultBg = (use_default_colors() == OK) ? -1 : COLOR_BLACK; |
|
170 |
|
171 // Initialize color pairs |
|
172 for (int i = 0; i < NUM_COLORS; ++i) |
|
173 for (int j = 0; j < NUM_COLORS; ++j) |
|
174 { |
|
175 int pairnum = 1 + (i * NUM_COLORS + j); |
|
176 int fg = (i == DEFAULT) ? defaultFg : i; |
|
177 int bg = (j == DEFAULT) ? defaultBg : j; |
|
178 |
|
179 if (::init_pair (pairnum, fg, bg) == ERR) |
|
180 print ("Unable to initialize color pair %d (%d, %d)\n", pairnum, fg, bg); |
|
181 } |
|
182 } |
|
183 else |
|
184 { |
|
185 print ("This terminal does not appear to support colors.\n"); |
165 } |
186 } |
166 |
187 |
167 render_full(); |
188 render_full(); |
168 refresh(); |
189 refresh(); |
169 NeedRefresh = false; |
190 NeedRefresh = false; |
171 |
192 |
172 // ------------------------------------------------------------------------------------------------- |
193 // ------------------------------------------------------------------------------------------------- |
173 // |
194 // |
174 void Interface::render_titlebar() |
195 void Interface::render_titlebar() |
175 { |
196 { |
176 String message = Title; |
197 if (Title.length() <= COLS) |
177 |
|
178 if (Title.is_empty()) |
|
179 message = format (APPNAME " %1 (%2)", full_version_string(), changeset_date_string()); |
|
180 |
|
181 if (message.length() <= COLS) |
|
182 { |
198 { |
183 int pair = color_pair (WHITE, BLUE); |
199 int pair = color_pair (WHITE, BLUE); |
184 int startx = (COLS - message.length()) / 2; |
200 int startx = (COLS - Title.length()) / 2; |
185 int endx = startx + message.length(); |
201 int endx = startx + Title.length(); |
186 attron (pair); |
202 attron (pair); |
187 mvprintw (0, startx, "%s", message.chars()); |
203 mvprintw (0, startx, "%s", Title.chars()); |
188 mvhline (0, 0, ' ', startx); |
204 mvhline (0, 0, ' ', startx); |
189 mvhline (0, endx, ' ', COLS - endx); |
205 mvhline (0, endx, ' ', COLS - endx); |
190 attroff (pair); |
206 attroff (pair); |
191 } |
207 } |
192 |
208 |
380 for (int i = 0; i < height; ++i) |
396 for (int i = 0; i < height; ++i) |
381 { |
397 { |
382 mvhline (y, x, ' ', width); |
398 mvhline (y, x, ' ', width); |
383 |
399 |
384 if (i < PlayerNames.size()) |
400 if (i < PlayerNames.size()) |
385 render_colorline (y, x, width, PlayerNames[i], false); |
401 { |
|
402 String displaynick = PlayerNames[i]; |
|
403 |
|
404 if (displaynick.length() > width) |
|
405 { |
|
406 displaynick = displaynick.mid (0, width - 3); |
|
407 displaynick += "..."; |
|
408 } |
|
409 |
|
410 mvprintw (y, x, "%s", displaynick.chars()); |
|
411 } |
386 |
412 |
387 y++; |
413 y++; |
388 } |
414 } |
389 |
415 |
390 NeedNicklistRender = false; |
416 NeedNicklistRender = false; |
486 case RCON_CONNECTED: |
512 case RCON_CONNECTED: |
487 { |
513 { |
488 String adminText; |
514 String adminText; |
489 |
515 |
490 if (Session.num_admins() == 0) |
516 if (Session.num_admins() == 0) |
|
517 { |
491 adminText = "No other admins"; |
518 adminText = "No other admins"; |
|
519 } |
492 else |
520 else |
493 adminText = format ("%1 other admin%s1", Session.num_admins()); |
521 { |
494 |
522 adminText.sprintf ("%d other admin%s", Session.num_admins(), |
495 text = format ("%1 | %2 | %3", Session.address().to_string (IP_WITH_PORT), |
523 Session.num_admins() != 1 ? "s" : ""); |
496 Session.level(), adminText); |
524 } |
|
525 |
|
526 text.sprintf ("%s | %s | %s", Session.address().to_string (IP_WITH_PORT).chars(), |
|
527 Session.level().chars(), adminText.chars()); |
497 } |
528 } |
498 break; |
529 break; |
499 } |
530 } |
500 |
531 |
501 if (not text.is_empty()) |
532 if (not text.is_empty()) |
502 text += " | "; |
533 text += " | "; |
503 |
534 |
504 text += "^N to connect, ^Q to quit"; |
535 text += "Ctrl+N to connect, Ctrl+Q to quit"; |
505 |
536 |
506 if (text != StatusBarText) |
537 if (text != StatusBarText) |
507 { |
538 { |
508 StatusBarText = text; |
539 StatusBarText = text; |
509 NeedStatusBarRender = true; |
540 NeedStatusBarRender = true; |
773 { |
808 { |
774 CurrentAddress = IPAddress::from_string (current_input()); |
809 CurrentAddress = IPAddress::from_string (current_input()); |
775 } |
810 } |
776 catch (std::exception& e) |
811 catch (std::exception& e) |
777 { |
812 { |
778 print ("%1\n", e.what()); |
813 print ("%s\n", e.what()); |
779 return; |
814 return; |
780 } |
815 } |
781 |
816 |
782 if (CurrentAddress.port == 0) |
817 if (CurrentAddress.port == 0) |
783 CurrentAddress.port = 10666; |
818 CurrentAddress.port = 10666; |
870 } |
905 } |
871 } |
906 } |
872 |
907 |
873 // ------------------------------------------------------------------------------------------------- |
908 // ------------------------------------------------------------------------------------------------- |
874 // |
909 // |
|
910 void Interface::vprint (const char* fmtstr, va_list args) |
|
911 { |
|
912 String message; |
|
913 message.vsprintf (fmtstr, args); |
|
914 print_to_console (message); |
|
915 } |
|
916 |
|
917 // ------------------------------------------------------------------------------------------------- |
|
918 // |
|
919 void __cdecl Interface::print (const char* fmtstr, ...) |
|
920 { |
|
921 va_list args; |
|
922 va_start (args, fmtstr); |
|
923 vprint (fmtstr, args); |
|
924 va_end (args); |
|
925 } |
|
926 |
|
927 // ------------------------------------------------------------------------------------------------- |
|
928 // |
|
929 void __cdecl Interface::print_warning (const char* fmtstr, ...) |
|
930 { |
|
931 va_list args; |
|
932 va_start (args, fmtstr); |
|
933 print_to_console (TEXTCOLOR_BrightYellow "-!- "); |
|
934 vprint (fmtstr, args); |
|
935 print_to_console (TEXTCOLOR_Reset); |
|
936 va_end (args); |
|
937 } |
|
938 |
|
939 // ------------------------------------------------------------------------------------------------- |
|
940 // |
|
941 void __cdecl Interface::print_error (const char* fmtstr, ...) |
|
942 { |
|
943 va_list args; |
|
944 va_start (args, fmtstr); |
|
945 print_to_console (TEXTCOLOR_BrightRed "!!! "); |
|
946 vprint (fmtstr, args); |
|
947 print_to_console (TEXTCOLOR_Reset); |
|
948 va_end (args); |
|
949 } |
|
950 |
|
951 // ------------------------------------------------------------------------------------------------- |
|
952 // |
875 void Interface::print_to_console (String a) |
953 void Interface::print_to_console (String a) |
876 { |
954 { |
877 // Zandronum sometimes sends color codes as "\\c" and sometimes as "\x1C". |
955 // Zandronum sometimes sends color codes as "\\c" and sometimes as "\x1C". |
878 // Let's correct that on our end and hope this won't cause conflicts. |
956 // Let's correct that on our end and hope this won't cause conflicts. |
879 a.replace ("\\c", "\x1C"); |
957 a.replace ("\\c", "\x1C"); |
912 { |
990 { |
913 CurrentAddress = IPAddress::from_string (address); |
991 CurrentAddress = IPAddress::from_string (address); |
914 } |
992 } |
915 catch (std::exception& e) |
993 catch (std::exception& e) |
916 { |
994 { |
917 print ("%1\n", e.what()); |
995 print ("%s\n", e.what()); |
918 return; |
996 return; |
919 } |
997 } |
920 |
998 |
921 if (CurrentAddress.port == 0) |
999 if (CurrentAddress.port == 0) |
922 CurrentAddress.port = 10666; |
1000 CurrentAddress.port = 10666; |
928 |
1006 |
929 // ------------------------------------------------------------------------------------------------- |
1007 // ------------------------------------------------------------------------------------------------- |
930 // |
1008 // |
931 void Interface::set_player_names (const StringList& names) |
1009 void Interface::set_player_names (const StringList& names) |
932 { |
1010 { |
933 PlayerNames.clear(); |
1011 PlayerNames = names; |
934 |
|
935 for (const String& name : names) |
|
936 { |
|
937 ColoredLine coloredname; |
|
938 coloredname.add_string (name); |
|
939 coloredname.finalize(); |
|
940 PlayerNames.append (coloredname); |
|
941 } |
|
942 |
|
943 NeedNicklistRender = true; |
1012 NeedNicklistRender = true; |
944 } |
1013 } |
945 |
1014 |
946 // ------------------------------------------------------------------------------------------------- |
1015 // ------------------------------------------------------------------------------------------------- |
947 // |
1016 // |
957 input.replace (0, part.length(), complete); |
1026 input.replace (0, part.length(), complete); |
958 CursorPosition = complete.length(); |
1027 CursorPosition = complete.length(); |
959 NeedInputRender = true; |
1028 NeedInputRender = true; |
960 } |
1029 } |
961 } |
1030 } |
962 |
|
963 // ------------------------------------------------------------------------------------------------- |
|
964 // |
|
965 void Interface::disconnected() |
|
966 { |
|
967 Title = ""; |
|
968 update_statusbar(); |
|
969 render_titlebar(); |
|
970 } |
|