sources/interface.cpp

changeset 34
3caf69e7350b
parent 33
bb209480d0ec
child 38
80b26bc9707a
child 42
9699687081df
equal deleted inserted replaced
33:bb209480d0ec 34:3caf69e7350b
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;
207 ::refresh(); 252 ::refresh();
208 ::timeout (0); 253 ::timeout (0);
209 ::use_default_colors(); 254 ::use_default_colors();
210 g_input.clear(); 255 g_input.clear();
211 g_input << ""; 256 g_input << "";
257 g_output.clear();
258 g_output << RendererLine();
212 g_title = format (APPNAME " %1 (%2)", full_version_string(), changeset_date_string()); 259 g_title = format (APPNAME " %1 (%2)", full_version_string(), changeset_date_string());
213 260
214 for (int i = 0; i < NUM_COLORS; ++i) 261 for (int i = 0; i < NUM_COLORS; ++i)
215 for (int j = 0; j < NUM_COLORS; ++j) 262 for (int j = 0; j < NUM_COLORS; ++j)
216 { 263 {
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 //
608 g_needInputRender = true; 708 g_needInputRender = true;
609 } 709 }
610 break; 710 break;
611 711
612 case KEY_PPAGE: 712 case KEY_PPAGE:
613 g_outputScroll += PAGE_SIZE; 713 g_outputScroll += min (g_pageSize, LINES / 2);
614 g_needOutputRender = true; 714 g_needOutputRender = true;
615 break; 715 break;
616 716
617 case KEY_NPAGE: 717 case KEY_NPAGE:
618 g_outputScroll -= PAGE_SIZE; 718 g_outputScroll -= min (g_pageSize, LINES / 2);
619 g_needOutputRender = true; 719 g_needOutputRender = true;
620 break; 720 break;
621 721
622 case '\n': 722 case '\n':
623 case KEY_ENTER: 723 case KEY_ENTER:
697 797
698 for (char ch : a) 798 for (char ch : a)
699 { 799 {
700 if (ch == '\n') 800 if (ch == '\n')
701 { 801 {
702 g_output << ""; 802 g_output[g_output.size() - 1].finalize();
803 g_output << RendererLine();
703 continue; 804 continue;
704 } 805 }
705 806
706 g_output[g_output.size() - 1] += ch; 807 g_output[g_output.size() - 1].add_char (ch);
707 } 808 }
708 809
709 g_needOutputRender = true; 810 g_needOutputRender = true;
710 } 811 }
711 812
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 }

mercurial