Xurses...
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

178 lines
5.9KB

  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. #include <termios.h>
  12. #include <sys/ioctl.h>
  13. #include <sys/types.h>
  14. #include <sys/stat.h>
  15. #define CURSES_LENGTH ((int) sizeof ("\033[-;3-m-\033[0m") - 1)
  16. #define CURSES_OFFSET ((int) sizeof ("\033[H") - 1)
  17. #define CURSES_RETURN ((int) sizeof ("\r\n") - 1)
  18. static int curses_stop = SIGNAL_Q;
  19. static int curses_signal = SIGNAL_NONE;
  20. static int curses_screen_width = 0;
  21. static int curses_screen_height = 0;
  22. static int curses_screen_size = 0;
  23. static char * curses_screen = NULL;
  24. static char curses_format [CURSES_LENGTH + 1] = "\033[-;3-m-\033[0m";
  25. static void (* curses_action [SIGNAL_COUNT]) (void) = { 0 };
  26. static struct termios curses_old_terminal;
  27. static struct termios curses_new_terminal;
  28. int curses_active = 0;
  29. static void curses_free (void) {
  30. curses_screen = deallocate (curses_screen);
  31. terminal_clear ();
  32. fatal_failure (tcsetattr (STDIN_FILENO, TCSAFLUSH, & curses_old_terminal) == -1, "tcsetattr: Failed to set default terminal attributes.");
  33. }
  34. static void curses_screen_offset (void) {
  35. string_copy (& curses_screen [0], "\033[H");
  36. curses_screen_size = CURSES_OFFSET;
  37. }
  38. static char * curses_screen_position (int x, int y) {
  39. fatal_failure (x <= -1, "curses_screen_position: X position is below the lower bound.");
  40. fatal_failure (y <= -1, "curses_screen_position: Y position is below the lower bound.");
  41. fatal_failure (x >= curses_screen_width, "curses_screen_position: X position is above the upper bound.");
  42. fatal_failure (y >= curses_screen_height, "curses_screen_position: Y position is above the upper bound.");
  43. return (& curses_screen [CURSES_LENGTH * (y * curses_screen_width + x) + y * CURSES_RETURN + CURSES_OFFSET]);
  44. }
  45. static char * curses_format_character (char character, int colour, int effect) {
  46. log_in (LOG_WARNING, character_is_invisible (character), "curses_format_character: Can not format invisible characters.");
  47. log_in (LOG_FAILURE, colour >= COLOUR_COUNT, "curses_format_character: Colour is invalid enumeration value.");
  48. log_in (LOG_FAILURE, effect >= EFFECT_COUNT, "curses_format_character: Effect is invalid enumeration value.");
  49. curses_format [2] = (char) (effect % EFFECT_COUNT) + '0';
  50. curses_format [5] = (char) (colour % COLOUR_COUNT) + '0';
  51. curses_format [7] = character;
  52. log_out ("curses.log");
  53. return (curses_format);
  54. }
  55. static void curses_idle (void) {
  56. return;
  57. }
  58. void curses_configure (void) {
  59. struct winsize screen_dimension;
  60. char signal = 0;
  61. char offset = 0;
  62. atexit (curses_free);
  63. fatal_failure (ioctl (STDOUT_FILENO, TIOCGWINSZ, & screen_dimension) == -1, "ioctl: Failed to get terminal dimensions.");
  64. curses_screen_width = (int) screen_dimension.ws_col;
  65. curses_screen_height = (int) screen_dimension.ws_row;
  66. fatal_failure (tcgetattr (STDIN_FILENO, & curses_old_terminal) == -1, "tcgetattr: Failed to get default terminal attributes.");
  67. curses_new_terminal = curses_old_terminal;
  68. curses_new_terminal.c_cc [VMIN] = (unsigned char) 0;
  69. curses_new_terminal.c_cc [VTIME] = (unsigned char) 1;
  70. curses_new_terminal.c_iflag &= (unsigned int) ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
  71. curses_new_terminal.c_oflag &= (unsigned int) ~(OPOST);
  72. curses_new_terminal.c_cflag |= (unsigned int) (CS8);
  73. curses_new_terminal.c_lflag &= (unsigned int) ~(ECHO | ICANON | IEXTEN | ISIG);
  74. fatal_failure (tcsetattr (STDIN_FILENO, TCSAFLUSH, & curses_new_terminal) == -1, "tcsetattr: Failed to set reverse terminal attributes.");
  75. curses_screen = allocate (CURSES_OFFSET + CURSES_LENGTH * curses_screen_width * curses_screen_height + (curses_screen_height - 1) * CURSES_RETURN + 1);
  76. for (signal = SIGNAL_NONE; signal != SIGNAL_COUNT; ++signal) {
  77. curses_unbind ((char) signal);
  78. }
  79. terminal_clear ();
  80. curses_screen_offset ();
  81. for (offset = 0; offset != curses_screen_height - 1; ++offset) {
  82. string_copy (& curses_screen [CURSES_LENGTH * curses_screen_width * offset + CURSES_OFFSET], "\r\n");
  83. }
  84. curses_active = 1;
  85. }
  86. void curses_synchronize (void) {
  87. curses_signal = '\0';
  88. out (curses_screen, CURSES_OFFSET + CURSES_LENGTH * curses_screen_width * curses_screen_height/* + curses_screen_height * CURSES_RETURN*/);
  89. in (& curses_signal, 1);
  90. switch (curses_signal) {
  91. case '0': curses_signal = SIGNAL_0; break;
  92. case 'Q': curses_signal = SIGNAL_Q | SIGNAL_SHIFT; break;
  93. case 'q': curses_signal = SIGNAL_Q; break;
  94. default: curses_signal = SIGNAL_NONE; break;
  95. }
  96. if (curses_signal == curses_stop) {
  97. curses_active = 0;
  98. return;
  99. }
  100. if ((curses_signal > SIGNAL_ANY) && (curses_signal < SIGNAL_COUNT)) {
  101. curses_action [curses_signal] ();
  102. }
  103. curses_screen_offset ();
  104. }
  105. void curses_render_character (char character, int colour, int effect, int x, int y) {
  106. string_copy (curses_screen_position (x, y), curses_format_character (character, colour, effect));
  107. curses_screen_size += CURSES_LENGTH;
  108. }
  109. void curses_render_background (char character, int colour, int effect) {
  110. int x, y;
  111. for (y = 0; y != curses_screen_height; ++y) {
  112. for (x = 0; x != curses_screen_width; ++x) {
  113. curses_render_character (character, colour, effect, x, y);
  114. }
  115. }
  116. }
  117. void curses_bind (int signal, void (* action) (void)) {
  118. curses_action [signal] = action;
  119. }
  120. void curses_unbind (int signal) {
  121. curses_action [signal] = curses_idle;
  122. }
  123. void curses_exit (int signal) {
  124. curses_stop = signal;
  125. }
  126. #endif