Xurses...
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

207 lines
6.8KB

  1. /*
  2. * Copyright (c) 2023 : Ognjen 'xolatile' Milan Robovic
  3. *
  4. * Xurses is free software! You will redistribute it or modify it under the terms of the GNU General Public License by Free Software Foundation.
  5. * And when you do redistribute it or modify it, it will use either version 3 of the License, or (at yours truly opinion) any later version.
  6. * It is distributed in the hope that it will be useful or harmful, it really depends... But no warranty what so ever, seriously. See GNU/GPLv3.
  7. */
  8. #ifndef XURSES_SOURCE
  9. #define XURSES_SOURCE
  10. #include <xolatile/xurses.h>
  11. int curses_active = 1;
  12. int curses_cursor = 0;
  13. char curses_signal = '\0';
  14. int curses_screen_width = 0;
  15. int curses_screen_height = 0;
  16. int curses_screen_size = 0;
  17. char * curses_screen = NULL;
  18. char curses_format [CURSES_LENGTH + 1] = "\033[-;3-m-\033[0m";
  19. void (* curses_action [SIGNAL_COUNT]) (void) = { 0 };
  20. struct termios curses_old_terminal;
  21. struct termios curses_new_terminal;
  22. void curses_initialize (void) {
  23. struct winsize screen_dimension;
  24. char signal = 0;
  25. char offset = 0;
  26. fatal_failure (ioctl (STDOUT_FILENO, TIOCGWINSZ, & screen_dimension) == -1, "ioctl: Failed to get terminal dimensions.");
  27. curses_screen_width = (int) screen_dimension.ws_col;
  28. curses_screen_height = (int) screen_dimension.ws_row;
  29. fatal_failure (tcgetattr (STDIN_FILENO, & curses_old_terminal) == -1, "tcgetattr: Failed to get default terminal attributes.");
  30. curses_new_terminal = curses_old_terminal;
  31. curses_new_terminal.c_cc [VMIN] = (unsigned char) 0;
  32. curses_new_terminal.c_cc [VTIME] = (unsigned char) 1;
  33. curses_new_terminal.c_iflag &= (unsigned int) ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
  34. curses_new_terminal.c_oflag &= (unsigned int) ~(OPOST);
  35. curses_new_terminal.c_cflag |= (unsigned int) (CS8);
  36. curses_new_terminal.c_lflag &= (unsigned int) ~(ECHO | ICANON | IEXTEN | ISIG);
  37. fatal_failure (tcsetattr (STDIN_FILENO, TCSAFLUSH, & curses_new_terminal) == -1, "tcsetattr: Failed to set reverse terminal attributes.");
  38. curses_screen = allocate (CURSES_OFFSET + CURSES_LENGTH * curses_screen_width * curses_screen_height + (curses_screen_height - 1) * CURSES_RETURN + 1);
  39. for (signal = SIGNAL_NONE; signal != SIGNAL_COUNT; ++signal) {
  40. curses_unbind ((char) signal);
  41. }
  42. terminal_clear ();
  43. curses_screen_offset ();
  44. for (offset = 0; offset != curses_screen_height - 1; ++offset) {
  45. string_copy (& curses_screen [CURSES_LENGTH * curses_screen_width * offset + CURSES_OFFSET], "\r\n");
  46. }
  47. }
  48. void curses_deinitialize (void) {
  49. curses_screen = deallocate (curses_screen);
  50. terminal_clear ();
  51. fatal_failure (tcsetattr (STDIN_FILENO, TCSAFLUSH, & curses_old_terminal) == -1, "tcsetattr: Failed to set default terminal attributes.");
  52. }
  53. void curses_synchronize (void) {
  54. curses_signal = '\0';
  55. /*
  56. out (curses_screen, curses_screen_size);
  57. */
  58. out (curses_screen, CURSES_OFFSET + CURSES_LENGTH * curses_screen_width * curses_screen_height/* + curses_screen_height * CURSES_RETURN*/);
  59. in (& curses_signal, 1);
  60. switch (curses_signal) {
  61. case '0': curses_signal = SIGNAL_0; break;
  62. case 'Q': curses_signal = SIGNAL_Q | SIGNAL_SHIFT; break;
  63. case 'q': curses_signal = SIGNAL_Q; break;
  64. default: curses_signal = SIGNAL_NONE; break;
  65. }
  66. if ((curses_signal > SIGNAL_ANY) && (curses_signal < SIGNAL_COUNT)) {
  67. curses_action [curses_signal] ();
  68. }
  69. curses_screen_offset ();
  70. }
  71. void curses_configure (void) {
  72. atexit (curses_deinitialize);
  73. curses_initialize ();
  74. }
  75. void curses_screen_offset (void) {
  76. string_copy (& curses_screen [0], "\033[H");
  77. curses_screen_size = CURSES_OFFSET;
  78. }
  79. char * curses_screen_position (int x, int y) {
  80. fatal_failure (x <= -1, "curses_screen_position: X position is below the lower bound.");
  81. fatal_failure (y <= -1, "curses_screen_position: Y position is below the lower bound.");
  82. fatal_failure (x >= curses_screen_width, "curses_screen_position: X position is above the upper bound.");
  83. fatal_failure (y >= curses_screen_height, "curses_screen_position: Y position is above the upper bound.");
  84. return (& curses_screen [CURSES_LENGTH * (y * curses_screen_width + x) + y * CURSES_RETURN + CURSES_OFFSET]);
  85. }
  86. char * curses_format_character (char character, int colour, int effect) {
  87. log_in (LOG_WARNING, character_is_invisible (character), "curses_format_character: Can not format invisible characters.");
  88. log_in (LOG_FAILURE, colour >= COLOUR_COUNT, "curses_format_character: Colour is invalid enumeration value.");
  89. log_in (LOG_FAILURE, effect >= EFFECT_COUNT, "curses_format_character: Effect is invalid enumeration value.");
  90. curses_format [2] = (char) (effect % EFFECT_COUNT) + '0';
  91. curses_format [5] = (char) (colour % COLOUR_COUNT) + '0';
  92. curses_format [7] = character;
  93. log_out ("curses.log");
  94. return (curses_format);
  95. }
  96. void curses_output_character (char character, int colour, int effect) {
  97. out (curses_format_character (character, colour, effect), CURSES_LENGTH);
  98. }
  99. void curses_render_character (char character, int x, int y, int colour, int effect) {
  100. string_copy (curses_screen_position (x, y), curses_format_character (character, colour, effect));
  101. curses_screen_size += CURSES_LENGTH;
  102. }
  103. /*
  104. void curses_output_string (char * string, int colour, int effect) {
  105. char format [8] = "\033[ ;3 m";
  106. format [2] = (char) effect + '0';
  107. format [5] = (char) colour + '0';
  108. string_copy_limit (& curses_screen [curses_screen_size], format, 7);
  109. string_copy_limit (& curses_screen [curses_screen_size], string, string_length (string));
  110. string_copy_limit (& curses_screen [curses_screen_size], "\033[0m", 4);
  111. curses_screen_size += 7 + string_length (string) + 4;
  112. }
  113. void curses_render_string (char * string, int colour, int effect, int x, int y) {
  114. int offset = 0;
  115. int length = string_length (string);
  116. for (offset = 0; offset != length; (++offset, ++x)) {
  117. curses_render_character (string [offset], colour, effect, x, y);
  118. }
  119. }
  120. void curses_append_cursor (int cursor_x, int cursor_y) {
  121. char format [11] = "\033[000;000H";
  122. format [4] = (char) (cursor_y / 1) % 10 + '0';
  123. format [3] = (char) (cursor_y / 10) % 10 + '0';
  124. format [2] = (char) (cursor_y / 100) % 10 + '0';
  125. format [8] = (char) (cursor_x / 1) % 10 + '0';
  126. format [7] = (char) (cursor_x / 10) % 10 + '0';
  127. format [6] = (char) (cursor_x / 100) % 10 + '0';
  128. string_copy_limit (& curses_screen [curses_screen_size], format, 10);
  129. curses_screen_size += 10;
  130. }
  131. */
  132. void curses_blank (void) {
  133. int i, j;
  134. for (i = 0; i != curses_screen_height; ++i) {
  135. for (j = 0; j != curses_screen_width; ++j) {
  136. curses_render_character (' ', EFFECT_NORMAL, COLOUR_WHITE, j, i);
  137. }
  138. }
  139. }
  140. void curses_bind (int signal, void (* action) (void)) {
  141. curses_action [signal] = action;
  142. }
  143. void curses_unbind (int signal) {
  144. curses_action [signal] = curses_idle;
  145. }
  146. void curses_idle (void) {
  147. return;
  148. }
  149. void curses_exit (void) {
  150. curses_active = 0;
  151. }
  152. #endif