windows_mphal.c 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. /*
  2. * This file is part of the MicroPython project, http://micropython.org/
  3. *
  4. * The MIT License (MIT)
  5. *
  6. * Copyright (c) 2015 Damien P. George
  7. *
  8. * Permission is hereby granted, free of charge, to any person obtaining a copy
  9. * of this software and associated documentation files (the "Software"), to deal
  10. * in the Software without restriction, including without limitation the rights
  11. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12. * copies of the Software, and to permit persons to whom the Software is
  13. * furnished to do so, subject to the following conditions:
  14. *
  15. * The above copyright notice and this permission notice shall be included in
  16. * all copies or substantial portions of the Software.
  17. *
  18. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24. * THE SOFTWARE.
  25. */
  26. #include "py/mpstate.h"
  27. #include "py/mphal.h"
  28. #include <sys/time.h>
  29. #include <windows.h>
  30. #include <unistd.h>
  31. HANDLE std_in = NULL;
  32. HANDLE con_out = NULL;
  33. DWORD orig_mode = 0;
  34. STATIC void assure_stdin_handle() {
  35. if (!std_in) {
  36. std_in = GetStdHandle(STD_INPUT_HANDLE);
  37. assert(std_in != INVALID_HANDLE_VALUE);
  38. }
  39. }
  40. STATIC void assure_conout_handle() {
  41. if (!con_out) {
  42. con_out = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE,
  43. FILE_SHARE_READ | FILE_SHARE_WRITE,
  44. NULL, OPEN_EXISTING, 0, 0);
  45. assert(con_out != INVALID_HANDLE_VALUE);
  46. }
  47. }
  48. void mp_hal_stdio_mode_raw(void) {
  49. assure_stdin_handle();
  50. GetConsoleMode(std_in, &orig_mode);
  51. DWORD mode = orig_mode;
  52. mode &= ~ENABLE_ECHO_INPUT;
  53. mode &= ~ENABLE_LINE_INPUT;
  54. mode &= ~ENABLE_PROCESSED_INPUT;
  55. SetConsoleMode(std_in, mode);
  56. }
  57. void mp_hal_stdio_mode_orig(void) {
  58. assure_stdin_handle();
  59. SetConsoleMode(std_in, orig_mode);
  60. }
  61. // Handler to be installed by SetConsoleCtrlHandler, currently used only to handle Ctrl-C.
  62. // This handler has to be installed just once (this has to be done elswhere in init code).
  63. // Previous versions of the mp_hal code would install a handler whenever Ctrl-C input is
  64. // allowed and remove the handler again when it is not. That is not necessary though (1),
  65. // and it might introduce problems (2) because console notifications are delivered to the
  66. // application in a separate thread.
  67. // (1) mp_hal_set_interrupt_char effectively enables/disables processing of Ctrl-C via the
  68. // ENABLE_PROCESSED_INPUT flag so in raw mode console_sighandler won't be called.
  69. // (2) if mp_hal_set_interrupt_char would remove the handler while Ctrl-C was issued earlier,
  70. // the thread created for handling it might not be running yet so we'd miss the notification.
  71. BOOL WINAPI console_sighandler(DWORD evt) {
  72. if (evt == CTRL_C_EVENT) {
  73. if (MP_STATE_VM(mp_pending_exception) == MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception))) {
  74. // this is the second time we are called, so die straight away
  75. exit(1);
  76. }
  77. mp_obj_exception_clear_traceback(MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception)));
  78. MP_STATE_VM(mp_pending_exception) = MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception));
  79. return TRUE;
  80. }
  81. return FALSE;
  82. }
  83. void mp_hal_set_interrupt_char(char c) {
  84. assure_stdin_handle();
  85. if (c == CHAR_CTRL_C) {
  86. DWORD mode;
  87. GetConsoleMode(std_in, &mode);
  88. mode |= ENABLE_PROCESSED_INPUT;
  89. SetConsoleMode(std_in, mode);
  90. } else {
  91. DWORD mode;
  92. GetConsoleMode(std_in, &mode);
  93. mode &= ~ENABLE_PROCESSED_INPUT;
  94. SetConsoleMode(std_in, mode);
  95. }
  96. }
  97. void mp_hal_move_cursor_back(uint pos) {
  98. assure_conout_handle();
  99. CONSOLE_SCREEN_BUFFER_INFO info;
  100. GetConsoleScreenBufferInfo(con_out, &info);
  101. info.dwCursorPosition.X -= (short)pos;
  102. if (info.dwCursorPosition.X < 0) {
  103. info.dwCursorPosition.X = 0;
  104. }
  105. SetConsoleCursorPosition(con_out, info.dwCursorPosition);
  106. }
  107. void mp_hal_erase_line_from_cursor(uint n_chars_to_erase) {
  108. assure_conout_handle();
  109. CONSOLE_SCREEN_BUFFER_INFO info;
  110. GetConsoleScreenBufferInfo(con_out, &info);
  111. DWORD written;
  112. FillConsoleOutputCharacter(con_out, ' ', n_chars_to_erase, info.dwCursorPosition, &written);
  113. FillConsoleOutputAttribute(con_out, info.wAttributes, n_chars_to_erase, info.dwCursorPosition, &written);
  114. }
  115. typedef struct item_t {
  116. WORD vkey;
  117. const char *seq;
  118. } item_t;
  119. // map virtual key codes to VT100 escape sequences
  120. STATIC item_t keyCodeMap[] = {
  121. {VK_UP, "[A"},
  122. {VK_DOWN, "[B"},
  123. {VK_RIGHT, "[C"},
  124. {VK_LEFT, "[D"},
  125. {VK_HOME, "[H"},
  126. {VK_END, "[F"},
  127. {VK_DELETE, "[3~"},
  128. {0, ""} //sentinel
  129. };
  130. STATIC const char *cur_esc_seq = NULL;
  131. STATIC int esc_seq_process_vk(int vk) {
  132. for (item_t *p = keyCodeMap; p->vkey != 0; ++p) {
  133. if (p->vkey == vk) {
  134. cur_esc_seq = p->seq;
  135. return 27; // ESC, start of escape sequence
  136. }
  137. }
  138. return 0; // nothing found
  139. }
  140. STATIC int esc_seq_chr() {
  141. if (cur_esc_seq) {
  142. const char c = *cur_esc_seq++;
  143. if (c) {
  144. return c;
  145. }
  146. cur_esc_seq = NULL;
  147. }
  148. return 0;
  149. }
  150. int mp_hal_stdin_rx_chr(void) {
  151. // currently processing escape seq?
  152. const int ret = esc_seq_chr();
  153. if (ret) {
  154. return ret;
  155. }
  156. // poll until key which we handle is pressed
  157. assure_stdin_handle();
  158. DWORD num_read;
  159. INPUT_RECORD rec;
  160. for (;;) {
  161. if (!ReadConsoleInput(std_in, &rec, 1, &num_read) || !num_read) {
  162. return CHAR_CTRL_C; // EOF, ctrl-D
  163. }
  164. if (rec.EventType != KEY_EVENT || !rec.Event.KeyEvent.bKeyDown) { // only want key down events
  165. continue;
  166. }
  167. const char c = rec.Event.KeyEvent.uChar.AsciiChar;
  168. if (c) { // plain ascii char, return it
  169. return c;
  170. }
  171. const int ret = esc_seq_process_vk(rec.Event.KeyEvent.wVirtualKeyCode);
  172. if (ret) {
  173. return ret;
  174. }
  175. }
  176. }
  177. void mp_hal_stdout_tx_strn(const char *str, size_t len) {
  178. write(1, str, len);
  179. }
  180. void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len) {
  181. mp_hal_stdout_tx_strn(str, len);
  182. }
  183. void mp_hal_stdout_tx_str(const char *str) {
  184. mp_hal_stdout_tx_strn(str, strlen(str));
  185. }
  186. mp_uint_t mp_hal_ticks_ms(void) {
  187. struct timeval tv;
  188. gettimeofday(&tv, NULL);
  189. return tv.tv_sec * 1000 + tv.tv_usec / 1000;
  190. }
  191. mp_uint_t mp_hal_ticks_us(void) {
  192. struct timeval tv;
  193. gettimeofday(&tv, NULL);
  194. return tv.tv_sec * 1000000 + tv.tv_usec;
  195. }
  196. mp_uint_t mp_hal_ticks_cpu(void) {
  197. LARGE_INTEGER value;
  198. QueryPerformanceCounter(&value);
  199. #ifdef _WIN64
  200. return value.QuadPart;
  201. #else
  202. return value.LowPart;
  203. #endif
  204. }