Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

settings_ui.c 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  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., 675 Mass Ave, Cambridge, MA 02139, USA.
  17. */
  18. FILE_LICENCE ( GPL2_OR_LATER );
  19. #include <stdio.h>
  20. #include <stdarg.h>
  21. #include <unistd.h>
  22. #include <string.h>
  23. #include <curses.h>
  24. #include <ipxe/console.h>
  25. #include <ipxe/settings.h>
  26. #include <ipxe/editbox.h>
  27. #include <ipxe/keys.h>
  28. #include <ipxe/settings_ui.h>
  29. /** @file
  30. *
  31. * Option configuration console
  32. *
  33. */
  34. /* Colour pairs */
  35. #define CPAIR_NORMAL 1
  36. #define CPAIR_SELECT 2
  37. #define CPAIR_EDIT 3
  38. #define CPAIR_ALERT 4
  39. /* Screen layout */
  40. #define TITLE_ROW 1
  41. #define SETTINGS_LIST_ROW 3
  42. #define SETTINGS_LIST_COL 1
  43. #define SETTINGS_LIST_ROWS 16
  44. #define INFO_ROW 20
  45. #define ALERT_ROW 20
  46. #define INSTRUCTION_ROW 22
  47. #define INSTRUCTION_PAD " "
  48. /** Layout of text within a setting widget */
  49. struct setting_row {
  50. char start[0];
  51. char pad1[1];
  52. char name[15];
  53. char pad2[1];
  54. char value[60];
  55. char pad3[1];
  56. char nul;
  57. } __attribute__ (( packed ));
  58. /** A setting widget */
  59. struct setting_widget {
  60. /** Settings block */
  61. struct settings *settings;
  62. /** Index of the first visible setting, for scrolling. */
  63. unsigned int first_visible;
  64. /** Configuration setting */
  65. struct setting *setting;
  66. /** Screen row */
  67. unsigned int row;
  68. /** Screen column */
  69. unsigned int col;
  70. /** Edit box widget used for editing setting */
  71. struct edit_box editbox;
  72. /** Editing in progress flag */
  73. int editing;
  74. /** Buffer for setting's value */
  75. char value[256]; /* enough size for a DHCP string */
  76. };
  77. /** Number of registered configuration settings */
  78. #define NUM_SETTINGS table_num_entries ( SETTINGS )
  79. static void load_setting ( struct setting_widget *widget ) __nonnull;
  80. static int save_setting ( struct setting_widget *widget ) __nonnull;
  81. static void init_widget ( struct setting_widget *widget,
  82. struct settings *settings ) __nonnull;
  83. static void draw_setting ( struct setting_widget *widget ) __nonnull;
  84. static int edit_setting ( struct setting_widget *widget, int key ) __nonnull;
  85. static void select_setting ( struct setting_widget *widget,
  86. unsigned int index ) __nonnull;
  87. static void reveal ( struct setting_widget *widget, unsigned int n) __nonnull;
  88. static void vmsg ( unsigned int row, const char *fmt, va_list args ) __nonnull;
  89. static void msg ( unsigned int row, const char *fmt, ... ) __nonnull;
  90. static void valert ( const char *fmt, va_list args ) __nonnull;
  91. static void alert ( const char *fmt, ... ) __nonnull;
  92. static void draw_info_row ( struct setting *setting ) __nonnull;
  93. static int main_loop ( struct settings *settings ) __nonnull;
  94. /**
  95. * Load setting widget value from configuration settings
  96. *
  97. * @v widget Setting widget
  98. *
  99. */
  100. static void load_setting ( struct setting_widget *widget ) {
  101. /* Mark as not editing */
  102. widget->editing = 0;
  103. /* Read current setting value */
  104. if ( fetchf_setting ( widget->settings, widget->setting,
  105. widget->value, sizeof ( widget->value ) ) < 0 ) {
  106. widget->value[0] = '\0';
  107. }
  108. /* Initialise edit box */
  109. init_editbox ( &widget->editbox, widget->value,
  110. sizeof ( widget->value ), NULL, widget->row,
  111. ( widget->col + offsetof ( struct setting_row, value )),
  112. sizeof ( ( ( struct setting_row * ) NULL )->value ), 0);
  113. }
  114. /**
  115. * Save setting widget value back to configuration settings
  116. *
  117. * @v widget Setting widget
  118. */
  119. static int save_setting ( struct setting_widget *widget ) {
  120. return storef_setting ( widget->settings, widget->setting,
  121. widget->value );
  122. }
  123. /**
  124. * Initialise the scrolling setting widget, drawing initial display.
  125. *
  126. * @v widget Setting widget
  127. * @v settings Settings block
  128. */
  129. static void init_widget ( struct setting_widget *widget,
  130. struct settings *settings ) {
  131. memset ( widget, 0, sizeof ( *widget ) );
  132. widget->settings = settings;
  133. widget->first_visible = SETTINGS_LIST_ROWS;
  134. reveal ( widget, 0 );
  135. }
  136. /**
  137. * Draw setting widget
  138. *
  139. * @v widget Setting widget
  140. */
  141. static void draw_setting ( struct setting_widget *widget ) {
  142. struct setting_row row;
  143. unsigned int len;
  144. unsigned int curs_col;
  145. char *value;
  146. /* Fill row with spaces */
  147. memset ( &row, ' ', sizeof ( row ) );
  148. row.nul = '\0';
  149. /* Construct dot-padded name */
  150. memset ( row.name, '.', sizeof ( row.name ) );
  151. len = strlen ( widget->setting->name );
  152. if ( len > sizeof ( row.name ) )
  153. len = sizeof ( row.name );
  154. memcpy ( row.name, widget->setting->name, len );
  155. /* Construct space-padded value */
  156. value = widget->value;
  157. if ( ! *value )
  158. value = "<not specified>";
  159. len = strlen ( value );
  160. if ( len > sizeof ( row.value ) )
  161. len = sizeof ( row.value );
  162. memcpy ( row.value, value, len );
  163. curs_col = ( widget->col + offsetof ( typeof ( row ), value )
  164. + len );
  165. /* Print row */
  166. mvprintw ( widget->row, widget->col, "%s", row.start );
  167. move ( widget->row, curs_col );
  168. if ( widget->editing )
  169. draw_editbox ( &widget->editbox );
  170. }
  171. /**
  172. * Edit setting widget
  173. *
  174. * @v widget Setting widget
  175. * @v key Key pressed by user
  176. * @ret key Key returned to application, or zero
  177. */
  178. static int edit_setting ( struct setting_widget *widget, int key ) {
  179. widget->editing = 1;
  180. return edit_editbox ( &widget->editbox, key );
  181. }
  182. /**
  183. * Select a setting for display updates, by index.
  184. *
  185. * @v widget Setting widget
  186. * @v settings Settings block
  187. * @v index Index of setting with settings list
  188. */
  189. static void select_setting ( struct setting_widget *widget,
  190. unsigned int index ) {
  191. struct setting *all_settings = table_start ( SETTINGS );
  192. unsigned int skip = offsetof ( struct setting_widget, setting );
  193. /* Reset the widget, preserving static state. */
  194. memset ( ( char * ) widget + skip, 0, sizeof ( *widget ) - skip );
  195. widget->setting = &all_settings[index];
  196. widget->row = SETTINGS_LIST_ROW + index - widget->first_visible;
  197. widget->col = SETTINGS_LIST_COL;
  198. /* Read current setting value */
  199. load_setting ( widget );
  200. }
  201. /**
  202. * Print message centred on specified row
  203. *
  204. * @v row Row
  205. * @v fmt printf() format string
  206. * @v args printf() argument list
  207. */
  208. static void vmsg ( unsigned int row, const char *fmt, va_list args ) {
  209. char buf[COLS];
  210. size_t len;
  211. len = vsnprintf ( buf, sizeof ( buf ), fmt, args );
  212. mvprintw ( row, ( ( COLS - len ) / 2 ), "%s", buf );
  213. }
  214. /**
  215. * Print message centred on specified row
  216. *
  217. * @v row Row
  218. * @v fmt printf() format string
  219. * @v .. printf() arguments
  220. */
  221. static void msg ( unsigned int row, const char *fmt, ... ) {
  222. va_list args;
  223. va_start ( args, fmt );
  224. vmsg ( row, fmt, args );
  225. va_end ( args );
  226. }
  227. /**
  228. * Clear message on specified row
  229. *
  230. * @v row Row
  231. */
  232. static void clearmsg ( unsigned int row ) {
  233. move ( row, 0 );
  234. clrtoeol();
  235. }
  236. /**
  237. * Print alert message
  238. *
  239. * @v fmt printf() format string
  240. * @v args printf() argument list
  241. */
  242. static void valert ( const char *fmt, va_list args ) {
  243. clearmsg ( ALERT_ROW );
  244. color_set ( CPAIR_ALERT, NULL );
  245. vmsg ( ALERT_ROW, fmt, args );
  246. sleep ( 2 );
  247. color_set ( CPAIR_NORMAL, NULL );
  248. clearmsg ( ALERT_ROW );
  249. }
  250. /**
  251. * Print alert message
  252. *
  253. * @v fmt printf() format string
  254. * @v ... printf() arguments
  255. */
  256. static void alert ( const char *fmt, ... ) {
  257. va_list args;
  258. va_start ( args, fmt );
  259. valert ( fmt, args );
  260. va_end ( args );
  261. }
  262. /**
  263. * Draw title row
  264. */
  265. static void draw_title_row ( void ) {
  266. attron ( A_BOLD );
  267. msg ( TITLE_ROW, "iPXE option configuration console" );
  268. attroff ( A_BOLD );
  269. }
  270. /**
  271. * Draw information row
  272. *
  273. * @v setting Current configuration setting
  274. */
  275. static void draw_info_row ( struct setting *setting ) {
  276. clearmsg ( INFO_ROW );
  277. attron ( A_BOLD );
  278. msg ( INFO_ROW, "%s - %s", setting->name, setting->description );
  279. attroff ( A_BOLD );
  280. }
  281. /**
  282. * Draw instruction row
  283. *
  284. * @v editing Editing in progress flag
  285. */
  286. static void draw_instruction_row ( int editing ) {
  287. clearmsg ( INSTRUCTION_ROW );
  288. if ( editing ) {
  289. msg ( INSTRUCTION_ROW,
  290. "Enter - accept changes" INSTRUCTION_PAD
  291. "Ctrl-C - discard changes" );
  292. } else {
  293. msg ( INSTRUCTION_ROW,
  294. "Ctrl-D - delete setting" INSTRUCTION_PAD
  295. "Ctrl-X - exit configuration utility" );
  296. }
  297. }
  298. /**
  299. * Reveal a setting by index: Scroll the setting list to reveal the
  300. * specified setting.
  301. *
  302. * @widget The main loop's display widget.
  303. * @n The index of the setting to reveal.
  304. */
  305. static void reveal ( struct setting_widget *widget, unsigned int n)
  306. {
  307. unsigned int i;
  308. /* Simply return if setting N is already on-screen. */
  309. if ( n - widget->first_visible < SETTINGS_LIST_ROWS )
  310. return;
  311. /* Jump scroll to make the specified setting visible. */
  312. while ( widget->first_visible < n )
  313. widget->first_visible += SETTINGS_LIST_ROWS;
  314. while ( widget->first_visible > n )
  315. widget->first_visible -= SETTINGS_LIST_ROWS;
  316. /* Draw elipses before and/or after the settings list to
  317. represent any invisible settings. */
  318. mvaddstr ( SETTINGS_LIST_ROW - 1,
  319. SETTINGS_LIST_COL + 1,
  320. widget->first_visible > 0 ? "..." : " " );
  321. mvaddstr ( SETTINGS_LIST_ROW + SETTINGS_LIST_ROWS,
  322. SETTINGS_LIST_COL + 1,
  323. ( widget->first_visible + SETTINGS_LIST_ROWS < NUM_SETTINGS
  324. ? "..."
  325. : " " ) );
  326. /* Draw visible settings. */
  327. for ( i = 0; i < SETTINGS_LIST_ROWS; i++ ) {
  328. if ( widget->first_visible + i < NUM_SETTINGS ) {
  329. select_setting ( widget, widget->first_visible + i );
  330. draw_setting ( widget );
  331. } else {
  332. clearmsg ( SETTINGS_LIST_ROW + i );
  333. }
  334. }
  335. /* Set the widget to the current row, which will be redrawn
  336. appropriately by the main loop. */
  337. select_setting ( widget, n );
  338. }
  339. static int main_loop ( struct settings *settings ) {
  340. struct setting_widget widget;
  341. unsigned int current = 0;
  342. unsigned int next;
  343. int key;
  344. int rc;
  345. /* Print initial screen content */
  346. draw_title_row();
  347. color_set ( CPAIR_NORMAL, NULL );
  348. init_widget ( &widget, settings );
  349. while ( 1 ) {
  350. /* Redraw information and instruction rows */
  351. draw_info_row ( widget.setting );
  352. draw_instruction_row ( widget.editing );
  353. /* Redraw current setting */
  354. color_set ( ( widget.editing ? CPAIR_EDIT : CPAIR_SELECT ),
  355. NULL );
  356. draw_setting ( &widget );
  357. color_set ( CPAIR_NORMAL, NULL );
  358. key = getkey ( 0 );
  359. if ( widget.editing ) {
  360. key = edit_setting ( &widget, key );
  361. switch ( key ) {
  362. case CR:
  363. case LF:
  364. if ( ( rc = save_setting ( &widget ) ) != 0 ) {
  365. alert ( " Could not set %s: %s ",
  366. widget.setting->name,
  367. strerror ( rc ) );
  368. }
  369. /* Fall through */
  370. case CTRL_C:
  371. load_setting ( &widget );
  372. break;
  373. default:
  374. /* Do nothing */
  375. break;
  376. }
  377. } else {
  378. next = current;
  379. switch ( key ) {
  380. case KEY_DOWN:
  381. if ( next < ( NUM_SETTINGS - 1 ) )
  382. reveal ( &widget, ++next );
  383. break;
  384. case KEY_UP:
  385. if ( next > 0 )
  386. reveal ( &widget, --next ) ;
  387. break;
  388. case CTRL_D:
  389. delete_setting ( widget.settings,
  390. widget.setting );
  391. select_setting ( &widget, next );
  392. draw_setting ( &widget );
  393. break;
  394. case CTRL_X:
  395. return 0;
  396. default:
  397. edit_setting ( &widget, key );
  398. break;
  399. }
  400. if ( next != current ) {
  401. draw_setting ( &widget );
  402. select_setting ( &widget, next );
  403. current = next;
  404. }
  405. }
  406. }
  407. }
  408. int settings_ui ( struct settings *settings ) {
  409. int rc;
  410. initscr();
  411. start_color();
  412. init_pair ( CPAIR_NORMAL, COLOR_WHITE, COLOR_BLUE );
  413. init_pair ( CPAIR_SELECT, COLOR_WHITE, COLOR_RED );
  414. init_pair ( CPAIR_EDIT, COLOR_BLACK, COLOR_CYAN );
  415. init_pair ( CPAIR_ALERT, COLOR_WHITE, COLOR_RED );
  416. color_set ( CPAIR_NORMAL, NULL );
  417. erase();
  418. rc = main_loop ( settings );
  419. endwin();
  420. return rc;
  421. }