31 #include <string.h> |
31 #include <string.h> |
32 #include "interface.h" |
32 #include "interface.h" |
33 #include "network/rconsession.h" |
33 #include "network/rconsession.h" |
34 #include "network/ipaddress.h" |
34 #include "network/ipaddress.h" |
35 |
35 |
36 enum { PAGE_SIZE = 10 }; |
36 enum |
|
37 { |
|
38 RLINE_ON_BLACK = 256, |
|
39 RLINE_ON_RED, |
|
40 RLINE_ON_GREEN, |
|
41 RLINE_ON_YELLOW, |
|
42 RLINE_ON_BLUE, |
|
43 RLINE_ON_MAGENTA, |
|
44 RLINE_ON_CYAN, |
|
45 RLINE_ON_WHITE, |
|
46 RLINE_ON_BOLD, |
|
47 RLINE_OFF_BLACK, |
|
48 RLINE_OFF_RED, |
|
49 RLINE_OFF_GREEN, |
|
50 RLINE_OFF_YELLOW, |
|
51 RLINE_OFF_BLUE, |
|
52 RLINE_OFF_MAGENTA, |
|
53 RLINE_OFF_CYAN, |
|
54 RLINE_OFF_WHITE, |
|
55 RLINE_OFF_BOLD, |
|
56 }; |
|
57 |
|
58 class RendererLine |
|
59 { |
|
60 public: |
|
61 RendererLine() {} |
|
62 |
|
63 METHOD data() const -> const Vector<int>& { return m_data; } |
|
64 METHOD length() const -> int { return m_length; } |
|
65 METHOD add_char (char ch) -> void; |
|
66 METHOD finalize() -> void; |
|
67 METHOD rows (int cols) const -> int; |
|
68 |
|
69 private: |
|
70 METHOD set_color (Color a, bool on) -> void; |
|
71 |
|
72 Vector<int> m_data; |
|
73 int m_length = 0; |
|
74 bool m_final = false; |
|
75 Color m_activeColor = DEFAULT; |
|
76 bool m_boldActive = false; |
|
77 int m_colorCodeStage = 0; |
|
78 String m_string; |
|
79 }; |
|
80 |
|
81 static const int g_pageSize = 10; |
37 |
82 |
38 enum InputState |
83 enum InputState |
39 { |
84 { |
40 INPUTSTATE_NORMAL, |
85 INPUTSTATE_NORMAL, |
41 INPUTSTATE_ADDRESS, |
86 INPUTSTATE_ADDRESS, |
50 static bool g_needRefresh = false; |
95 static bool g_needRefresh = false; |
51 static bool g_needStatusBarRender = false; |
96 static bool g_needStatusBarRender = false; |
52 static bool g_needInputRender = false; |
97 static bool g_needInputRender = false; |
53 static bool g_needOutputRender = false; |
98 static bool g_needOutputRender = false; |
54 static struct { char ch; int x; } g_cursorChar; |
99 static struct { char ch; int x; } g_cursorChar; |
55 static Vector<String> g_output = {""}; |
100 static Vector<RendererLine> g_output;; |
56 static int g_outputScroll = 0; |
101 static int g_outputScroll = 0; |
57 static String g_title; |
102 static String g_title; |
58 static InputState g_inputState = INPUTSTATE_NORMAL; |
103 static InputState g_inputState = INPUTSTATE_NORMAL; |
59 static Function<void (void)> g_disconnectConfirmFunction = nullptr; |
104 static Function<void (void)> g_disconnectConfirmFunction = nullptr; |
60 static IPAddress g_address; |
105 static IPAddress g_address; |
270 // ------------------------------------------------------------------------------------------------- |
317 // ------------------------------------------------------------------------------------------------- |
271 // |
318 // |
272 static FUNCTION |
319 static FUNCTION |
273 interface_render_output() -> void |
320 interface_render_output() -> void |
274 { |
321 { |
|
322 if (g_output.size() == 1) |
|
323 return; |
|
324 |
|
325 g_outputScroll = clamp (g_outputScroll, 0, g_output.size() - 1); |
|
326 |
275 int height = LINES - 3; |
327 int height = LINES - 3; |
276 |
328 int printOffset = 0; |
277 // ensure we're within bounds |
329 int end = g_output.size() - 1 - g_outputScroll; |
278 if (g_outputScroll + height >= g_output.size()) |
330 int start = end; |
279 g_outputScroll = g_output.size() - height - 1; |
331 int usedHeight = 0; |
280 else if (g_outputScroll < 0) |
|
281 g_outputScroll = 0; |
|
282 |
|
283 int start = max (0, g_output.size() - height - 1 - g_outputScroll); |
|
284 int end = min (g_output.size(), start + height); |
|
285 int y = 1; |
332 int y = 1; |
286 assert (end - start <= height); |
333 bool tightFit = false; |
|
334 |
|
335 // Where to start? |
|
336 while (start > 0) |
|
337 { |
|
338 int rows = g_output[start - 1].rows (COLS); |
|
339 |
|
340 if (usedHeight + rows > height) |
|
341 { |
|
342 // This line won't fit anymore. |
|
343 tightFit = true; |
|
344 break; |
|
345 } |
|
346 |
|
347 start--; |
|
348 usedHeight += rows; |
|
349 } |
|
350 |
|
351 // See if there's any more rows to use (end may be too small) |
|
352 if (not tightFit) |
|
353 { |
|
354 while (end < g_output.size()) |
|
355 { |
|
356 int rows = g_output[end].rows (COLS); |
|
357 |
|
358 if (usedHeight + rows > height) |
|
359 { |
|
360 tightFit = true; |
|
361 break; |
|
362 } |
|
363 |
|
364 end++; |
|
365 usedHeight += rows; |
|
366 } |
|
367 } |
|
368 |
|
369 if (start > 0) |
|
370 printOffset = height - usedHeight; |
|
371 |
|
372 g_outputScroll = g_output.size() - 1 - end; |
|
373 |
|
374 if (start < 0 or start == end or printOffset >= height) |
|
375 return; |
|
376 |
|
377 assert (start <= end and start - end <= height); |
|
378 |
|
379 // Clear the display |
|
380 for (int i = y; i < y + height; ++i) |
|
381 mvhline (i, 0, ' ', COLS); |
|
382 |
|
383 // Print the lines |
|
384 y += printOffset; |
287 |
385 |
288 for (int i = start; i < end; ++i) |
386 for (int i = start; i < end; ++i) |
289 { |
387 { |
290 mvhline (y, 0, ' ', COLS); |
|
291 int activeColor = -1; |
|
292 bool boldActive = false; |
|
293 int x = 0; |
388 int x = 0; |
294 |
389 |
295 for (int j = 0; j < g_output[i].length(); ++j) |
390 for (int byte : g_output[i].data()) |
296 { |
391 { |
297 char ch = g_output[i][j]; |
392 if (x == COLS) |
298 |
|
299 if (ch == '\x1C' and j + 1 < g_output[i].length()) |
|
300 { |
393 { |
301 if (activeColor != -1) |
394 x = 0; |
302 attroff (activeColor); |
395 ++y; |
303 |
|
304 if (boldActive) |
|
305 attroff (A_BOLD); |
|
306 |
|
307 char colorChar = g_output[i][j + 1]; |
|
308 |
|
309 if (colorChar >= 'a' and colorChar <= 'v' and colorChar != 'l') |
|
310 { |
|
311 auto colorInfo = g_colorCodes[colorChar - 'a']; |
|
312 activeColor = interface_color_pair (colorInfo.color, DEFAULT); |
|
313 boldActive = colorInfo.bold; |
|
314 attron (activeColor); |
|
315 |
|
316 if (boldActive) |
|
317 attron (A_BOLD); |
|
318 } |
|
319 |
|
320 j++; |
|
321 continue; |
|
322 } |
396 } |
323 |
397 |
324 mvaddch (y, x++, g_output[i][j]); |
398 if (isprint (byte)) |
325 } |
399 mvaddch (y, x++, char (byte)); |
326 |
400 else switch (byte) |
327 if (activeColor != -1) |
401 { |
328 attroff (activeColor); |
402 case RLINE_ON_BLACK: |
329 |
403 case RLINE_ON_GREEN: |
330 if (boldActive) |
404 case RLINE_ON_YELLOW: |
331 attroff (A_BOLD); |
405 case RLINE_ON_BLUE: |
|
406 case RLINE_ON_MAGENTA: |
|
407 case RLINE_ON_CYAN: |
|
408 case RLINE_ON_WHITE: |
|
409 attron (interface_color_pair (Color (byte - RLINE_ON_BLACK), DEFAULT)); |
|
410 break; |
|
411 |
|
412 case RLINE_OFF_BLACK: |
|
413 case RLINE_OFF_GREEN: |
|
414 case RLINE_OFF_YELLOW: |
|
415 case RLINE_OFF_BLUE: |
|
416 case RLINE_OFF_MAGENTA: |
|
417 case RLINE_OFF_CYAN: |
|
418 case RLINE_OFF_WHITE: |
|
419 attroff (interface_color_pair (Color (byte - RLINE_OFF_BLACK), DEFAULT)); |
|
420 break; |
|
421 |
|
422 case RLINE_ON_BOLD: |
|
423 attron (A_BOLD); |
|
424 break; |
|
425 |
|
426 case RLINE_OFF_BOLD: |
|
427 attroff (A_BOLD); |
|
428 break; |
|
429 } |
|
430 } |
332 |
431 |
333 ++y; |
432 ++y; |
334 } |
433 } |
335 |
434 |
|
435 g_needOutputRender = false; |
336 g_needRefresh = true; |
436 g_needRefresh = true; |
337 } |
437 } |
338 |
438 |
339 // ------------------------------------------------------------------------------------------------- |
439 // ------------------------------------------------------------------------------------------------- |
340 // |
440 // |
729 |
830 |
730 RCONSession* session = RCONSession::new_session(); |
831 RCONSession* session = RCONSession::new_session(); |
731 session->set_password (password); |
832 session->set_password (password); |
732 session->connect (g_address); |
833 session->connect (g_address); |
733 } |
834 } |
|
835 |
|
836 // ------------------------------------------------------------------------------------------------- |
|
837 // |
|
838 METHOD |
|
839 RendererLine::finalize() -> void |
|
840 { |
|
841 if (m_activeColor != DEFAULT) |
|
842 this->set_color (m_activeColor, false); |
|
843 |
|
844 if (m_boldActive) |
|
845 m_data << RLINE_OFF_BOLD; |
|
846 |
|
847 m_final = true; |
|
848 } |
|
849 |
|
850 // ------------------------------------------------------------------------------------------------- |
|
851 // |
|
852 METHOD |
|
853 RendererLine::add_char (char ch) -> void |
|
854 { |
|
855 if (m_final) |
|
856 return; // Don't touch finalized lines. |
|
857 |
|
858 if (ch == '\x1C' and m_colorCodeStage == 0) |
|
859 { |
|
860 m_colorCodeStage = 1; |
|
861 return; |
|
862 } |
|
863 |
|
864 if (m_colorCodeStage == 1) |
|
865 { |
|
866 if (m_activeColor != DEFAULT) |
|
867 this->set_color (m_activeColor, false); |
|
868 |
|
869 if (m_boldActive) |
|
870 m_data << RLINE_OFF_BOLD; |
|
871 |
|
872 if (ch >= 'a' and ch <= 'v' and ch != 'l') |
|
873 { |
|
874 auto colorInfo = g_colorCodes[ch - 'a']; |
|
875 m_activeColor = colorInfo.color; |
|
876 m_boldActive = colorInfo.bold; |
|
877 assert (m_activeColor < 8); |
|
878 this->set_color (m_activeColor, true); |
|
879 |
|
880 if (m_boldActive) |
|
881 m_data << RLINE_ON_BOLD; |
|
882 } |
|
883 |
|
884 m_colorCodeStage = 0; |
|
885 return; |
|
886 } |
|
887 |
|
888 if (isprint (ch)) |
|
889 { |
|
890 m_string += ch; |
|
891 m_data << int (ch); |
|
892 ++m_length; |
|
893 } |
|
894 } |
|
895 |
|
896 // ------------------------------------------------------------------------------------------------- |
|
897 // |
|
898 METHOD |
|
899 RendererLine::set_color (Color a, bool on) -> void |
|
900 { |
|
901 switch (a) |
|
902 { |
|
903 case BLACK: m_data << (on ? RLINE_ON_BLACK : RLINE_OFF_BLACK); break; |
|
904 case RED: m_data << (on ? RLINE_ON_RED : RLINE_OFF_RED); break; |
|
905 case GREEN: m_data << (on ? RLINE_ON_GREEN : RLINE_OFF_GREEN); break; |
|
906 case YELLOW: m_data << (on ? RLINE_ON_YELLOW : RLINE_OFF_YELLOW); break; |
|
907 case BLUE: m_data << (on ? RLINE_ON_BLUE : RLINE_OFF_BLUE); break; |
|
908 case MAGENTA: m_data << (on ? RLINE_ON_MAGENTA : RLINE_OFF_MAGENTA); break; |
|
909 case CYAN: m_data << (on ? RLINE_ON_CYAN : RLINE_OFF_CYAN); break; |
|
910 case WHITE: m_data << (on ? RLINE_ON_WHITE : RLINE_OFF_WHITE); break; |
|
911 case NUM_COLORS: |
|
912 case DEFAULT: assert (false); break; |
|
913 } |
|
914 } |
|
915 |
|
916 // ------------------------------------------------------------------------------------------------- |
|
917 // How many rows does this line take up? |
|
918 // |
|
919 METHOD |
|
920 RendererLine::rows (int cols) const -> int |
|
921 { |
|
922 int rows = length() / cols; |
|
923 |
|
924 if (length() % cols != 0) |
|
925 rows++; |
|
926 |
|
927 return max (rows, 1); |
|
928 } |