console.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. /*
  2. * GRUB -- GRand Unified Bootloader
  3. * Copyright (C) 2006,2007,2008 Free Software Foundation, Inc.
  4. *
  5. * GRUB is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * GRUB is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include <grub/term.h>
  19. #include <grub/misc.h>
  20. #include <grub/types.h>
  21. #include <grub/err.h>
  22. #include <grub/efi/efi.h>
  23. #include <grub/efi/api.h>
  24. #include <grub/efi/console.h>
  25. static grub_uint32_t
  26. map_char (grub_uint32_t c)
  27. {
  28. /* Map some unicode characters to the EFI character. */
  29. switch (c)
  30. {
  31. case GRUB_UNICODE_LEFTARROW:
  32. c = GRUB_UNICODE_BLACK_LEFT_TRIANGLE;
  33. break;
  34. case GRUB_UNICODE_UPARROW:
  35. c = GRUB_UNICODE_BLACK_UP_TRIANGLE;
  36. break;
  37. case GRUB_UNICODE_RIGHTARROW:
  38. c = GRUB_UNICODE_BLACK_RIGHT_TRIANGLE;
  39. break;
  40. case GRUB_UNICODE_DOWNARROW:
  41. c = GRUB_UNICODE_BLACK_DOWN_TRIANGLE;
  42. break;
  43. case GRUB_UNICODE_HLINE:
  44. c = GRUB_UNICODE_LIGHT_HLINE;
  45. break;
  46. case GRUB_UNICODE_VLINE:
  47. c = GRUB_UNICODE_LIGHT_VLINE;
  48. break;
  49. case GRUB_UNICODE_CORNER_UL:
  50. c = GRUB_UNICODE_LIGHT_CORNER_UL;
  51. break;
  52. case GRUB_UNICODE_CORNER_UR:
  53. c = GRUB_UNICODE_LIGHT_CORNER_UR;
  54. break;
  55. case GRUB_UNICODE_CORNER_LL:
  56. c = GRUB_UNICODE_LIGHT_CORNER_LL;
  57. break;
  58. case GRUB_UNICODE_CORNER_LR:
  59. c = GRUB_UNICODE_LIGHT_CORNER_LR;
  60. break;
  61. }
  62. return c;
  63. }
  64. static void
  65. grub_console_putchar (struct grub_term_output *term __attribute__ ((unused)),
  66. const struct grub_unicode_glyph *c)
  67. {
  68. grub_efi_char16_t str[2 + 30];
  69. grub_efi_simple_text_output_interface_t *o;
  70. unsigned i, j;
  71. if (grub_efi_is_finished)
  72. return;
  73. o = grub_efi_system_table->con_out;
  74. /* For now, do not try to use a surrogate pair. */
  75. if (c->base > 0xffff)
  76. str[0] = '?';
  77. else
  78. str[0] = (grub_efi_char16_t) map_char (c->base & 0xffff);
  79. j = 1;
  80. for (i = 0; i < c->ncomb && j + 1 < ARRAY_SIZE (str); i++)
  81. if (c->base < 0xffff)
  82. str[j++] = grub_unicode_get_comb (c)[i].code;
  83. str[j] = 0;
  84. /* Should this test be cached? */
  85. if ((c->base > 0x7f || c->ncomb)
  86. && efi_call_2 (o->test_string, o, str) != GRUB_EFI_SUCCESS)
  87. return;
  88. efi_call_2 (o->output_string, o, str);
  89. }
  90. const unsigned efi_codes[] =
  91. {
  92. 0, GRUB_TERM_KEY_UP, GRUB_TERM_KEY_DOWN, GRUB_TERM_KEY_RIGHT,
  93. GRUB_TERM_KEY_LEFT, GRUB_TERM_KEY_HOME, GRUB_TERM_KEY_END, GRUB_TERM_KEY_INSERT,
  94. GRUB_TERM_KEY_DC, GRUB_TERM_KEY_PPAGE, GRUB_TERM_KEY_NPAGE, GRUB_TERM_KEY_F1,
  95. GRUB_TERM_KEY_F2, GRUB_TERM_KEY_F3, GRUB_TERM_KEY_F4, GRUB_TERM_KEY_F5,
  96. GRUB_TERM_KEY_F6, GRUB_TERM_KEY_F7, GRUB_TERM_KEY_F8, GRUB_TERM_KEY_F9,
  97. GRUB_TERM_KEY_F10, GRUB_TERM_KEY_F11, GRUB_TERM_KEY_F12, '\e'
  98. };
  99. static int
  100. grub_efi_translate_key (grub_efi_input_key_t key)
  101. {
  102. if (key.scan_code == 0)
  103. {
  104. /* Some firmware implementations use VT100-style codes against the spec.
  105. This is especially likely if driven by serial.
  106. */
  107. if (key.unicode_char < 0x20 && key.unicode_char != 0
  108. && key.unicode_char != '\t' && key.unicode_char != '\b'
  109. && key.unicode_char != '\n' && key.unicode_char != '\r')
  110. return GRUB_TERM_CTRL | (key.unicode_char - 1 + 'a');
  111. else
  112. return key.unicode_char;
  113. }
  114. else if (key.scan_code < ARRAY_SIZE (efi_codes))
  115. return efi_codes[key.scan_code];
  116. if ((key.unicode_char >= 0x20 && key.unicode_char <= 0x7f)
  117. || key.unicode_char == '\t' || key.unicode_char == '\b'
  118. || key.unicode_char == '\n' || key.unicode_char == '\r')
  119. return key.unicode_char;
  120. return GRUB_TERM_NO_KEY;
  121. }
  122. static int
  123. grub_console_getkey_con (struct grub_term_input *term __attribute__ ((unused)))
  124. {
  125. grub_efi_simple_input_interface_t *i;
  126. grub_efi_input_key_t key;
  127. grub_efi_status_t status;
  128. i = grub_efi_system_table->con_in;
  129. status = efi_call_2 (i->read_key_stroke, i, &key);
  130. if (status != GRUB_EFI_SUCCESS)
  131. return GRUB_TERM_NO_KEY;
  132. return grub_efi_translate_key(key);
  133. }
  134. static int
  135. grub_console_getkey_ex(struct grub_term_input *term)
  136. {
  137. grub_efi_key_data_t key_data;
  138. grub_efi_status_t status;
  139. grub_efi_uint32_t kss;
  140. int key = -1;
  141. grub_efi_simple_text_input_ex_interface_t *text_input = term->data;
  142. status = efi_call_2 (text_input->read_key_stroke, text_input, &key_data);
  143. if (status != GRUB_EFI_SUCCESS)
  144. return GRUB_TERM_NO_KEY;
  145. kss = key_data.key_state.key_shift_state;
  146. key = grub_efi_translate_key(key_data.key);
  147. if (key == GRUB_TERM_NO_KEY)
  148. return GRUB_TERM_NO_KEY;
  149. if (kss & GRUB_EFI_SHIFT_STATE_VALID)
  150. {
  151. if ((kss & GRUB_EFI_LEFT_SHIFT_PRESSED
  152. || kss & GRUB_EFI_RIGHT_SHIFT_PRESSED)
  153. && (key & GRUB_TERM_EXTENDED))
  154. key |= GRUB_TERM_SHIFT;
  155. if (kss & GRUB_EFI_LEFT_ALT_PRESSED || kss & GRUB_EFI_RIGHT_ALT_PRESSED)
  156. key |= GRUB_TERM_ALT;
  157. if (kss & GRUB_EFI_LEFT_CONTROL_PRESSED
  158. || kss & GRUB_EFI_RIGHT_CONTROL_PRESSED)
  159. key |= GRUB_TERM_CTRL;
  160. }
  161. return key;
  162. }
  163. static grub_err_t
  164. grub_efi_console_input_init (struct grub_term_input *term)
  165. {
  166. grub_efi_guid_t text_input_ex_guid =
  167. GRUB_EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
  168. if (grub_efi_is_finished)
  169. return 0;
  170. grub_efi_simple_text_input_ex_interface_t *text_input = term->data;
  171. if (text_input)
  172. return 0;
  173. text_input = grub_efi_open_protocol(grub_efi_system_table->console_in_handler,
  174. &text_input_ex_guid,
  175. GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
  176. term->data = (void *)text_input;
  177. return 0;
  178. }
  179. static int
  180. grub_console_getkey (struct grub_term_input *term)
  181. {
  182. if (grub_efi_is_finished)
  183. return 0;
  184. if (term->data)
  185. return grub_console_getkey_ex(term);
  186. else
  187. return grub_console_getkey_con(term);
  188. }
  189. static struct grub_term_coordinate
  190. grub_console_getwh (struct grub_term_output *term __attribute__ ((unused)))
  191. {
  192. grub_efi_simple_text_output_interface_t *o;
  193. grub_efi_uintn_t columns, rows;
  194. o = grub_efi_system_table->con_out;
  195. if (grub_efi_is_finished || efi_call_4 (o->query_mode, o, o->mode->mode,
  196. &columns, &rows) != GRUB_EFI_SUCCESS)
  197. {
  198. /* Why does this fail? */
  199. columns = 80;
  200. rows = 25;
  201. }
  202. return (struct grub_term_coordinate) { columns, rows };
  203. }
  204. static struct grub_term_coordinate
  205. grub_console_getxy (struct grub_term_output *term __attribute__ ((unused)))
  206. {
  207. grub_efi_simple_text_output_interface_t *o;
  208. if (grub_efi_is_finished)
  209. return (struct grub_term_coordinate) { 0, 0 };
  210. o = grub_efi_system_table->con_out;
  211. return (struct grub_term_coordinate) { o->mode->cursor_column, o->mode->cursor_row };
  212. }
  213. static void
  214. grub_console_gotoxy (struct grub_term_output *term __attribute__ ((unused)),
  215. struct grub_term_coordinate pos)
  216. {
  217. grub_efi_simple_text_output_interface_t *o;
  218. if (grub_efi_is_finished)
  219. return;
  220. o = grub_efi_system_table->con_out;
  221. efi_call_3 (o->set_cursor_position, o, pos.x, pos.y);
  222. }
  223. static void
  224. grub_console_cls (struct grub_term_output *term __attribute__ ((unused)))
  225. {
  226. grub_efi_simple_text_output_interface_t *o;
  227. grub_efi_int32_t orig_attr;
  228. if (grub_efi_is_finished)
  229. return;
  230. o = grub_efi_system_table->con_out;
  231. orig_attr = o->mode->attribute;
  232. efi_call_2 (o->set_attributes, o, GRUB_EFI_BACKGROUND_BLACK);
  233. efi_call_1 (o->clear_screen, o);
  234. efi_call_2 (o->set_attributes, o, orig_attr);
  235. }
  236. static void
  237. grub_console_setcolorstate (struct grub_term_output *term
  238. __attribute__ ((unused)),
  239. grub_term_color_state state)
  240. {
  241. grub_efi_simple_text_output_interface_t *o;
  242. if (grub_efi_is_finished)
  243. return;
  244. o = grub_efi_system_table->con_out;
  245. switch (state) {
  246. case GRUB_TERM_COLOR_STANDARD:
  247. efi_call_2 (o->set_attributes, o, GRUB_TERM_DEFAULT_STANDARD_COLOR
  248. & 0x7f);
  249. break;
  250. case GRUB_TERM_COLOR_NORMAL:
  251. efi_call_2 (o->set_attributes, o, grub_term_normal_color & 0x7f);
  252. break;
  253. case GRUB_TERM_COLOR_HIGHLIGHT:
  254. efi_call_2 (o->set_attributes, o, grub_term_highlight_color & 0x7f);
  255. break;
  256. default:
  257. break;
  258. }
  259. }
  260. static void
  261. grub_console_setcursor (struct grub_term_output *term __attribute__ ((unused)),
  262. int on)
  263. {
  264. grub_efi_simple_text_output_interface_t *o;
  265. if (grub_efi_is_finished)
  266. return;
  267. o = grub_efi_system_table->con_out;
  268. efi_call_2 (o->enable_cursor, o, on);
  269. }
  270. static grub_err_t
  271. grub_efi_console_output_init (struct grub_term_output *term)
  272. {
  273. grub_efi_set_text_mode (1);
  274. grub_console_setcursor (term, 1);
  275. return 0;
  276. }
  277. static grub_err_t
  278. grub_efi_console_output_fini (struct grub_term_output *term)
  279. {
  280. grub_console_setcursor (term, 0);
  281. grub_efi_set_text_mode (0);
  282. return 0;
  283. }
  284. static struct grub_term_input grub_console_term_input =
  285. {
  286. .name = "console",
  287. .getkey = grub_console_getkey,
  288. .init = grub_efi_console_input_init,
  289. };
  290. static struct grub_term_output grub_console_term_output =
  291. {
  292. .name = "console",
  293. .init = grub_efi_console_output_init,
  294. .fini = grub_efi_console_output_fini,
  295. .putchar = grub_console_putchar,
  296. .getwh = grub_console_getwh,
  297. .getxy = grub_console_getxy,
  298. .gotoxy = grub_console_gotoxy,
  299. .cls = grub_console_cls,
  300. .setcolorstate = grub_console_setcolorstate,
  301. .setcursor = grub_console_setcursor,
  302. .flags = GRUB_TERM_CODE_TYPE_VISUAL_GLYPHS,
  303. .progress_update_divisor = GRUB_PROGRESS_FAST
  304. };
  305. void
  306. grub_console_init (void)
  307. {
  308. /* FIXME: it is necessary to consider the case where no console control
  309. is present but the default is already in text mode. */
  310. if (! grub_efi_set_text_mode (1))
  311. {
  312. grub_error (GRUB_ERR_BAD_DEVICE, "cannot set text mode");
  313. return;
  314. }
  315. grub_term_register_output ("console", &grub_console_term_output);
  316. grub_term_register_input ("console", &grub_console_term_input);
  317. }
  318. void
  319. grub_console_fini (void)
  320. {
  321. grub_term_unregister_input (&grub_console_term_input);
  322. grub_term_unregister_output (&grub_console_term_output);
  323. }