console.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  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. typedef enum {
  26. GRUB_TEXT_MODE_UNDEFINED = -1,
  27. GRUB_TEXT_MODE_UNAVAILABLE = 0,
  28. GRUB_TEXT_MODE_AVAILABLE
  29. }
  30. grub_text_mode;
  31. typedef enum {
  32. GRUB_CURSOR_MODE_UNDEFINED = -1,
  33. GRUB_CURSOR_MODE_OFF = 0,
  34. GRUB_CURSUR_MODE_ON
  35. }
  36. grub_cursor_mode;
  37. static grub_text_mode text_mode = GRUB_TEXT_MODE_UNDEFINED;
  38. static grub_cursor_mode cursor_mode = GRUB_CURSOR_MODE_UNDEFINED;
  39. static grub_term_color_state text_colorstate = GRUB_TERM_COLOR_UNDEFINED;
  40. static grub_uint32_t
  41. map_char (grub_uint32_t c)
  42. {
  43. /* Map some unicode characters to the EFI character. */
  44. switch (c)
  45. {
  46. case GRUB_UNICODE_LEFTARROW:
  47. c = GRUB_UNICODE_BLACK_LEFT_TRIANGLE;
  48. break;
  49. case GRUB_UNICODE_UPARROW:
  50. c = GRUB_UNICODE_BLACK_UP_TRIANGLE;
  51. break;
  52. case GRUB_UNICODE_RIGHTARROW:
  53. c = GRUB_UNICODE_BLACK_RIGHT_TRIANGLE;
  54. break;
  55. case GRUB_UNICODE_DOWNARROW:
  56. c = GRUB_UNICODE_BLACK_DOWN_TRIANGLE;
  57. break;
  58. case GRUB_UNICODE_HLINE:
  59. c = GRUB_UNICODE_LIGHT_HLINE;
  60. break;
  61. case GRUB_UNICODE_VLINE:
  62. c = GRUB_UNICODE_LIGHT_VLINE;
  63. break;
  64. case GRUB_UNICODE_CORNER_UL:
  65. c = GRUB_UNICODE_LIGHT_CORNER_UL;
  66. break;
  67. case GRUB_UNICODE_CORNER_UR:
  68. c = GRUB_UNICODE_LIGHT_CORNER_UR;
  69. break;
  70. case GRUB_UNICODE_CORNER_LL:
  71. c = GRUB_UNICODE_LIGHT_CORNER_LL;
  72. break;
  73. case GRUB_UNICODE_CORNER_LR:
  74. c = GRUB_UNICODE_LIGHT_CORNER_LR;
  75. break;
  76. }
  77. return c;
  78. }
  79. static void
  80. grub_console_setcolorstate (struct grub_term_output *term
  81. __attribute__ ((unused)),
  82. grub_term_color_state state)
  83. {
  84. grub_efi_simple_text_output_interface_t *o;
  85. if (grub_efi_is_finished || text_mode != GRUB_TEXT_MODE_AVAILABLE)
  86. {
  87. /*
  88. * Cache colorstate changes before the first text-output, this avoids
  89. * "color_normal" environment writes causing a switch to textmode.
  90. */
  91. text_colorstate = state;
  92. return;
  93. }
  94. if (grub_efi_is_finished)
  95. return;
  96. o = grub_efi_system_table->con_out;
  97. switch (state) {
  98. case GRUB_TERM_COLOR_STANDARD:
  99. o->set_attributes (o, GRUB_TERM_DEFAULT_STANDARD_COLOR & 0x7f);
  100. break;
  101. case GRUB_TERM_COLOR_NORMAL:
  102. o->set_attributes (o, grub_term_normal_color & 0x7f);
  103. break;
  104. case GRUB_TERM_COLOR_HIGHLIGHT:
  105. o->set_attributes (o, grub_term_highlight_color & 0x7f);
  106. break;
  107. default:
  108. break;
  109. }
  110. }
  111. static void
  112. grub_console_setcursor (struct grub_term_output *term __attribute__ ((unused)),
  113. int on)
  114. {
  115. grub_efi_simple_text_output_interface_t *o;
  116. if (grub_efi_is_finished || text_mode != GRUB_TEXT_MODE_AVAILABLE)
  117. {
  118. /* Cache cursor changes before the first text-output */
  119. cursor_mode = on;
  120. return;
  121. }
  122. o = grub_efi_system_table->con_out;
  123. o->enable_cursor (o, on);
  124. }
  125. static grub_err_t
  126. grub_prepare_for_text_output (struct grub_term_output *term)
  127. {
  128. if (grub_efi_is_finished)
  129. return GRUB_ERR_BAD_DEVICE;
  130. if (text_mode != GRUB_TEXT_MODE_UNDEFINED)
  131. return text_mode ? GRUB_ERR_NONE : GRUB_ERR_BAD_DEVICE;
  132. if (! grub_efi_set_text_mode (1))
  133. {
  134. /* This really should never happen */
  135. grub_error (GRUB_ERR_BAD_DEVICE, "cannot set text mode");
  136. text_mode = GRUB_TEXT_MODE_UNAVAILABLE;
  137. return GRUB_ERR_BAD_DEVICE;
  138. }
  139. if (cursor_mode != GRUB_CURSOR_MODE_UNDEFINED)
  140. grub_console_setcursor (term, cursor_mode);
  141. if (text_colorstate != GRUB_TERM_COLOR_UNDEFINED)
  142. grub_console_setcolorstate (term, text_colorstate);
  143. text_mode = GRUB_TEXT_MODE_AVAILABLE;
  144. return GRUB_ERR_NONE;
  145. }
  146. static void
  147. grub_console_putchar (struct grub_term_output *term,
  148. const struct grub_unicode_glyph *c)
  149. {
  150. grub_efi_char16_t str[2 + 30];
  151. grub_efi_simple_text_output_interface_t *o;
  152. unsigned i, j;
  153. if (grub_prepare_for_text_output (term) != GRUB_ERR_NONE)
  154. return;
  155. o = grub_efi_system_table->con_out;
  156. /* For now, do not try to use a surrogate pair. */
  157. if (c->base > 0xffff)
  158. str[0] = '?';
  159. else
  160. str[0] = (grub_efi_char16_t) map_char (c->base & 0xffff);
  161. j = 1;
  162. for (i = 0; i < c->ncomb && j + 1 < ARRAY_SIZE (str); i++)
  163. if (c->base < 0xffff)
  164. str[j++] = grub_unicode_get_comb (c)[i].code;
  165. str[j] = 0;
  166. /* Should this test be cached? */
  167. if ((c->base > 0x7f || c->ncomb)
  168. && o->test_string (o, str) != GRUB_EFI_SUCCESS)
  169. return;
  170. o->output_string (o, str);
  171. }
  172. const unsigned efi_codes[] =
  173. {
  174. 0, GRUB_TERM_KEY_UP, GRUB_TERM_KEY_DOWN, GRUB_TERM_KEY_RIGHT,
  175. GRUB_TERM_KEY_LEFT, GRUB_TERM_KEY_HOME, GRUB_TERM_KEY_END, GRUB_TERM_KEY_INSERT,
  176. GRUB_TERM_KEY_DC, GRUB_TERM_KEY_PPAGE, GRUB_TERM_KEY_NPAGE, GRUB_TERM_KEY_F1,
  177. GRUB_TERM_KEY_F2, GRUB_TERM_KEY_F3, GRUB_TERM_KEY_F4, GRUB_TERM_KEY_F5,
  178. GRUB_TERM_KEY_F6, GRUB_TERM_KEY_F7, GRUB_TERM_KEY_F8, GRUB_TERM_KEY_F9,
  179. GRUB_TERM_KEY_F10, GRUB_TERM_KEY_F11, GRUB_TERM_KEY_F12, GRUB_TERM_ESC
  180. };
  181. static int
  182. grub_efi_translate_key (grub_efi_input_key_t key)
  183. {
  184. if (key.scan_code == 0)
  185. {
  186. /* Some firmware implementations use VT100-style codes against the spec.
  187. This is especially likely if driven by serial.
  188. */
  189. if (key.unicode_char < 0x20 && key.unicode_char != 0
  190. && key.unicode_char != '\t' && key.unicode_char != '\b'
  191. && key.unicode_char != '\n' && key.unicode_char != '\r')
  192. return GRUB_TERM_CTRL | (key.unicode_char - 1 + 'a');
  193. else
  194. return key.unicode_char;
  195. }
  196. /* Some devices send enter with scan_code 0x0d (F3) and unicode_char 0x0d. */
  197. else if (key.scan_code == '\r' && key.unicode_char == '\r')
  198. return key.unicode_char;
  199. else if (key.scan_code < ARRAY_SIZE (efi_codes))
  200. return efi_codes[key.scan_code];
  201. if ((key.unicode_char >= 0x20 && key.unicode_char <= 0x7f)
  202. || key.unicode_char == '\t' || key.unicode_char == '\b'
  203. || key.unicode_char == '\n' || key.unicode_char == '\r')
  204. return key.unicode_char;
  205. return GRUB_TERM_NO_KEY;
  206. }
  207. static int
  208. grub_console_getkey_con (struct grub_term_input *term __attribute__ ((unused)))
  209. {
  210. grub_efi_simple_input_interface_t *i;
  211. grub_efi_input_key_t key;
  212. grub_efi_status_t status;
  213. i = grub_efi_system_table->con_in;
  214. status = i->read_key_stroke (i, &key);
  215. if (status != GRUB_EFI_SUCCESS)
  216. return GRUB_TERM_NO_KEY;
  217. return grub_efi_translate_key(key);
  218. }
  219. /*
  220. * When more then just modifiers are pressed, our getkeystatus() consumes a
  221. * press from the queue, this function buffers the press for the regular
  222. * getkey() so that it does not get lost.
  223. */
  224. static grub_err_t
  225. grub_console_read_key_stroke (
  226. grub_efi_simple_text_input_ex_interface_t *text_input,
  227. grub_efi_key_data_t *key_data_ret, int *key_ret,
  228. int consume)
  229. {
  230. static grub_efi_key_data_t key_data;
  231. grub_efi_status_t status;
  232. int key;
  233. if (!text_input)
  234. return GRUB_ERR_EOF;
  235. key = grub_efi_translate_key (key_data.key);
  236. if (key == GRUB_TERM_NO_KEY) {
  237. status = text_input->read_key_stroke (text_input, &key_data);
  238. if (status != GRUB_EFI_SUCCESS)
  239. return GRUB_ERR_EOF;
  240. key = grub_efi_translate_key (key_data.key);
  241. }
  242. *key_data_ret = key_data;
  243. *key_ret = key;
  244. if (consume) {
  245. key_data.key.scan_code = 0;
  246. key_data.key.unicode_char = 0;
  247. }
  248. return GRUB_ERR_NONE;
  249. }
  250. static int
  251. grub_console_getkey_ex (struct grub_term_input *term)
  252. {
  253. grub_efi_key_data_t key_data;
  254. grub_efi_uint32_t kss;
  255. grub_err_t err;
  256. int key = -1;
  257. err = grub_console_read_key_stroke (term->data, &key_data, &key, 1);
  258. if (err != GRUB_ERR_NONE || key == GRUB_TERM_NO_KEY)
  259. return GRUB_TERM_NO_KEY;
  260. kss = key_data.key_state.key_shift_state;
  261. if (kss & GRUB_EFI_SHIFT_STATE_VALID)
  262. {
  263. if ((kss & GRUB_EFI_LEFT_SHIFT_PRESSED
  264. || kss & GRUB_EFI_RIGHT_SHIFT_PRESSED)
  265. && (key & GRUB_TERM_EXTENDED))
  266. key |= GRUB_TERM_SHIFT;
  267. if (kss & GRUB_EFI_LEFT_ALT_PRESSED || kss & GRUB_EFI_RIGHT_ALT_PRESSED)
  268. key |= GRUB_TERM_ALT;
  269. if (kss & GRUB_EFI_LEFT_CONTROL_PRESSED
  270. || kss & GRUB_EFI_RIGHT_CONTROL_PRESSED)
  271. key |= GRUB_TERM_CTRL;
  272. }
  273. return key;
  274. }
  275. static int
  276. grub_console_getkeystatus (struct grub_term_input *term)
  277. {
  278. grub_efi_key_data_t key_data;
  279. grub_efi_uint32_t kss;
  280. int key, mods = 0;
  281. if (grub_efi_is_finished)
  282. return 0;
  283. if (grub_console_read_key_stroke (term->data, &key_data, &key, 0))
  284. return 0;
  285. kss = key_data.key_state.key_shift_state;
  286. if (kss & GRUB_EFI_SHIFT_STATE_VALID)
  287. {
  288. if (kss & GRUB_EFI_LEFT_SHIFT_PRESSED)
  289. mods |= GRUB_TERM_STATUS_LSHIFT;
  290. if (kss & GRUB_EFI_RIGHT_SHIFT_PRESSED)
  291. mods |= GRUB_TERM_STATUS_RSHIFT;
  292. if (kss & GRUB_EFI_LEFT_ALT_PRESSED)
  293. mods |= GRUB_TERM_STATUS_LALT;
  294. if (kss & GRUB_EFI_RIGHT_ALT_PRESSED)
  295. mods |= GRUB_TERM_STATUS_RALT;
  296. if (kss & GRUB_EFI_LEFT_CONTROL_PRESSED)
  297. mods |= GRUB_TERM_STATUS_LCTRL;
  298. if (kss & GRUB_EFI_RIGHT_CONTROL_PRESSED)
  299. mods |= GRUB_TERM_STATUS_RCTRL;
  300. }
  301. return mods;
  302. }
  303. static grub_err_t
  304. grub_efi_console_input_init (struct grub_term_input *term)
  305. {
  306. static grub_guid_t text_input_ex_guid =
  307. GRUB_EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
  308. if (grub_efi_is_finished)
  309. return 0;
  310. grub_efi_simple_text_input_ex_interface_t *text_input = term->data;
  311. if (text_input)
  312. return 0;
  313. text_input = grub_efi_open_protocol(grub_efi_system_table->console_in_handler,
  314. &text_input_ex_guid,
  315. GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
  316. term->data = (void *)text_input;
  317. return 0;
  318. }
  319. static int
  320. grub_console_getkey (struct grub_term_input *term)
  321. {
  322. if (grub_efi_is_finished)
  323. return 0;
  324. if (term->data)
  325. return grub_console_getkey_ex(term);
  326. else
  327. return grub_console_getkey_con(term);
  328. }
  329. static struct grub_term_coordinate
  330. grub_console_getwh (struct grub_term_output *term)
  331. {
  332. grub_efi_simple_text_output_interface_t *o;
  333. grub_efi_uintn_t columns, rows;
  334. o = grub_efi_system_table->con_out;
  335. if (grub_prepare_for_text_output (term) != GRUB_ERR_NONE ||
  336. o->query_mode (o, o->mode->mode,
  337. &columns, &rows) != GRUB_EFI_SUCCESS)
  338. {
  339. /* Why does this fail? */
  340. columns = 80;
  341. rows = 25;
  342. }
  343. return (struct grub_term_coordinate) { columns, rows };
  344. }
  345. static struct grub_term_coordinate
  346. grub_console_getxy (struct grub_term_output *term __attribute__ ((unused)))
  347. {
  348. grub_efi_simple_text_output_interface_t *o;
  349. if (grub_efi_is_finished || text_mode != GRUB_TEXT_MODE_AVAILABLE)
  350. return (struct grub_term_coordinate) { 0, 0 };
  351. o = grub_efi_system_table->con_out;
  352. return (struct grub_term_coordinate) { o->mode->cursor_column, o->mode->cursor_row };
  353. }
  354. static void
  355. grub_console_gotoxy (struct grub_term_output *term,
  356. struct grub_term_coordinate pos)
  357. {
  358. grub_efi_simple_text_output_interface_t *o;
  359. if (grub_prepare_for_text_output (term) != GRUB_ERR_NONE)
  360. return;
  361. o = grub_efi_system_table->con_out;
  362. o->set_cursor_position (o, pos.x, pos.y);
  363. }
  364. static void
  365. grub_console_cls (struct grub_term_output *term __attribute__ ((unused)))
  366. {
  367. grub_efi_simple_text_output_interface_t *o;
  368. grub_efi_int32_t orig_attr;
  369. if (grub_prepare_for_text_output (term) != GRUB_ERR_NONE)
  370. return;
  371. o = grub_efi_system_table->con_out;
  372. orig_attr = o->mode->attribute;
  373. o->set_attributes (o, GRUB_EFI_BACKGROUND_BLACK);
  374. o->clear_screen (o);
  375. o->set_attributes (o, orig_attr);
  376. }
  377. static grub_err_t
  378. grub_efi_console_output_fini (struct grub_term_output *term)
  379. {
  380. if (text_mode != GRUB_TEXT_MODE_AVAILABLE)
  381. return 0;
  382. grub_console_setcursor (term, 0);
  383. grub_efi_set_text_mode (0);
  384. text_mode = GRUB_TEXT_MODE_UNDEFINED;
  385. return 0;
  386. }
  387. static struct grub_term_input grub_console_term_input =
  388. {
  389. .name = "console",
  390. .getkey = grub_console_getkey,
  391. .getkeystatus = grub_console_getkeystatus,
  392. .init = grub_efi_console_input_init,
  393. };
  394. static struct grub_term_output grub_console_term_output =
  395. {
  396. .name = "console",
  397. .fini = grub_efi_console_output_fini,
  398. .putchar = grub_console_putchar,
  399. .getwh = grub_console_getwh,
  400. .getxy = grub_console_getxy,
  401. .gotoxy = grub_console_gotoxy,
  402. .cls = grub_console_cls,
  403. .setcolorstate = grub_console_setcolorstate,
  404. .setcursor = grub_console_setcursor,
  405. .flags = GRUB_TERM_CODE_TYPE_VISUAL_GLYPHS,
  406. .progress_update_divisor = GRUB_PROGRESS_FAST
  407. };
  408. void
  409. grub_console_init (void)
  410. {
  411. grub_term_register_output ("console", &grub_console_term_output);
  412. grub_term_register_input ("console", &grub_console_term_input);
  413. }
  414. void
  415. grub_console_fini (void)
  416. {
  417. grub_term_unregister_input (&grub_console_term_input);
  418. grub_term_unregister_output (&grub_console_term_output);
  419. }