menu_text.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. /* menu_text.c - Basic text menu implementation. */
  2. /*
  3. * GRUB -- GRand Unified Bootloader
  4. * Copyright (C) 2003,2004,2005,2006,2007,2008,2009 Free Software Foundation, Inc.
  5. *
  6. * GRUB is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * GRUB is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <grub/normal_menu.h>
  20. #include <grub/term.h>
  21. #include <grub/misc.h>
  22. #include <grub/loader.h>
  23. #include <grub/mm.h>
  24. #include <grub/time.h>
  25. #include <grub/env.h>
  26. #include <grub/menu_viewer.h>
  27. #include <grub/i18n.h>
  28. #include <grub/charset.h>
  29. #include <grub/lib.h>
  30. static grub_uint8_t grub_color_menu_normal;
  31. static grub_uint8_t grub_color_menu_highlight;
  32. struct menu_viewer_data
  33. {
  34. int first, offset;
  35. grub_menu_t menu;
  36. struct grub_term_output *term;
  37. };
  38. static void
  39. print_spaces (int number_spaces, struct grub_term_output *term)
  40. {
  41. int i;
  42. for (i = 0; i < number_spaces; i++)
  43. grub_putcode (' ', term);
  44. }
  45. grub_ssize_t
  46. grub_getstringwidth (grub_uint32_t * str, const grub_uint32_t * last_position,
  47. struct grub_term_output *term)
  48. {
  49. grub_ssize_t width = 0;
  50. while (str < last_position)
  51. {
  52. width += grub_term_getcharwidth (term, *str);
  53. str++;
  54. }
  55. return width;
  56. }
  57. void
  58. grub_print_message_indented (const char *msg, int margin_left, int margin_right,
  59. struct grub_term_output *term)
  60. {
  61. int line_len;
  62. grub_uint32_t *unicode_msg;
  63. grub_uint32_t *last_position;
  64. int msg_len;
  65. line_len = grub_term_width (term) - grub_term_getcharwidth (term, 'm') *
  66. (margin_left + margin_right);
  67. msg_len = grub_utf8_to_ucs4_alloc (msg, &unicode_msg, &last_position);
  68. if (msg_len < 0)
  69. {
  70. return;
  71. }
  72. grub_uint32_t *current_position = unicode_msg;
  73. grub_uint32_t *next_new_line = unicode_msg;
  74. int first_loop = 1;
  75. while (current_position < last_position)
  76. {
  77. if (! first_loop)
  78. grub_putcode ('\n', term);
  79. next_new_line = (grub_uint32_t *) last_position;
  80. while (grub_getstringwidth (current_position, next_new_line,term)
  81. > line_len
  82. || (next_new_line != last_position && *next_new_line != ' '
  83. && next_new_line > current_position))
  84. {
  85. next_new_line--;
  86. }
  87. if (next_new_line == current_position)
  88. {
  89. next_new_line = (next_new_line + line_len > last_position) ?
  90. (grub_uint32_t *) last_position : next_new_line + line_len;
  91. }
  92. print_spaces (margin_left, term);
  93. grub_print_ucs4 (current_position, next_new_line, term);
  94. next_new_line++;
  95. current_position = next_new_line;
  96. first_loop = 0;
  97. }
  98. grub_free (unicode_msg);
  99. }
  100. static void
  101. draw_border (struct grub_term_output *term)
  102. {
  103. unsigned i;
  104. grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
  105. grub_term_gotoxy (term, GRUB_TERM_MARGIN, GRUB_TERM_TOP_BORDER_Y);
  106. grub_putcode (GRUB_TERM_DISP_UL, term);
  107. for (i = 0; i < (unsigned) grub_term_border_width (term) - 2; i++)
  108. grub_putcode (GRUB_TERM_DISP_HLINE, term);
  109. grub_putcode (GRUB_TERM_DISP_UR, term);
  110. for (i = 0; i < (unsigned) grub_term_num_entries (term); i++)
  111. {
  112. grub_term_gotoxy (term, GRUB_TERM_MARGIN, GRUB_TERM_TOP_BORDER_Y + i + 1);
  113. grub_putcode (GRUB_TERM_DISP_VLINE, term);
  114. grub_term_gotoxy (term, GRUB_TERM_MARGIN + grub_term_border_width (term)
  115. - 1,
  116. GRUB_TERM_TOP_BORDER_Y + i + 1);
  117. grub_putcode (GRUB_TERM_DISP_VLINE, term);
  118. }
  119. grub_term_gotoxy (term, GRUB_TERM_MARGIN,
  120. GRUB_TERM_TOP_BORDER_Y + grub_term_num_entries (term) + 1);
  121. grub_putcode (GRUB_TERM_DISP_LL, term);
  122. for (i = 0; i < (unsigned) grub_term_border_width (term) - 2; i++)
  123. grub_putcode (GRUB_TERM_DISP_HLINE, term);
  124. grub_putcode (GRUB_TERM_DISP_LR, term);
  125. grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
  126. grub_term_gotoxy (term, GRUB_TERM_MARGIN,
  127. (GRUB_TERM_TOP_BORDER_Y + grub_term_num_entries (term)
  128. + GRUB_TERM_MARGIN + 1));
  129. }
  130. static void
  131. print_message (int nested, int edit, struct grub_term_output *term)
  132. {
  133. grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
  134. if (edit)
  135. {
  136. grub_putcode ('\n', term);
  137. #ifdef GRUB_MACHINE_EFI
  138. grub_print_message_indented (_("Minimum Emacs-like screen editing is \
  139. supported. TAB lists completions. Press F1 to boot, F2=Ctrl-a, F3=Ctrl-e, \
  140. F4 for a command-line or ESC to discard edits and return to the GRUB menu."),
  141. STANDARD_MARGIN, STANDARD_MARGIN, term);
  142. #else
  143. grub_print_message_indented (_("Minimum Emacs-like screen editing is \
  144. supported. TAB lists completions. Press Ctrl-x to boot, Ctrl-c for a \
  145. command-line or ESC to discard edits and return to the GRUB menu."),
  146. STANDARD_MARGIN, STANDARD_MARGIN, term);
  147. #endif
  148. }
  149. else
  150. {
  151. const char *msg = _("Use the %C and %C keys to select which "
  152. "entry is highlighted.\n");
  153. char *msg_translated;
  154. msg_translated = grub_xasprintf (msg, (grub_uint32_t) GRUB_TERM_DISP_UP,
  155. (grub_uint32_t) GRUB_TERM_DISP_DOWN);
  156. if (!msg_translated)
  157. return;
  158. grub_putchar ('\n');
  159. grub_print_message_indented (msg_translated, STANDARD_MARGIN,
  160. STANDARD_MARGIN, term);
  161. grub_free (msg_translated);
  162. if (nested)
  163. {
  164. grub_print_message_indented
  165. (_("Press enter to boot the selected OS, "
  166. "\'e\' to edit the commands before booting "
  167. "or \'c\' for a command-line. ESC to return previous menu.\n"),
  168. STANDARD_MARGIN, STANDARD_MARGIN, term);
  169. }
  170. else
  171. {
  172. grub_print_message_indented
  173. (_("Press enter to boot the selected OS, "
  174. "\'e\' to edit the commands before booting "
  175. "or \'c\' for a command-line.\n"),
  176. STANDARD_MARGIN, STANDARD_MARGIN, term);
  177. }
  178. }
  179. }
  180. static void
  181. print_entry (int y, int highlight, grub_menu_entry_t entry,
  182. struct grub_term_output *term)
  183. {
  184. int x;
  185. const char *title;
  186. grub_size_t title_len;
  187. grub_ssize_t len;
  188. grub_uint32_t *unicode_title;
  189. grub_ssize_t i;
  190. grub_uint8_t old_color_normal, old_color_highlight;
  191. title = entry ? entry->title : "";
  192. title_len = grub_strlen (title);
  193. unicode_title = grub_malloc (title_len * sizeof (*unicode_title));
  194. if (! unicode_title)
  195. /* XXX How to show this error? */
  196. return;
  197. len = grub_utf8_to_ucs4 (unicode_title, title_len,
  198. (grub_uint8_t *) title, -1, 0);
  199. if (len < 0)
  200. {
  201. /* It is an invalid sequence. */
  202. grub_free (unicode_title);
  203. return;
  204. }
  205. grub_term_getcolor (term, &old_color_normal, &old_color_highlight);
  206. grub_term_setcolor (term, grub_color_menu_normal, grub_color_menu_highlight);
  207. grub_term_setcolorstate (term, highlight
  208. ? GRUB_TERM_COLOR_HIGHLIGHT
  209. : GRUB_TERM_COLOR_NORMAL);
  210. grub_term_gotoxy (term, GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN, y);
  211. for (x = GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN + 1, i = 0;
  212. x < (int) (GRUB_TERM_LEFT_BORDER_X + grub_term_border_width (term)
  213. - GRUB_TERM_MARGIN);
  214. i++)
  215. {
  216. if (i < len
  217. && x <= (int) (GRUB_TERM_LEFT_BORDER_X + grub_term_border_width (term)
  218. - GRUB_TERM_MARGIN - 1))
  219. {
  220. grub_ssize_t width;
  221. width = grub_term_getcharwidth (term, unicode_title[i]);
  222. if (x + width > (int) (GRUB_TERM_LEFT_BORDER_X
  223. + grub_term_border_width (term)
  224. - GRUB_TERM_MARGIN - 1))
  225. grub_putcode (GRUB_TERM_DISP_RIGHT, term);
  226. else
  227. grub_putcode (unicode_title[i], term);
  228. x += width;
  229. }
  230. else
  231. {
  232. grub_putcode (' ', term);
  233. x++;
  234. }
  235. }
  236. grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
  237. grub_putcode (' ', term);
  238. grub_term_gotoxy (term, grub_term_cursor_x (term), y);
  239. grub_term_setcolor (term, old_color_normal, old_color_highlight);
  240. grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
  241. grub_free (unicode_title);
  242. }
  243. static void
  244. print_entries (grub_menu_t menu, int first, int offset,
  245. struct grub_term_output *term)
  246. {
  247. grub_menu_entry_t e;
  248. int i;
  249. grub_term_gotoxy (term,
  250. GRUB_TERM_LEFT_BORDER_X + grub_term_border_width (term),
  251. GRUB_TERM_FIRST_ENTRY_Y);
  252. if (first)
  253. grub_putcode (GRUB_TERM_DISP_UP, term);
  254. else
  255. grub_putcode (' ', term);
  256. e = grub_menu_get_entry (menu, first);
  257. for (i = 0; i < grub_term_num_entries (term); i++)
  258. {
  259. print_entry (GRUB_TERM_FIRST_ENTRY_Y + i, offset == i, e, term);
  260. if (e)
  261. e = e->next;
  262. }
  263. grub_term_gotoxy (term, GRUB_TERM_LEFT_BORDER_X
  264. + grub_term_border_width (term),
  265. GRUB_TERM_TOP_BORDER_Y + grub_term_num_entries (term));
  266. if (e)
  267. grub_putcode (GRUB_TERM_DISP_DOWN, term);
  268. else
  269. grub_putcode (' ', term);
  270. grub_term_gotoxy (term, grub_term_cursor_x (term),
  271. GRUB_TERM_FIRST_ENTRY_Y + offset);
  272. }
  273. /* Initialize the screen. If NESTED is non-zero, assume that this menu
  274. is run from another menu or a command-line. If EDIT is non-zero, show
  275. a message for the menu entry editor. */
  276. void
  277. grub_menu_init_page (int nested, int edit,
  278. struct grub_term_output *term)
  279. {
  280. grub_uint8_t old_color_normal, old_color_highlight;
  281. grub_term_getcolor (term, &old_color_normal, &old_color_highlight);
  282. /* By default, use the same colors for the menu. */
  283. grub_color_menu_normal = old_color_normal;
  284. grub_color_menu_highlight = old_color_highlight;
  285. /* Then give user a chance to replace them. */
  286. grub_parse_color_name_pair (&grub_color_menu_normal,
  287. grub_env_get ("menu_color_normal"));
  288. grub_parse_color_name_pair (&grub_color_menu_highlight,
  289. grub_env_get ("menu_color_highlight"));
  290. grub_normal_init_page (term);
  291. grub_term_setcolor (term, grub_color_menu_normal, grub_color_menu_highlight);
  292. draw_border (term);
  293. grub_term_setcolor (term, old_color_normal, old_color_highlight);
  294. print_message (nested, edit, term);
  295. }
  296. static void
  297. menu_text_print_timeout (int timeout, void *dataptr)
  298. {
  299. const char *msg =
  300. _("The highlighted entry will be executed automatically in %ds.");
  301. struct menu_viewer_data *data = dataptr;
  302. char *msg_translated;
  303. int posx;
  304. grub_term_gotoxy (data->term, 0, grub_term_height (data->term) - 3);
  305. msg_translated = grub_xasprintf (msg, timeout);
  306. if (!msg_translated)
  307. {
  308. grub_print_error ();
  309. grub_errno = GRUB_ERR_NONE;
  310. return;
  311. }
  312. grub_print_message_indented (msg_translated, 3, 0, data->term);
  313. posx = grub_term_getxy (data->term) >> 8;
  314. print_spaces (grub_term_width (data->term) - posx - 1, data->term);
  315. grub_term_gotoxy (data->term,
  316. grub_term_cursor_x (data->term),
  317. GRUB_TERM_FIRST_ENTRY_Y + data->offset);
  318. grub_term_refresh (data->term);
  319. }
  320. static void
  321. menu_text_set_chosen_entry (int entry, void *dataptr)
  322. {
  323. struct menu_viewer_data *data = dataptr;
  324. int oldoffset = data->offset;
  325. int complete_redraw = 0;
  326. data->offset = entry - data->first;
  327. if (data->offset > grub_term_num_entries (data->term) - 1)
  328. {
  329. data->first = entry - (grub_term_num_entries (data->term) - 1);
  330. data->offset = grub_term_num_entries (data->term) - 1;
  331. complete_redraw = 1;
  332. }
  333. if (data->offset < 0)
  334. {
  335. data->offset = 0;
  336. data->first = entry;
  337. complete_redraw = 1;
  338. }
  339. if (complete_redraw)
  340. print_entries (data->menu, data->first, data->offset, data->term);
  341. else
  342. {
  343. print_entry (GRUB_TERM_FIRST_ENTRY_Y + oldoffset, 0,
  344. grub_menu_get_entry (data->menu, data->first + oldoffset),
  345. data->term);
  346. print_entry (GRUB_TERM_FIRST_ENTRY_Y + data->offset, 1,
  347. grub_menu_get_entry (data->menu, data->first + data->offset),
  348. data->term);
  349. }
  350. grub_term_refresh (data->term);
  351. }
  352. static void
  353. menu_text_fini (void *dataptr)
  354. {
  355. struct menu_viewer_data *data = dataptr;
  356. grub_term_setcursor (data->term, 1);
  357. grub_term_cls (data->term);
  358. }
  359. static void
  360. menu_text_clear_timeout (void *dataptr)
  361. {
  362. struct menu_viewer_data *data = dataptr;
  363. grub_term_gotoxy (data->term, 0, grub_term_height (data->term) - 3);
  364. print_spaces (grub_term_width (data->term) - 1, data->term);
  365. grub_term_gotoxy (data->term, grub_term_cursor_x (data->term),
  366. GRUB_TERM_FIRST_ENTRY_Y + data->offset);
  367. grub_term_refresh (data->term);
  368. }
  369. grub_err_t
  370. grub_menu_try_text (struct grub_term_output *term,
  371. int entry, grub_menu_t menu, int nested)
  372. {
  373. struct menu_viewer_data *data;
  374. struct grub_menu_viewer *instance;
  375. instance = grub_zalloc (sizeof (*instance));
  376. if (!instance)
  377. return grub_errno;
  378. data = grub_zalloc (sizeof (*data));
  379. if (!data)
  380. {
  381. grub_free (instance);
  382. return grub_errno;
  383. }
  384. data->term = term;
  385. instance->data = data;
  386. instance->set_chosen_entry = menu_text_set_chosen_entry;
  387. instance->print_timeout = menu_text_print_timeout;
  388. instance->clear_timeout = menu_text_clear_timeout;
  389. instance->fini = menu_text_fini;
  390. data->menu = menu;
  391. data->offset = entry;
  392. data->first = 0;
  393. if (data->offset > grub_term_num_entries (data->term) - 1)
  394. {
  395. data->first = data->offset - (grub_term_num_entries (data->term) - 1);
  396. data->offset = grub_term_num_entries (data->term) - 1;
  397. }
  398. grub_term_setcursor (data->term, 0);
  399. grub_menu_init_page (nested, 0, data->term);
  400. print_entries (menu, data->first, data->offset, data->term);
  401. grub_term_refresh (data->term);
  402. grub_menu_register_viewer (instance);
  403. return GRUB_ERR_NONE;
  404. }