Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

settings_ui.c 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  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 1
  44. #define SETTINGS_LIST_ROW 3
  45. #define SETTINGS_LIST_COL 1
  46. #define SETTINGS_LIST_ROWS 15
  47. #define INFO_ROW 19
  48. #define ALERT_ROW 22
  49. #define INSTRUCTION_ROW 22
  50. #define INSTRUCTION_PAD " "
  51. /** Layout of text within a setting widget */
  52. struct setting_row_text {
  53. char start[0];
  54. char pad1[1];
  55. char name[15];
  56. char pad2[1];
  57. char value[60];
  58. char pad3[1];
  59. char nul;
  60. } __attribute__ (( packed ));
  61. /** A setting row widget */
  62. struct setting_row_widget {
  63. /** Target configuration settings block
  64. *
  65. * Valid only for rows that lead to new settings blocks.
  66. */
  67. struct settings *settings;
  68. /** Configuration setting
  69. *
  70. * Valid only for rows that represent individual settings.
  71. */
  72. struct setting *setting;
  73. /** Screen row */
  74. unsigned int row;
  75. /** Screen column */
  76. unsigned int col;
  77. /** Edit box widget used for editing setting */
  78. struct edit_box editbox;
  79. /** Editing in progress flag */
  80. int editing;
  81. /** Setting originates from this block flag */
  82. int originates_here;
  83. /** Buffer for setting's value */
  84. char value[256]; /* enough size for a DHCP string */
  85. };
  86. /** A settings widget */
  87. struct setting_widget {
  88. /** Settings block */
  89. struct settings *settings;
  90. /** Number of rows */
  91. unsigned int num_rows;
  92. /** Current row index */
  93. unsigned int current;
  94. /** Index of the first visible row, for scrolling. */
  95. unsigned int first_visible;
  96. /** Active row */
  97. struct setting_row_widget row;
  98. };
  99. /**
  100. * Select a setting row
  101. *
  102. * @v widget Setting widget
  103. * @v index Index of setting row
  104. * @ret count Number of settings rows
  105. */
  106. static unsigned int select_setting_row ( struct setting_widget *widget,
  107. unsigned int index ) {
  108. struct settings *settings;
  109. struct settings *origin;
  110. struct setting *setting;
  111. unsigned int count = 0;
  112. /* Initialise structure */
  113. memset ( &widget->row, 0, sizeof ( widget->row ) );
  114. widget->current = index;
  115. widget->row.row = ( SETTINGS_LIST_ROW + index - widget->first_visible );
  116. widget->row.col = SETTINGS_LIST_COL;
  117. /* Include parent settings block, if applicable */
  118. if ( widget->settings->parent && ( count++ == index ) ) {
  119. widget->row.settings = widget->settings->parent;
  120. snprintf ( widget->row.value, sizeof ( widget->row.value ),
  121. "../" );
  122. }
  123. /* Include any child settings blocks, if applicable */
  124. list_for_each_entry ( settings, &widget->settings->children, siblings ){
  125. if ( count++ == index ) {
  126. widget->row.settings = settings;
  127. snprintf ( widget->row.value,
  128. sizeof ( widget->row.value ), "%s/",
  129. settings->name );
  130. }
  131. }
  132. /* Include any applicable settings */
  133. for_each_table_entry ( setting, SETTINGS ) {
  134. if ( ! setting_applies ( widget->settings, setting ) )
  135. continue;
  136. if ( count++ == index ) {
  137. widget->row.setting = setting;
  138. /* Read current setting value */
  139. fetchf_setting ( widget->settings, widget->row.setting,
  140. widget->row.value,
  141. sizeof ( widget->row.value ) );
  142. /* Check setting's origin */
  143. origin = fetch_setting_origin ( widget->settings,
  144. widget->row.setting );
  145. widget->row.originates_here =
  146. ( origin == widget->settings );
  147. }
  148. }
  149. /* Initialise edit box */
  150. init_editbox ( &widget->row.editbox, widget->row.value,
  151. sizeof ( widget->row.value ), NULL, widget->row.row,
  152. ( widget->row.col +
  153. offsetof ( struct setting_row_text, value ) ),
  154. sizeof ( ( ( struct setting_row_text * ) NULL )->value ),
  155. 0 );
  156. return count;
  157. }
  158. static size_t string_copy ( char *dest, const char *src, size_t len ) {
  159. size_t src_len;
  160. src_len = strlen ( src );
  161. if ( len > src_len )
  162. len = src_len;
  163. memcpy ( dest, src, len );
  164. return len;
  165. }
  166. /**
  167. * Draw setting row
  168. *
  169. * @v widget Setting widget
  170. */
  171. static void draw_setting_row ( struct setting_widget *widget ) {
  172. struct setting_row_text text;
  173. unsigned int curs_offset;
  174. char *value;
  175. /* Fill row with spaces */
  176. memset ( &text, ' ', sizeof ( text ) );
  177. text.nul = '\0';
  178. /* Construct row content */
  179. if ( widget->row.settings ) {
  180. /* Construct space-padded name */
  181. curs_offset = ( offsetof ( typeof ( text ), name ) +
  182. string_copy ( text.name, widget->row.value,
  183. sizeof ( text.name ) ) );
  184. } else {
  185. /* Construct dot-padded name */
  186. memset ( text.name, '.', sizeof ( text.name ) );
  187. string_copy ( text.name, widget->row.setting->name,
  188. sizeof ( text.name ) );
  189. /* Construct space-padded value */
  190. value = widget->row.value;
  191. if ( ! *value )
  192. value = "<not specified>";
  193. curs_offset = ( offsetof ( typeof ( text ), value ) +
  194. string_copy ( text.value, value,
  195. sizeof ( text.value ) ) );
  196. }
  197. /* Print row */
  198. if ( widget->row.originates_here || widget->row.settings )
  199. attron ( A_BOLD );
  200. mvprintw ( widget->row.row, widget->row.col, "%s", text.start );
  201. attroff ( A_BOLD );
  202. move ( widget->row.row, widget->row.col + curs_offset );
  203. }
  204. /**
  205. * Edit setting widget
  206. *
  207. * @v widget Setting widget
  208. * @v key Key pressed by user
  209. * @ret key Key returned to application, or zero
  210. */
  211. static int edit_setting ( struct setting_widget *widget, int key ) {
  212. assert ( widget->row.setting != NULL );
  213. widget->row.editing = 1;
  214. return edit_editbox ( &widget->row.editbox, key );
  215. }
  216. /**
  217. * Save setting widget value back to configuration settings
  218. *
  219. * @v widget Setting widget
  220. */
  221. static int save_setting ( struct setting_widget *widget ) {
  222. assert ( widget->row.setting != NULL );
  223. return storef_setting ( widget->settings, widget->row.setting,
  224. widget->row.value );
  225. }
  226. /**
  227. * Print message centred on specified row
  228. *
  229. * @v row Row
  230. * @v fmt printf() format string
  231. * @v args printf() argument list
  232. */
  233. static void vmsg ( unsigned int row, const char *fmt, va_list args ) {
  234. char buf[COLS];
  235. size_t len;
  236. len = vsnprintf ( buf, sizeof ( buf ), fmt, args );
  237. mvprintw ( row, ( ( COLS - len ) / 2 ), "%s", buf );
  238. }
  239. /**
  240. * Print message centred on specified row
  241. *
  242. * @v row Row
  243. * @v fmt printf() format string
  244. * @v .. printf() arguments
  245. */
  246. static void msg ( unsigned int row, const char *fmt, ... ) {
  247. va_list args;
  248. va_start ( args, fmt );
  249. vmsg ( row, fmt, args );
  250. va_end ( args );
  251. }
  252. /**
  253. * Clear message on specified row
  254. *
  255. * @v row Row
  256. */
  257. static void clearmsg ( unsigned int row ) {
  258. move ( row, 0 );
  259. clrtoeol();
  260. }
  261. /**
  262. * Print alert message
  263. *
  264. * @v fmt printf() format string
  265. * @v args printf() argument list
  266. */
  267. static void valert ( const char *fmt, va_list args ) {
  268. clearmsg ( ALERT_ROW );
  269. color_set ( CPAIR_ALERT, NULL );
  270. vmsg ( ALERT_ROW, fmt, args );
  271. sleep ( 2 );
  272. color_set ( CPAIR_NORMAL, NULL );
  273. clearmsg ( ALERT_ROW );
  274. }
  275. /**
  276. * Print alert message
  277. *
  278. * @v fmt printf() format string
  279. * @v ... printf() arguments
  280. */
  281. static void alert ( const char *fmt, ... ) {
  282. va_list args;
  283. va_start ( args, fmt );
  284. valert ( fmt, args );
  285. va_end ( args );
  286. }
  287. /**
  288. * Draw title row
  289. *
  290. * @v widget Setting widget
  291. */
  292. static void draw_title_row ( struct setting_widget *widget ) {
  293. const char *name;
  294. clearmsg ( TITLE_ROW );
  295. name = settings_name ( widget->settings );
  296. attron ( A_BOLD );
  297. msg ( TITLE_ROW, "iPXE configuration settings%s%s",
  298. ( name[0] ? " - " : "" ), name );
  299. attroff ( A_BOLD );
  300. }
  301. /**
  302. * Draw information row
  303. *
  304. * @v widget Setting widget
  305. */
  306. static void draw_info_row ( struct setting_widget *widget ) {
  307. struct settings *origin;
  308. char buf[32];
  309. /* Draw nothing unless this row represents a setting */
  310. clearmsg ( INFO_ROW );
  311. clearmsg ( INFO_ROW + 1 );
  312. if ( ! widget->row.setting )
  313. return;
  314. /* Determine a suitable setting name */
  315. origin = fetch_setting_origin ( widget->settings, widget->row.setting );
  316. if ( ! origin )
  317. origin = widget->settings;
  318. setting_name ( origin, widget->row.setting, buf, sizeof ( buf ) );
  319. /* Draw row */
  320. attron ( A_BOLD );
  321. msg ( INFO_ROW, "%s - %s", buf, widget->row.setting->description );
  322. attroff ( A_BOLD );
  323. color_set ( CPAIR_URL, NULL );
  324. msg ( ( INFO_ROW + 1 ), "http://ipxe.org/cfg/%s",
  325. widget->row.setting->name );
  326. color_set ( CPAIR_NORMAL, NULL );
  327. }
  328. /**
  329. * Draw instruction row
  330. *
  331. * @v widget Setting widget
  332. */
  333. static void draw_instruction_row ( struct setting_widget *widget ) {
  334. clearmsg ( INSTRUCTION_ROW );
  335. if ( widget->row.editing ) {
  336. msg ( INSTRUCTION_ROW,
  337. "Enter - accept changes" INSTRUCTION_PAD
  338. "Ctrl-C - discard changes" );
  339. } else {
  340. msg ( INSTRUCTION_ROW,
  341. "%sCtrl-X - exit configuration utility",
  342. ( widget->row.originates_here ?
  343. "Ctrl-D - delete setting" INSTRUCTION_PAD : "" ) );
  344. }
  345. }
  346. /**
  347. * Reveal setting row
  348. *
  349. * @v widget Setting widget
  350. * @v index Index of setting row
  351. */
  352. static void reveal_setting_row ( struct setting_widget *widget,
  353. unsigned int index ) {
  354. unsigned int i;
  355. /* Simply return if setting N is already on-screen. */
  356. if ( index - widget->first_visible < SETTINGS_LIST_ROWS )
  357. return;
  358. /* Jump scroll to make the specified setting row visible. */
  359. while ( widget->first_visible < index )
  360. widget->first_visible += SETTINGS_LIST_ROWS;
  361. while ( widget->first_visible > index )
  362. widget->first_visible -= SETTINGS_LIST_ROWS;
  363. /* Draw ellipses before and/or after the settings list to
  364. * represent any invisible settings.
  365. */
  366. mvaddstr ( SETTINGS_LIST_ROW - 1,
  367. SETTINGS_LIST_COL + 1,
  368. widget->first_visible > 0 ? "..." : " " );
  369. mvaddstr ( SETTINGS_LIST_ROW + SETTINGS_LIST_ROWS,
  370. SETTINGS_LIST_COL + 1,
  371. ( ( widget->first_visible + SETTINGS_LIST_ROWS )
  372. < widget->num_rows ? "..." : " " ) );
  373. /* Draw visible settings. */
  374. for ( i = 0; i < SETTINGS_LIST_ROWS; i++ ) {
  375. if ( ( widget->first_visible + i ) < widget->num_rows ) {
  376. select_setting_row ( widget,
  377. widget->first_visible + i );
  378. draw_setting_row ( widget );
  379. } else {
  380. clearmsg ( SETTINGS_LIST_ROW + i );
  381. }
  382. }
  383. }
  384. /**
  385. * Reveal setting row
  386. *
  387. * @v widget Setting widget
  388. * @v settings Settings block
  389. */
  390. static void init_widget ( struct setting_widget *widget,
  391. struct settings *settings ) {
  392. widget->settings = settings;
  393. widget->num_rows = select_setting_row ( widget, 0 );
  394. widget->first_visible = SETTINGS_LIST_ROWS;
  395. draw_title_row ( widget );
  396. reveal_setting_row ( widget, 0 );
  397. select_setting_row ( widget, 0 );
  398. }
  399. static int main_loop ( struct settings *settings ) {
  400. struct setting_widget widget;
  401. int redraw = 1;
  402. int move;
  403. unsigned int next;
  404. int key;
  405. int rc;
  406. /* Print initial screen content */
  407. color_set ( CPAIR_NORMAL, NULL );
  408. memset ( &widget, 0, sizeof ( widget ) );
  409. init_widget ( &widget, settings );
  410. while ( 1 ) {
  411. /* Redraw rows if necessary */
  412. if ( redraw ) {
  413. draw_info_row ( &widget );
  414. draw_instruction_row ( &widget );
  415. color_set ( ( widget.row.editing ?
  416. CPAIR_EDIT : CPAIR_SELECT ), NULL );
  417. draw_setting_row ( &widget );
  418. color_set ( CPAIR_NORMAL, NULL );
  419. redraw = 0;
  420. }
  421. if ( widget.row.editing ) {
  422. /* Sanity check */
  423. assert ( widget.row.setting != NULL );
  424. /* Redraw edit box */
  425. color_set ( CPAIR_EDIT, NULL );
  426. draw_editbox ( &widget.row.editbox );
  427. color_set ( CPAIR_NORMAL, NULL );
  428. /* Process keypress */
  429. key = edit_setting ( &widget, getkey ( 0 ) );
  430. switch ( key ) {
  431. case CR:
  432. case LF:
  433. if ( ( rc = save_setting ( &widget ) ) != 0 )
  434. alert ( " %s ", strerror ( rc ) );
  435. /* Fall through */
  436. case CTRL_C:
  437. select_setting_row ( &widget, widget.current );
  438. redraw = 1;
  439. break;
  440. default:
  441. /* Do nothing */
  442. break;
  443. }
  444. } else {
  445. /* Process keypress */
  446. key = getkey ( 0 );
  447. move = 0;
  448. switch ( key ) {
  449. case KEY_DOWN:
  450. if ( widget.current < ( widget.num_rows - 1 ) )
  451. move = +1;
  452. break;
  453. case KEY_UP:
  454. if ( widget.current > 0 )
  455. move = -1;
  456. break;
  457. case CTRL_D:
  458. if ( ! widget.row.setting )
  459. break;
  460. if ( ( rc = delete_setting ( widget.settings,
  461. widget.row.setting ) ) != 0 ) {
  462. alert ( " %s ", strerror ( rc ) );
  463. }
  464. select_setting_row ( &widget, widget.current );
  465. redraw = 1;
  466. break;
  467. case CTRL_X:
  468. return 0;
  469. case CR:
  470. case LF:
  471. if ( widget.row.settings ) {
  472. init_widget ( &widget,
  473. widget.row.settings );
  474. redraw = 1;
  475. }
  476. /* Fall through */
  477. default:
  478. if ( widget.row.setting ) {
  479. edit_setting ( &widget, key );
  480. redraw = 1;
  481. }
  482. break;
  483. }
  484. if ( move ) {
  485. next = ( widget.current + move );
  486. draw_setting_row ( &widget );
  487. redraw = 1;
  488. reveal_setting_row ( &widget, next );
  489. select_setting_row ( &widget, next );
  490. }
  491. }
  492. }
  493. }
  494. int settings_ui ( struct settings *settings ) {
  495. int rc;
  496. initscr();
  497. start_color();
  498. init_pair ( CPAIR_NORMAL, COLOR_NORMAL_FG, COLOR_NORMAL_BG );
  499. init_pair ( CPAIR_SELECT, COLOR_SELECT_FG, COLOR_SELECT_BG );
  500. init_pair ( CPAIR_EDIT, COLOR_EDIT_FG, COLOR_EDIT_BG );
  501. init_pair ( CPAIR_ALERT, COLOR_ALERT_FG, COLOR_ALERT_BG );
  502. init_pair ( CPAIR_URL, COLOR_URL_FG, COLOR_URL_BG );
  503. color_set ( CPAIR_NORMAL, NULL );
  504. erase();
  505. rc = main_loop ( settings );
  506. endwin();
  507. return rc;
  508. }