You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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