Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

settings_ui.c 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. /*
  2. * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
  3. *
  4. * This program is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU General Public License as
  6. * published by the Free Software Foundation; either version 2 of the
  7. * License, or any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful, but
  10. * WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software
  16. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  17. * 02110-1301, USA.
  18. */
  19. FILE_LICENCE ( GPL2_OR_LATER );
  20. #include <stdio.h>
  21. #include <stdarg.h>
  22. #include <unistd.h>
  23. #include <string.h>
  24. #include <curses.h>
  25. #include <ipxe/console.h>
  26. #include <ipxe/settings.h>
  27. #include <ipxe/editbox.h>
  28. #include <ipxe/keys.h>
  29. #include <ipxe/settings_ui.h>
  30. #include <config/colour.h>
  31. /** @file
  32. *
  33. * Option configuration console
  34. *
  35. */
  36. /* Colour pairs */
  37. #define CPAIR_NORMAL 1
  38. #define CPAIR_SELECT 2
  39. #define CPAIR_EDIT 3
  40. #define CPAIR_ALERT 4
  41. #define CPAIR_URL 5
  42. /* Screen layout */
  43. #define TITLE_ROW 1U
  44. #define SETTINGS_LIST_ROW 3U
  45. #define SETTINGS_LIST_COL 1U
  46. #define SETTINGS_LIST_ROWS ( LINES - 6U - SETTINGS_LIST_ROW )
  47. #define INFO_ROW ( LINES - 5U )
  48. #define ALERT_ROW ( LINES - 2U )
  49. #define INSTRUCTION_ROW ( LINES - 2U )
  50. #define INSTRUCTION_PAD " "
  51. /** Layout of text within a setting widget */
  52. #define SETTING_ROW_TEXT( cols ) struct { \
  53. char start[0]; \
  54. char pad1[1]; \
  55. union { \
  56. char settings[ cols - 1 - 1 - 1 - 1 ]; \
  57. struct { \
  58. char name[15]; \
  59. char pad2[1]; \
  60. char value[ cols - 1 - 15 - 1 - 1 - 1 - 1 ]; \
  61. } setting; \
  62. } u; \
  63. char pad3[1]; \
  64. char nul; \
  65. } __attribute__ (( packed ))
  66. /** A setting row widget */
  67. struct setting_row_widget {
  68. /** Target configuration settings block
  69. *
  70. * Valid only for rows that lead to new settings blocks.
  71. */
  72. struct settings *settings;
  73. /** Configuration setting origin
  74. *
  75. * Valid only for rows that represent individual settings.
  76. */
  77. struct settings *origin;
  78. /** Configuration setting
  79. *
  80. * Valid only for rows that represent individual settings.
  81. */
  82. struct setting setting;
  83. /** Screen row */
  84. unsigned int row;
  85. /** Screen column */
  86. unsigned int col;
  87. /** Edit box widget used for editing setting */
  88. struct edit_box editbox;
  89. /** Editing in progress flag */
  90. int editing;
  91. /** Buffer for setting's value */
  92. char value[256]; /* enough size for a DHCP string */
  93. };
  94. /** A settings widget */
  95. struct setting_widget {
  96. /** Settings block */
  97. struct settings *settings;
  98. /** Number of rows */
  99. unsigned int num_rows;
  100. /** Current row index */
  101. unsigned int current;
  102. /** Index of the first visible row, for scrolling. */
  103. unsigned int first_visible;
  104. /** Active row */
  105. struct setting_row_widget row;
  106. };
  107. /**
  108. * Select a setting row
  109. *
  110. * @v widget Setting widget
  111. * @v index Index of setting row
  112. * @ret count Number of settings rows
  113. */
  114. static unsigned int select_setting_row ( struct setting_widget *widget,
  115. unsigned int index ) {
  116. SETTING_ROW_TEXT ( COLS ) *text;
  117. struct settings *settings;
  118. struct setting *setting;
  119. struct setting *previous = NULL;
  120. unsigned int count = 0;
  121. /* Initialise structure */
  122. memset ( &widget->row, 0, sizeof ( widget->row ) );
  123. widget->current = index;
  124. widget->row.row = ( SETTINGS_LIST_ROW + index - widget->first_visible );
  125. widget->row.col = SETTINGS_LIST_COL;
  126. /* Include parent settings block, if applicable */
  127. if ( widget->settings->parent && ( count++ == index ) ) {
  128. widget->row.settings = widget->settings->parent;
  129. snprintf ( widget->row.value, sizeof ( widget->row.value ),
  130. "../" );
  131. }
  132. /* Include any child settings blocks, if applicable */
  133. list_for_each_entry ( settings, &widget->settings->children, siblings ){
  134. if ( count++ == index ) {
  135. widget->row.settings = settings;
  136. snprintf ( widget->row.value,
  137. sizeof ( widget->row.value ), "%s/",
  138. settings->name );
  139. }
  140. }
  141. /* Include any applicable settings */
  142. for_each_table_entry ( setting, SETTINGS ) {
  143. /* Skip inapplicable settings */
  144. if ( ! setting_applies ( widget->settings, setting ) )
  145. continue;
  146. /* Skip duplicate settings */
  147. if ( previous && ( setting_cmp ( setting, previous ) == 0 ) )
  148. continue;
  149. previous = setting;
  150. /* Read current setting value and origin */
  151. if ( count++ == index ) {
  152. fetchf_setting ( widget->settings, setting,
  153. &widget->row.origin,
  154. &widget->row.setting,
  155. widget->row.value,
  156. sizeof ( widget->row.value ) );
  157. }
  158. }
  159. /* Initialise edit box */
  160. init_editbox ( &widget->row.editbox, widget->row.value,
  161. sizeof ( widget->row.value ), NULL, widget->row.row,
  162. ( widget->row.col +
  163. offsetof ( typeof ( *text ), u.setting.value ) ),
  164. sizeof ( text->u.setting.value ), 0 );
  165. return count;
  166. }
  167. /**
  168. * Copy string without NUL termination
  169. *
  170. * @v dest Destination
  171. * @v src Source
  172. * @v len Maximum length of destination
  173. * @ret len Length of (unterminated) string
  174. */
  175. static size_t string_copy ( char *dest, const char *src, size_t len ) {
  176. size_t src_len;
  177. src_len = strlen ( src );
  178. if ( len > src_len )
  179. len = src_len;
  180. memcpy ( dest, src, len );
  181. return len;
  182. }
  183. /**
  184. * Draw setting row
  185. *
  186. * @v widget Setting widget
  187. */
  188. static void draw_setting_row ( struct setting_widget *widget ) {
  189. SETTING_ROW_TEXT ( COLS ) text;
  190. unsigned int curs_offset;
  191. char *value;
  192. /* Fill row with spaces */
  193. memset ( &text, ' ', sizeof ( text ) );
  194. text.nul = '\0';
  195. /* Construct row content */
  196. if ( widget->row.settings ) {
  197. /* Construct space-padded name */
  198. curs_offset = ( offsetof ( typeof ( text ), u.settings ) +
  199. string_copy ( text.u.settings,
  200. widget->row.value,
  201. sizeof ( text.u.settings ) ) );
  202. } else {
  203. /* Construct dot-padded name */
  204. memset ( text.u.setting.name, '.',
  205. sizeof ( text.u.setting.name ) );
  206. string_copy ( text.u.setting.name, widget->row.setting.name,
  207. sizeof ( text.u.setting.name ) );
  208. /* Construct space-padded value */
  209. value = widget->row.value;
  210. if ( ! *value )
  211. value = "<not specified>";
  212. curs_offset = ( offsetof ( typeof ( text ), u.setting.value ) +
  213. string_copy ( text.u.setting.value, value,
  214. sizeof ( text.u.setting.value )));
  215. }
  216. /* Print row */
  217. if ( ( widget->row.origin == widget->settings ) ||
  218. ( widget->row.settings != NULL ) ) {
  219. attron ( A_BOLD );
  220. }
  221. mvprintw ( widget->row.row, widget->row.col, "%s", text.start );
  222. attroff ( A_BOLD );
  223. move ( widget->row.row, widget->row.col + curs_offset );
  224. }
  225. /**
  226. * Edit setting widget
  227. *
  228. * @v widget Setting widget
  229. * @v key Key pressed by user
  230. * @ret key Key returned to application, or zero
  231. */
  232. static int edit_setting ( struct setting_widget *widget, int key ) {
  233. assert ( widget->row.setting.name != NULL );
  234. widget->row.editing = 1;
  235. return edit_editbox ( &widget->row.editbox, key );
  236. }
  237. /**
  238. * Save setting widget value back to configuration settings
  239. *
  240. * @v widget Setting widget
  241. */
  242. static int save_setting ( struct setting_widget *widget ) {
  243. assert ( widget->row.setting.name != NULL );
  244. return storef_setting ( widget->settings, &widget->row.setting,
  245. widget->row.value );
  246. }
  247. /**
  248. * Print message centred on specified row
  249. *
  250. * @v row Row
  251. * @v fmt printf() format string
  252. * @v args printf() argument list
  253. */
  254. static void vmsg ( unsigned int row, const char *fmt, va_list args ) {
  255. char buf[COLS];
  256. size_t len;
  257. len = vsnprintf ( buf, sizeof ( buf ), fmt, args );
  258. mvprintw ( row, ( ( COLS - len ) / 2 ), "%s", buf );
  259. }
  260. /**
  261. * Print message centred on specified row
  262. *
  263. * @v row Row
  264. * @v fmt printf() format string
  265. * @v .. printf() arguments
  266. */
  267. static void msg ( unsigned int row, const char *fmt, ... ) {
  268. va_list args;
  269. va_start ( args, fmt );
  270. vmsg ( row, fmt, args );
  271. va_end ( args );
  272. }
  273. /**
  274. * Clear message on specified row
  275. *
  276. * @v row Row
  277. */
  278. static void clearmsg ( unsigned int row ) {
  279. move ( row, 0 );
  280. clrtoeol();
  281. }
  282. /**
  283. * Print alert message
  284. *
  285. * @v fmt printf() format string
  286. * @v args printf() argument list
  287. */
  288. static void valert ( const char *fmt, va_list args ) {
  289. clearmsg ( ALERT_ROW );
  290. color_set ( CPAIR_ALERT, NULL );
  291. vmsg ( ALERT_ROW, fmt, args );
  292. sleep ( 2 );
  293. color_set ( CPAIR_NORMAL, NULL );
  294. clearmsg ( ALERT_ROW );
  295. }
  296. /**
  297. * Print alert message
  298. *
  299. * @v fmt printf() format string
  300. * @v ... printf() arguments
  301. */
  302. static void alert ( const char *fmt, ... ) {
  303. va_list args;
  304. va_start ( args, fmt );
  305. valert ( fmt, args );
  306. va_end ( args );
  307. }
  308. /**
  309. * Draw title row
  310. *
  311. * @v widget Setting widget
  312. */
  313. static void draw_title_row ( struct setting_widget *widget ) {
  314. const char *name;
  315. clearmsg ( TITLE_ROW );
  316. name = settings_name ( widget->settings );
  317. attron ( A_BOLD );
  318. msg ( TITLE_ROW, "iPXE configuration settings%s%s",
  319. ( name[0] ? " - " : "" ), name );
  320. attroff ( A_BOLD );
  321. }
  322. /**
  323. * Draw information row
  324. *
  325. * @v widget Setting widget
  326. */
  327. static void draw_info_row ( struct setting_widget *widget ) {
  328. char buf[32];
  329. /* Draw nothing unless this row represents a setting */
  330. clearmsg ( INFO_ROW );
  331. clearmsg ( INFO_ROW + 1 );
  332. if ( ! widget->row.setting.name )
  333. return;
  334. /* Determine a suitable setting name */
  335. setting_name ( ( widget->row.origin ?
  336. widget->row.origin : widget->settings ),
  337. &widget->row.setting, buf, sizeof ( buf ) );
  338. /* Draw row */
  339. attron ( A_BOLD );
  340. msg ( INFO_ROW, "%s - %s", buf, widget->row.setting.description );
  341. attroff ( A_BOLD );
  342. color_set ( CPAIR_URL, NULL );
  343. msg ( ( INFO_ROW + 1 ), "http://ipxe.org/cfg/%s",
  344. widget->row.setting.name );
  345. color_set ( CPAIR_NORMAL, NULL );
  346. }
  347. /**
  348. * Draw instruction row
  349. *
  350. * @v widget Setting widget
  351. */
  352. static void draw_instruction_row ( struct setting_widget *widget ) {
  353. clearmsg ( INSTRUCTION_ROW );
  354. if ( widget->row.editing ) {
  355. msg ( INSTRUCTION_ROW,
  356. "Enter - accept changes" INSTRUCTION_PAD
  357. "Ctrl-C - discard changes" );
  358. } else {
  359. msg ( INSTRUCTION_ROW,
  360. "%sCtrl-X - exit configuration utility",
  361. ( ( widget->row.origin == widget->settings ) ?
  362. "Ctrl-D - delete setting" INSTRUCTION_PAD : "" ) );
  363. }
  364. }
  365. /**
  366. * Reveal setting row
  367. *
  368. * @v widget Setting widget
  369. * @v index Index of setting row
  370. */
  371. static void reveal_setting_row ( struct setting_widget *widget,
  372. unsigned int index ) {
  373. unsigned int i;
  374. /* Simply return if setting N is already on-screen. */
  375. if ( index - widget->first_visible < SETTINGS_LIST_ROWS )
  376. return;
  377. /* Jump scroll to make the specified setting row visible. */
  378. while ( widget->first_visible < index )
  379. widget->first_visible += SETTINGS_LIST_ROWS;
  380. while ( widget->first_visible > index )
  381. widget->first_visible -= SETTINGS_LIST_ROWS;
  382. /* Draw ellipses before and/or after the settings list to
  383. * represent any invisible settings.
  384. */
  385. mvaddstr ( SETTINGS_LIST_ROW - 1,
  386. SETTINGS_LIST_COL + 1,
  387. widget->first_visible > 0 ? "..." : " " );
  388. mvaddstr ( SETTINGS_LIST_ROW + SETTINGS_LIST_ROWS,
  389. SETTINGS_LIST_COL + 1,
  390. ( ( widget->first_visible + SETTINGS_LIST_ROWS )
  391. < widget->num_rows ? "..." : " " ) );
  392. /* Draw visible settings. */
  393. for ( i = 0; i < SETTINGS_LIST_ROWS; i++ ) {
  394. if ( ( widget->first_visible + i ) < widget->num_rows ) {
  395. select_setting_row ( widget,
  396. widget->first_visible + i );
  397. draw_setting_row ( widget );
  398. } else {
  399. clearmsg ( SETTINGS_LIST_ROW + i );
  400. }
  401. }
  402. }
  403. /**
  404. * Reveal setting row
  405. *
  406. * @v widget Setting widget
  407. * @v settings Settings block
  408. */
  409. static void init_widget ( struct setting_widget *widget,
  410. struct settings *settings ) {
  411. widget->settings = settings_target ( settings );
  412. widget->num_rows = select_setting_row ( widget, 0 );
  413. widget->first_visible = SETTINGS_LIST_ROWS;
  414. draw_title_row ( widget );
  415. reveal_setting_row ( widget, 0 );
  416. select_setting_row ( widget, 0 );
  417. }
  418. static int main_loop ( struct settings *settings ) {
  419. struct setting_widget widget;
  420. int redraw = 1;
  421. int move;
  422. unsigned int next;
  423. int key;
  424. int rc;
  425. /* Print initial screen content */
  426. color_set ( CPAIR_NORMAL, NULL );
  427. memset ( &widget, 0, sizeof ( widget ) );
  428. init_widget ( &widget, settings );
  429. while ( 1 ) {
  430. /* Redraw rows if necessary */
  431. if ( redraw ) {
  432. draw_info_row ( &widget );
  433. draw_instruction_row ( &widget );
  434. color_set ( ( widget.row.editing ?
  435. CPAIR_EDIT : CPAIR_SELECT ), NULL );
  436. draw_setting_row ( &widget );
  437. color_set ( CPAIR_NORMAL, NULL );
  438. curs_set ( widget.row.editing );
  439. redraw = 0;
  440. }
  441. if ( widget.row.editing ) {
  442. /* Sanity check */
  443. assert ( widget.row.setting.name != NULL );
  444. /* Redraw edit box */
  445. color_set ( CPAIR_EDIT, NULL );
  446. draw_editbox ( &widget.row.editbox );
  447. color_set ( CPAIR_NORMAL, NULL );
  448. /* Process keypress */
  449. key = edit_setting ( &widget, getkey ( 0 ) );
  450. switch ( key ) {
  451. case CR:
  452. case LF:
  453. if ( ( rc = save_setting ( &widget ) ) != 0 )
  454. alert ( " %s ", strerror ( rc ) );
  455. /* Fall through */
  456. case CTRL_C:
  457. select_setting_row ( &widget, widget.current );
  458. redraw = 1;
  459. break;
  460. default:
  461. /* Do nothing */
  462. break;
  463. }
  464. } else {
  465. /* Process keypress */
  466. key = getkey ( 0 );
  467. move = 0;
  468. switch ( key ) {
  469. case KEY_UP:
  470. move = -1;
  471. break;
  472. case KEY_DOWN:
  473. move = +1;
  474. break;
  475. case KEY_PPAGE:
  476. move = ( widget.first_visible -
  477. widget.current - 1 );
  478. break;
  479. case KEY_NPAGE:
  480. move = ( widget.first_visible - widget.current
  481. + SETTINGS_LIST_ROWS );
  482. break;
  483. case KEY_HOME:
  484. move = -widget.num_rows;
  485. break;
  486. case KEY_END:
  487. move = +widget.num_rows;
  488. break;
  489. case CTRL_D:
  490. if ( ! widget.row.setting.name )
  491. break;
  492. if ( ( rc = delete_setting ( widget.settings,
  493. &widget.row.setting ) ) != 0 ) {
  494. alert ( " %s ", strerror ( rc ) );
  495. }
  496. select_setting_row ( &widget, widget.current );
  497. redraw = 1;
  498. break;
  499. case CTRL_X:
  500. return 0;
  501. case CR:
  502. case LF:
  503. if ( widget.row.settings ) {
  504. init_widget ( &widget,
  505. widget.row.settings );
  506. redraw = 1;
  507. }
  508. /* Fall through */
  509. default:
  510. if ( widget.row.setting.name ) {
  511. edit_setting ( &widget, key );
  512. redraw = 1;
  513. }
  514. break;
  515. }
  516. if ( move ) {
  517. next = ( widget.current + move );
  518. if ( ( int ) next < 0 )
  519. next = 0;
  520. if ( next >= widget.num_rows )
  521. next = ( widget.num_rows - 1 );
  522. if ( next != widget.current ) {
  523. draw_setting_row ( &widget );
  524. redraw = 1;
  525. reveal_setting_row ( &widget, next );
  526. select_setting_row ( &widget, next );
  527. }
  528. }
  529. }
  530. }
  531. }
  532. int settings_ui ( struct settings *settings ) {
  533. int rc;
  534. initscr();
  535. start_color();
  536. init_pair ( CPAIR_NORMAL, COLOR_NORMAL_FG, COLOR_NORMAL_BG );
  537. init_pair ( CPAIR_SELECT, COLOR_SELECT_FG, COLOR_SELECT_BG );
  538. init_pair ( CPAIR_EDIT, COLOR_EDIT_FG, COLOR_EDIT_BG );
  539. init_pair ( CPAIR_ALERT, COLOR_ALERT_FG, COLOR_ALERT_BG );
  540. init_pair ( CPAIR_URL, COLOR_URL_FG, COLOR_URL_BG );
  541. color_set ( CPAIR_NORMAL, NULL );
  542. curs_set ( 0 );
  543. erase();
  544. rc = main_loop ( settings );
  545. endwin();
  546. return rc;
  547. }