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.

281 lines
8.8KB

  1. /*
  2. Copyright (c) 2023 : Ognjen 'xolatile' Milan Robovic
  3. Xurses is free software! You will redistribute it or modify it under the terms of the GNU General Public License by Free Software Foundation.
  4. 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.
  5. 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.
  6. */
  7. #ifndef XURSES_SOURCE
  8. #define XURSES_SOURCE
  9. #include <xolatile/xtandard.c>
  10. #include <xolatile/xurses.h>
  11. #include <termios.h>
  12. #include <sys/ioctl.h>
  13. #include <sys/types.h>
  14. #include <sys/stat.h>
  15. /* Internal constant definitions. */
  16. #define CURSES_FORMAT ((int) sizeof ("\033[-;3-m-\033[0m") - 1)
  17. #define CURSES_REVERT ((int) sizeof ("\033[H") - 1)
  18. #define CURSES_CURSOR ((int) sizeof ("\033[---;---H") - 1)
  19. /* Internal variable definitions. */
  20. static char curses_format [CURSES_FORMAT + 1] = "\033[-;3-m-\033[0m";
  21. static char curses_cursor [CURSES_CURSOR + 1] = "\033[---;---H";
  22. static char * curses_screen = NULL;
  23. static int curses_action_count = 0;
  24. static int * curses_activator = NULL;
  25. static void (* * curses_action) (void) = NULL;
  26. static struct termios curses_old_terminal;
  27. static struct termios curses_new_terminal;
  28. /* Internal function definitions. */
  29. static void curses_free (void) {
  30. curses_screen = deallocate (curses_screen);
  31. curses_activator = deallocate (curses_activator);
  32. curses_action = deallocate (curses_action);
  33. terminal_clear ();
  34. fatal_failure (tcsetattr (STDIN_FILENO, TCSAFLUSH, & curses_old_terminal) == -1, "tcsetattr: Failed to set default terminal attributes.");
  35. }
  36. /* Return offset of variable 'curses_screen' according to X and Y coordinates. */
  37. static char * curses_screen_offset (int x, int y) {
  38. /*log_in (LOG_FAILURE, x <= -1, "curses_screen_offset: X position is below the lower bound.");
  39. log_in (LOG_FAILURE, y <= -1, "curses_screen_offset: Y position is below the lower bound.");
  40. log_in (LOG_FAILURE, x >= curses_screen_width, "curses_screen_offset: X position is above the upper bound.");
  41. log_in (LOG_FAILURE, y >= curses_screen_height, "curses_screen_offset: Y position is above the upper bound.");*/
  42. limit (& x, 0, curses_screen_width - 1);
  43. limit (& y, 0, curses_screen_height - 1);
  44. return (& curses_screen [CURSES_REVERT + CURSES_FORMAT * (y * curses_screen_width + x)]);
  45. }
  46. static char * curses_format_character (char character, int colour, int effect) {
  47. /*log_in (LOG_WARNING, character_is_invisible (character), "curses_format_character: Can not format invisible characters.");
  48. log_in (LOG_FAILURE, colour >= COLOUR_COUNT, "curses_format_character: Colour is invalid enumeration value.");
  49. log_in (LOG_FAILURE, effect >= EFFECT_COUNT, "curses_format_character: Effect is invalid enumeration value.");*/
  50. if (character_is_invisible (character) != 0) {
  51. character = ' ';
  52. }
  53. colour %= COLOUR_COUNT;
  54. effect %= EFFECT_COUNT;
  55. switch (effect) {
  56. case EFFECT_NORMAL: effect = 0; break;
  57. case EFFECT_BOLD: effect = 1; break;
  58. case EFFECT_ITALIC: effect = 3; break;
  59. case EFFECT_UNDERLINE: effect = 4; break;
  60. case EFFECT_BLINK: effect = 5; break;
  61. case EFFECT_REVERSE: effect = 7; break;
  62. default: effect = 0; break;
  63. }
  64. curses_format [2] = (char) effect + '0';
  65. curses_format [5] = (char) colour + '0';
  66. curses_format [7] = character;
  67. /*log_out ("curses.log");*/
  68. return (curses_format);
  69. }
  70. static void curses_idle (void) {
  71. return;
  72. }
  73. static void curses_exit (void) {
  74. curses_active = 0;
  75. }
  76. /* External variable definitions. */
  77. int curses_realign_x = 0;
  78. int curses_realign_y = 0;
  79. int curses_tab_width = 8;
  80. int curses_character = 0;
  81. int curses_signal = SIGNAL_NONE;
  82. int curses_screen_width = 0;
  83. int curses_screen_height = 0;
  84. int curses_active = 1;
  85. /* External function definitions. */
  86. void curses_configure (void) {
  87. struct winsize screen_dimension;
  88. atexit (curses_free);
  89. fatal_failure (ioctl (STDOUT_FILENO, TIOCGWINSZ, & screen_dimension) == -1, "ioctl: Failed to get terminal dimensions.");
  90. curses_screen_width = (int) screen_dimension.ws_col;
  91. curses_screen_height = (int) screen_dimension.ws_row;
  92. fatal_failure (tcgetattr (STDIN_FILENO, & curses_old_terminal) == -1, "tcgetattr: Failed to get default terminal attributes.");
  93. curses_new_terminal = curses_old_terminal;
  94. curses_new_terminal.c_cc [VMIN] = (unsigned char) 0;
  95. curses_new_terminal.c_cc [VTIME] = (unsigned char) 1;
  96. curses_new_terminal.c_iflag &= (unsigned int) ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
  97. curses_new_terminal.c_oflag &= (unsigned int) ~(OPOST);
  98. curses_new_terminal.c_cflag |= (unsigned int) (CS8);
  99. curses_new_terminal.c_lflag &= (unsigned int) ~(ECHO | ICANON | IEXTEN | ISIG);
  100. fatal_failure (tcsetattr (STDIN_FILENO, TCSAFLUSH, & curses_new_terminal) == -1, "tcsetattr: Failed to set reverse terminal attributes.");
  101. curses_screen = allocate (CURSES_REVERT + CURSES_FORMAT * curses_screen_width * curses_screen_height + CURSES_CURSOR + 1);
  102. curses_bind (SIGNAL_ESCAPE, curses_exit);
  103. terminal_clear ();
  104. string_copy (& curses_screen [0], "\033[H");
  105. }
  106. void curses_synchronize (void) {
  107. int signal;
  108. curses_signal = curses_character = signal = 0;
  109. out (curses_screen, CURSES_REVERT + CURSES_FORMAT * curses_screen_width * curses_screen_height + CURSES_CURSOR);
  110. in (& signal, 4);
  111. curses_character = signal;
  112. if (signal == '\033') {
  113. curses_signal |= SIGNAL_ESCAPE;
  114. } else if (character_is_digit (signal) != 0) {
  115. curses_signal |= SIGNAL_0 + (int) (signal - '0');
  116. } else if (character_is_lowercase (signal) != 0) {
  117. curses_signal |= SIGNAL_A + (int) (signal - 'a');
  118. } else if (character_is_uppercase (signal) != 0) {
  119. curses_signal |= SIGNAL_A + (int) (signal - 'A');
  120. curses_signal |= SIGNAL_SHIFT;
  121. } else {
  122. curses_signal = SIGNAL_NONE;
  123. }
  124. for (signal = 0; signal != curses_action_count; ++signal) {
  125. if (curses_signal == curses_activator [signal]) {
  126. curses_action [signal] ();
  127. }
  128. }
  129. }
  130. void curses_bind (int signal, void (* action) (void)) {
  131. ++curses_action_count;
  132. curses_activator = reallocate (curses_activator, curses_action_count * (int) sizeof (* curses_activator));
  133. curses_action = reallocate (curses_action, curses_action_count * (int) sizeof (* curses_action));
  134. curses_activator [curses_action_count - 1] = signal;
  135. curses_action [curses_action_count - 1] = action;
  136. }
  137. void curses_unbind (int signal) {
  138. (void) signal;
  139. curses_activator [curses_action_count - 1] = SIGNAL_NONE;
  140. curses_action [curses_action_count - 1] = curses_idle;
  141. --curses_action_count;
  142. }
  143. void curses_render_cursor (int x, int y) {
  144. x %= 1000;
  145. y %= 1000;
  146. string_copy_limit (curses_cursor + 2, string_realign (number_to_string (y), 3, '0'), 3);
  147. string_copy_limit (curses_cursor + 6, string_realign (number_to_string (x), 3, '0'), 3);
  148. string_copy_limit (& curses_screen [CURSES_REVERT + CURSES_FORMAT * curses_screen_width * curses_screen_height], curses_cursor, CURSES_CURSOR);
  149. }
  150. void curses_render_character (char character, int colour, int effect, int x, int y) {
  151. if ((x >= curses_screen_width) || (y >= curses_screen_height)) {
  152. return;
  153. }
  154. string_copy_limit (curses_screen_offset (x, y), curses_format_character (character, colour, effect), CURSES_FORMAT);
  155. }
  156. void curses_render_background (char character, int colour, int effect) {
  157. int x, y;
  158. for (y = 0; y != curses_screen_height; ++y) {
  159. for (x = 0; x != curses_screen_width; ++x) {
  160. curses_render_character (character, colour, effect, x, y);
  161. }
  162. }
  163. }
  164. void curses_render_string_point (char * string, int limit, int colour, int effect, int * x, int * y) {
  165. int offset;
  166. for (offset = 0; offset != limit; ++offset) {
  167. if (string [offset] == '\n') {
  168. * x = curses_realign_x;
  169. * y += 1;
  170. } else if (string [offset] == '\t') {
  171. * x += curses_tab_width;
  172. } else {
  173. curses_render_character (string [offset], colour, effect, * x, * y);
  174. * x += 1;
  175. }
  176. }
  177. }
  178. void curses_render_number_point (int number, int limit, int colour, int effect, int * x, int * y) {
  179. (void) number;
  180. (void) limit;
  181. (void) colour;
  182. (void) effect;
  183. (void) x;
  184. (void) y;
  185. return;
  186. }
  187. void curses_render_string_limit (char * string, int limit, int colour, int effect, int x, int y) {
  188. int xx = x, yy = y;
  189. curses_render_string_point (string, limit, colour, effect, & xx, & yy);
  190. }
  191. void curses_render_number_limit (int number, int limit, int colour, int effect, int x, int y) {
  192. (void) number;
  193. (void) limit;
  194. (void) colour;
  195. (void) effect;
  196. (void) x;
  197. (void) y;
  198. return;
  199. }
  200. void curses_render_string (char * string, int colour, int effect, int x, int y) {
  201. curses_render_string_limit (string, string_length (string), colour, effect, x, y);
  202. }
  203. void curses_render_number (int number, int colour, int effect, int x, int y) {
  204. curses_render_number_limit (number, 4, colour, effect, x, y);
  205. }
  206. #endif