ui/ncurses: Unify key bindings & key help text
[petitboot] / ui / ncurses / nc-config.c
1 /*
2  *  Copyright (C) 2013 IBM Corporation
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; version 2 of the License.
7  *
8  *  This program is distributed in the hope that it will be useful,
9  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  *  GNU General Public License for more details.
12  *
13  *  You should have received a copy of the GNU General Public License
14  *  along with this program; if not, write to the Free Software
15  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17
18 #if defined(HAVE_CONFIG_H)
19 #include "config.h"
20 #endif
21
22 #include <errno.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include <pb-config/pb-config.h>
27 #include <talloc/talloc.h>
28 #include <types/types.h>
29 #include <log/log.h>
30
31 #include "nc-cui.h"
32 #include "nc-config.h"
33 #include "nc-widgets.h"
34
35 #define N_FIELDS        24
36
37 extern const char *config_help_text;
38
39 enum net_conf_type {
40         NET_CONF_TYPE_DHCP_ALL,
41         NET_CONF_TYPE_DHCP_ONE,
42         NET_CONF_TYPE_STATIC,
43 };
44
45 struct config_screen {
46         struct nc_scr           scr;
47         struct cui              *cui;
48         struct nc_widgetset     *widgetset;
49         WINDOW                  *pad;
50
51         bool                    exit;
52         bool                    show_help;
53         void                    (*on_exit)(struct cui *);
54
55         int                     scroll_y;
56
57         int                     label_x;
58         int                     field_x;
59         int                     network_config_y;
60
61         enum net_conf_type      net_conf_type;
62
63         struct {
64                 struct nc_widget_checkbox       *autoboot_f;
65                 struct nc_widget_label          *autoboot_l;
66                 struct nc_widget_textbox        *timeout_f;
67                 struct nc_widget_label          *timeout_l;
68                 struct nc_widget_label          *timeout_help_l;
69
70                 struct nc_widget_label          *network_l;
71                 struct nc_widget_select         *network_f;
72
73                 struct nc_widget_label          *iface_l;
74                 struct nc_widget_select         *iface_f;
75                 struct nc_widget_label          *ip_addr_l;
76                 struct nc_widget_textbox        *ip_addr_f;
77                 struct nc_widget_label          *ip_mask_l;
78                 struct nc_widget_textbox        *ip_mask_f;
79                 struct nc_widget_label          *ip_addr_mask_help_l;
80                 struct nc_widget_label          *gateway_l;
81                 struct nc_widget_textbox        *gateway_f;
82                 struct nc_widget_label          *gateway_help_l;
83                 struct nc_widget_label          *dns_l;
84                 struct nc_widget_textbox        *dns_f;
85                 struct nc_widget_label          *dns_dhcp_help_l;
86                 struct nc_widget_label          *dns_help_l;
87
88                 struct nc_widget_button         *ok_b;
89                 struct nc_widget_button         *help_b;
90                 struct nc_widget_button         *cancel_b;
91         } widgets;
92 };
93
94 static struct config_screen *config_screen_from_scr(struct nc_scr *scr)
95 {
96         struct config_screen *config_screen;
97
98         assert(scr->sig == pb_config_screen_sig);
99         config_screen = (struct config_screen *)
100                 ((char *)scr - (size_t)&((struct config_screen *)0)->scr);
101         assert(config_screen->scr.sig == pb_config_screen_sig);
102         return config_screen;
103 }
104
105 static void pad_refresh(struct config_screen *screen)
106 {
107         int y, x, rows, cols;
108
109         getmaxyx(screen->scr.sub_ncw, rows, cols);
110         getbegyx(screen->scr.sub_ncw, y, x);
111
112         prefresh(screen->pad, screen->scroll_y, 0, y, x, rows, cols);
113 }
114
115 static void config_screen_process_key(struct nc_scr *scr, int key)
116 {
117         struct config_screen *screen = config_screen_from_scr(scr);
118         bool handled;
119
120         handled = widgetset_process_key(screen->widgetset, key);
121
122         if (!handled) {
123                 switch (key) {
124                 case 'x':
125                 case 27: /* esc */
126                         screen->exit = true;
127                         break;
128                 case 'h':
129                         screen->show_help = true;
130                         break;
131                 }
132         }
133
134         if (screen->exit) {
135                 screen->on_exit(screen->cui);
136
137         } else if (screen->show_help) {
138                 screen->show_help = false;
139                 cui_show_help(screen->cui, "System Configuration",
140                                 config_help_text);
141
142         } else if (handled) {
143                 pad_refresh(screen);
144         }
145 }
146
147 static void config_screen_resize(struct nc_scr *scr)
148 {
149         struct config_screen *screen = config_screen_from_scr(scr);
150         (void)screen;
151 }
152
153 static int config_screen_post(struct nc_scr *scr)
154 {
155         struct config_screen *screen = config_screen_from_scr(scr);
156         widgetset_post(screen->widgetset);
157         nc_scr_frame_draw(scr);
158         redrawwin(scr->main_ncw);
159         wrefresh(screen->scr.main_ncw);
160         pad_refresh(screen);
161         return 0;
162 }
163
164 static int config_screen_unpost(struct nc_scr *scr)
165 {
166         struct config_screen *screen = config_screen_from_scr(scr);
167         widgetset_unpost(screen->widgetset);
168         return 0;
169 }
170
171 struct nc_scr *config_screen_scr(struct config_screen *screen)
172 {
173         return &screen->scr;
174 }
175
176 static int screen_process_form(struct config_screen *screen)
177 {
178         const struct system_info *sysinfo = screen->cui->sysinfo;
179         enum net_conf_type net_conf_type;
180         struct interface_config *iface;
181         struct config *config;
182         char *str, *end;
183         int rc;
184
185         config = config_copy(screen, screen->cui->config);
186
187         config->autoboot_enabled =
188                 widget_checkbox_get_value(screen->widgets.autoboot_f);
189
190
191         str = widget_textbox_get_value(screen->widgets.timeout_f);
192         if (str) {
193                 unsigned long x;
194                 errno = 0;
195                 x = strtoul(str, &end, 10);
196                 if (!errno && end != str)
197                         config->autoboot_timeout_sec = x;
198         }
199
200         net_conf_type = widget_select_get_value(screen->widgets.network_f);
201
202         /* if we don't have any network interfaces, prevent per-interface
203          * configuration */
204         if (sysinfo->n_interfaces == 0)
205                 net_conf_type = NET_CONF_TYPE_DHCP_ALL;
206
207         if (net_conf_type == NET_CONF_TYPE_DHCP_ALL) {
208                 config->network.n_interfaces = 0;
209
210         } else {
211                 int idx;
212
213                 iface = talloc_zero(config, struct interface_config);
214                 config->network.n_interfaces = 1;
215                 config->network.interfaces = talloc_array(config,
216                                 struct interface_config *, 1);
217                 config->network.interfaces[0] = iface;
218
219                 /* copy hwaddr (from the sysinfo interface data) to
220                  * the configuration */
221                 idx = widget_select_get_value(screen->widgets.iface_f);
222                 memcpy(iface->hwaddr, sysinfo->interfaces[idx]->hwaddr,
223                                 sizeof(iface->hwaddr));
224         }
225
226         if (net_conf_type == NET_CONF_TYPE_DHCP_ONE) {
227                 iface->method = CONFIG_METHOD_DHCP;
228         }
229
230         if (net_conf_type == NET_CONF_TYPE_STATIC) {
231                 char *ip, *mask, *gateway;
232
233                 ip = widget_textbox_get_value(screen->widgets.ip_addr_f);
234                 mask = widget_textbox_get_value(screen->widgets.ip_mask_f);
235                 gateway = widget_textbox_get_value(screen->widgets.gateway_f);
236
237                 if (!ip || !*ip || !mask || !*mask) {
238                         screen->scr.frame.status =
239                                 "No IP / mask values are set";
240                         nc_scr_frame_draw(&screen->scr);
241                         talloc_free(config);
242                         return -1;
243                 }
244
245                 iface->method = CONFIG_METHOD_STATIC;
246                 iface->static_config.address = talloc_asprintf(iface, "%s/%s",
247                                 ip, mask);
248                 iface->static_config.gateway = talloc_strdup(iface, gateway);
249         }
250
251         str = widget_textbox_get_value(screen->widgets.dns_f);
252         if (str && strlen(str)) {
253                 char *dns, *tmp;
254                 int i;
255
256                 for (;;) {
257                         dns = strtok_r(str, " \t", &tmp);
258
259                         if (!dns)
260                                 break;
261
262                         i = config->network.n_dns_servers++;
263                         config->network.dns_servers = talloc_realloc(config,
264                                         config->network.dns_servers,
265                                         const char *,
266                                         config->network.n_dns_servers);
267                         config->network.dns_servers[i] =
268                                 talloc_strdup(config, dns);
269
270                         str = NULL;
271                 }
272         }
273
274         rc = cui_send_config(screen->cui, config);
275         talloc_free(config);
276
277         if (rc)
278                 pb_log("cui_send_config failed!\n");
279         else
280                 pb_debug("config sent!\n");
281
282         return 0;
283 }
284
285 static void ok_click(void *arg)
286 {
287         struct config_screen *screen = arg;
288         if (screen_process_form(screen))
289                 /* errors are written to the status line, so we'll need
290                  * to refresh */
291                 wrefresh(screen->scr.main_ncw);
292         else
293                 screen->exit = true;
294 }
295
296 static void help_click(void *arg)
297 {
298         struct config_screen *screen = arg;
299         screen->show_help = true;
300 }
301
302 static void cancel_click(void *arg)
303 {
304         struct config_screen *screen = arg;
305         screen->exit = true;
306 }
307
308 static int layout_pair(struct config_screen *screen, int y,
309                 struct nc_widget_label *label,
310                 struct nc_widget *field)
311 {
312         struct nc_widget *label_w = widget_label_base(label);
313         widget_move(label_w, y, screen->label_x);
314         widget_move(field, y, screen->field_x);
315         return max(widget_height(label_w), widget_height(field));
316 }
317
318 static void config_screen_layout_widgets(struct config_screen *screen,
319                 enum net_conf_type net_conf)
320 {
321         struct nc_widget *wl, *wf, *wh;
322         int y, x, help_x;
323         bool show;
324
325         y = 1;
326         help_x = screen->field_x + 2 +
327                 widget_width(widget_textbox_base(screen->widgets.dns_f));
328
329         y += layout_pair(screen, y, screen->widgets.autoboot_l,
330                         widget_checkbox_base(screen->widgets.autoboot_f));
331
332         wf = widget_textbox_base(screen->widgets.timeout_f);
333         widget_move(widget_label_base(screen->widgets.timeout_l),
334                         y, screen->label_x);
335         widget_move(wf, y, screen->field_x);
336         widget_move(widget_label_base(screen->widgets.timeout_help_l),
337                         y, screen->field_x + widget_width(wf) + 1);
338
339         y += 2;
340
341         y += layout_pair(screen, y, screen->widgets.network_l,
342                         widget_select_base(screen->widgets.network_f));
343
344         y += 1;
345
346         /* conditionally show iface select */
347         wl = widget_label_base(screen->widgets.iface_l);
348         wf = widget_select_base(screen->widgets.iface_f);
349
350         show = net_conf == NET_CONF_TYPE_DHCP_ONE ||
351                 net_conf == NET_CONF_TYPE_STATIC;
352
353         widget_set_visible(wl, show);
354         widget_set_visible(wf, show);
355
356         if (show)
357                 y += layout_pair(screen, y, screen->widgets.iface_l, wf) + 1;
358
359         /* conditionally show static IP params */
360         show = net_conf == NET_CONF_TYPE_STATIC;
361
362         wl = widget_label_base(screen->widgets.ip_addr_l);
363         wf = widget_textbox_base(screen->widgets.ip_addr_f);
364         widget_set_visible(wl, show);
365         widget_set_visible(wf, show);
366         x = screen->field_x + widget_width(wf) + 1;
367
368         if (show)
369                 layout_pair(screen, y, screen->widgets.ip_addr_l, wf);
370
371         wl = widget_label_base(screen->widgets.ip_mask_l);
372         wf = widget_textbox_base(screen->widgets.ip_mask_f);
373         widget_set_visible(wl, show);
374         widget_set_visible(wf, show);
375
376         if (show) {
377                 widget_move(wl, y, x);
378                 widget_move(wf, y, x + 2);
379         }
380
381         /* help for IP/mask */
382         wh = widget_label_base(screen->widgets.ip_addr_mask_help_l);
383         widget_set_visible(wh, show);
384         if (show) {
385                 widget_move(wh, y, help_x);
386                 y++;
387         }
388
389         wl = widget_label_base(screen->widgets.gateway_l);
390         wf = widget_textbox_base(screen->widgets.gateway_f);
391         wh = widget_label_base(screen->widgets.gateway_help_l);
392         widget_set_visible(wl, show);
393         widget_set_visible(wf, show);
394         widget_set_visible(wh, show);
395
396         if (show) {
397                 layout_pair(screen, y, screen->widgets.gateway_l, wf);
398                 widget_move(wh, y, help_x);
399                 y++;
400         }
401
402         wh = widget_label_base(screen->widgets.dns_help_l);
403         layout_pair(screen, y, screen->widgets.dns_l,
404                         widget_textbox_base(screen->widgets.dns_f));
405         widget_move(wh, y, help_x);
406         y++;
407
408         /* we show the DNS/DHCP help if we're configuring DHCP */
409         show = net_conf != NET_CONF_TYPE_STATIC;
410         wl = widget_label_base(screen->widgets.dns_dhcp_help_l);
411         widget_set_visible(wl, show);
412         if (show) {
413                 widget_move(wl, y, screen->field_x);
414                 y += 1;
415         }
416
417         y += 1;
418
419         widget_move(widget_button_base(screen->widgets.ok_b),
420                         y, screen->field_x);
421         widget_move(widget_button_base(screen->widgets.help_b),
422                         y, screen->field_x + 10);
423         widget_move(widget_button_base(screen->widgets.cancel_b),
424                         y, screen->field_x + 20);
425 }
426
427 static void config_screen_network_change(void *arg, int value)
428 {
429         struct config_screen *screen = arg;
430         screen->net_conf_type = value;
431         widgetset_unpost(screen->widgetset);
432         config_screen_layout_widgets(screen, value);
433         widgetset_post(screen->widgetset);
434 }
435
436 static struct interface_config *first_active_interface(
437                 const struct config *config)
438 {
439         unsigned int i;
440
441         for (i = 0; i < config->network.n_interfaces; i++) {
442                 if (config->network.interfaces[i]->ignore)
443                         continue;
444                 return config->network.interfaces[i];
445         }
446         return NULL;
447 }
448
449 static enum net_conf_type find_net_conf_type(const struct config *config)
450 {
451         struct interface_config *ifcfg;
452
453         ifcfg = first_active_interface(config);
454
455         if (!ifcfg)
456                 return NET_CONF_TYPE_DHCP_ALL;
457
458         else if (ifcfg->method == CONFIG_METHOD_DHCP)
459                 return NET_CONF_TYPE_DHCP_ONE;
460
461         else if (ifcfg->method == CONFIG_METHOD_STATIC)
462                 return NET_CONF_TYPE_STATIC;
463
464         assert(0);
465         return NET_CONF_TYPE_DHCP_ALL;
466 }
467
468 static void config_screen_setup_empty(struct config_screen *screen)
469 {
470         widget_new_label(screen->widgetset, 2, screen->field_x,
471                         "Waiting for configuration data...");
472         screen->widgets.cancel_b = widget_new_button(screen->widgetset,
473                         4, screen->field_x, 6, "Cancel", cancel_click, screen);
474 }
475
476
477 static void config_screen_setup_widgets(struct config_screen *screen,
478                 const struct config *config,
479                 const struct system_info *sysinfo)
480 {
481         struct nc_widgetset *set = screen->widgetset;
482         struct interface_config *ifcfg;
483         char *str, *ip, *mask, *gw;
484         enum net_conf_type type;
485         unsigned int i;
486
487         build_assert(sizeof(screen->widgets) / sizeof(struct widget *)
488                         == N_FIELDS);
489
490         type = screen->net_conf_type;
491         ifcfg = first_active_interface(config);
492
493         screen->widgets.autoboot_l = widget_new_label(set, 0, 0, "Autoboot:");
494         screen->widgets.autoboot_f = widget_new_checkbox(set, 0, 0,
495                                         config->autoboot_enabled);
496
497         str = talloc_asprintf(screen, "%d", config->autoboot_timeout_sec);
498         screen->widgets.timeout_l = widget_new_label(set, 0, 0, "Timeout:");
499         screen->widgets.timeout_f = widget_new_textbox(set, 0, 0, 5, str);
500         screen->widgets.timeout_help_l = widget_new_label(set, 0, 0, "seconds");
501
502         widget_textbox_set_validator_integer(screen->widgets.timeout_f, 0, 999);
503
504         screen->widgets.network_l = widget_new_label(set, 0, 0, "Network");
505         screen->widgets.network_f = widget_new_select(set, 0, 0, 50);
506
507         widget_select_add_option(screen->widgets.network_f,
508                                         NET_CONF_TYPE_DHCP_ALL,
509                                         "DHCP on all active interfaces",
510                                         type == NET_CONF_TYPE_DHCP_ALL);
511         widget_select_add_option(screen->widgets.network_f,
512                                         NET_CONF_TYPE_DHCP_ONE,
513                                         "DHCP on a specific interface",
514                                         type == NET_CONF_TYPE_DHCP_ONE);
515         widget_select_add_option(screen->widgets.network_f,
516                                         NET_CONF_TYPE_STATIC,
517                                         "Static IP configuration",
518                                         type == NET_CONF_TYPE_STATIC);
519
520         widget_select_on_change(screen->widgets.network_f,
521                         config_screen_network_change, screen);
522
523         screen->widgets.iface_l = widget_new_label(set, 0, 0, "Device:");
524         screen->widgets.iface_f = widget_new_select(set, 0, 0, 50);
525
526         for (i = 0; i < sysinfo->n_interfaces; i++) {
527                 struct interface_info *info = sysinfo->interfaces[i];
528                 char str[50], mac[20];
529                 bool is_default;
530
531                 is_default = ifcfg && !memcmp(ifcfg->hwaddr, info->hwaddr,
532                                         sizeof(ifcfg->hwaddr));
533
534                 mac_str(info->hwaddr, info->hwaddr_size, mac, sizeof(mac));
535                 snprintf(str, sizeof(str), "%s [%s, %s]", info->name, mac,
536                                 info->link ? "link up" : "link down");
537
538                 widget_select_add_option(screen->widgets.iface_f,
539                                                 i, str, is_default);
540         }
541
542         gw = ip = mask = NULL;
543         if (ifcfg && ifcfg->method == CONFIG_METHOD_STATIC) {
544                 char *sep;
545
546                 str = talloc_strdup(screen, ifcfg->static_config.address);
547                 sep = strchr(str, '/');
548                 ip = str;
549
550                 if (sep) {
551                         *sep = '\0';
552                         mask = sep + 1;
553                 }
554                 gw = ifcfg->static_config.gateway;
555         }
556
557         screen->widgets.ip_addr_l = widget_new_label(set, 0, 0, "IP/mask:");
558         screen->widgets.ip_addr_f = widget_new_textbox(set, 0, 0, 16, ip);
559         screen->widgets.ip_mask_l = widget_new_label(set, 0, 0, "/");
560         screen->widgets.ip_mask_f = widget_new_textbox(set, 0, 0, 4, mask);
561         screen->widgets.ip_addr_mask_help_l =
562                 widget_new_label(set, 0, 0, "(eg. 192.168.0.10 / 24)");
563
564         widget_textbox_set_validator_ipv4(screen->widgets.ip_addr_f);
565         widget_textbox_set_validator_integer(screen->widgets.ip_mask_f, 1, 31);
566
567         screen->widgets.gateway_l = widget_new_label(set, 0, 0, "Gateway:");
568         screen->widgets.gateway_f = widget_new_textbox(set, 0, 0, 16, gw);
569         screen->widgets.gateway_help_l =
570                 widget_new_label(set, 0, 0, "(eg. 192.168.0.1)");
571
572         widget_textbox_set_validator_ipv4(screen->widgets.gateway_f);
573
574         str = talloc_strdup(screen, "");
575         for (i = 0; i < config->network.n_dns_servers; i++) {
576                 str = talloc_asprintf_append(str, "%s%s",
577                                 (i == 0) ? "" : " ",
578                                 config->network.dns_servers[i]);
579         }
580
581         screen->widgets.dns_l = widget_new_label(set, 0, 0, "DNS Server(s):");
582         screen->widgets.dns_f = widget_new_textbox(set, 0, 0, 32, str);
583         screen->widgets.dns_help_l =
584                 widget_new_label(set, 0, 0, "(eg. 192.168.0.2)");
585
586         widget_textbox_set_validator_ipv4_multi(screen->widgets.dns_f);
587
588         screen->widgets.dns_dhcp_help_l = widget_new_label(set, 0, 0,
589                         "(if not provided by DHCP server)");
590
591         screen->widgets.ok_b = widget_new_button(set, 0, 0, 6, "OK",
592                         ok_click, screen);
593         screen->widgets.help_b = widget_new_button(set, 0, 0, 6, "Help",
594                         help_click, screen);
595         screen->widgets.cancel_b = widget_new_button(set, 0, 0, 6, "Cancel",
596                         cancel_click, screen);
597 }
598
599 static void config_screen_widget_focus(struct nc_widget *widget, void *arg)
600 {
601         struct config_screen *screen = arg;
602         int w_y, s_max;
603
604         w_y = widget_y(widget) + widget_focus_y(widget);
605         s_max = getmaxy(screen->scr.sub_ncw) - 1;
606
607         if (w_y < screen->scroll_y)
608                 screen->scroll_y = w_y;
609
610         else if (w_y + screen->scroll_y + 1 > s_max)
611                 screen->scroll_y = 1 + w_y - s_max;
612
613         else
614                 return;
615
616         pad_refresh(screen);
617 }
618
619 static void config_screen_draw(struct config_screen *screen,
620                 const struct config *config,
621                 const struct system_info *sysinfo)
622 {
623         bool repost = false;
624         int height;
625
626         /* The size of the pad we'll need depends on the number of interfaces.
627          *
628          * We use N_FIELDS (which is quite conservative, as some fields share
629          * a line) as a base, then add 3 (as the network select field is
630          * takes 3 lines), and n_interfaces (as the network interface field
631          * has n_interfaces lines).
632          */
633         height = N_FIELDS + 3;
634         if (sysinfo)
635                 height += sysinfo->n_interfaces;
636         if (!screen->pad || getmaxy(screen->pad) < height) {
637                 if (screen->pad)
638                         delwin(screen->pad);
639                 screen->pad = newpad(height, COLS);
640         }
641
642         if (screen->widgetset) {
643                 widgetset_unpost(screen->widgetset);
644                 talloc_free(screen->widgetset);
645                 repost = true;
646         }
647
648         screen->widgetset = widgetset_create(screen, screen->scr.main_ncw,
649                         screen->pad);
650         widgetset_set_widget_focus(screen->widgetset,
651                         config_screen_widget_focus, screen);
652
653         if (!config || !sysinfo) {
654                 config_screen_setup_empty(screen);
655         } else {
656                 screen->net_conf_type = find_net_conf_type(config);
657
658                 config_screen_setup_widgets(screen, config, sysinfo);
659                 config_screen_layout_widgets(screen, screen->net_conf_type);
660         }
661
662         if (repost)
663                 widgetset_post(screen->widgetset);
664 }
665
666 void config_screen_update(struct config_screen *screen,
667                 const struct config *config,
668                 const struct system_info *sysinfo)
669 {
670         config_screen_draw(screen, config, sysinfo);
671         pad_refresh(screen);
672 }
673
674 static int config_screen_destroy(void *arg)
675 {
676         struct config_screen *screen = arg;
677         if (screen->pad)
678                 delwin(screen->pad);
679         return 0;
680 }
681
682 struct config_screen *config_screen_init(struct cui *cui,
683                 const struct config *config,
684                 const struct system_info *sysinfo,
685                 void (*on_exit)(struct cui *))
686 {
687         struct config_screen *screen;
688
689         screen = talloc_zero(cui, struct config_screen);
690         talloc_set_destructor(screen, config_screen_destroy);
691         nc_scr_init(&screen->scr, pb_config_screen_sig, 0,
692                         cui, config_screen_process_key,
693                         config_screen_post, config_screen_unpost,
694                         config_screen_resize);
695
696         screen->cui = cui;
697         screen->on_exit = on_exit;
698         screen->label_x = 2;
699         screen->field_x = 17;
700
701         screen->scr.frame.ltitle = talloc_strdup(screen,
702                         "Petitboot System Configuration");
703         screen->scr.frame.rtitle = NULL;
704         screen->scr.frame.help = talloc_strdup(screen,
705                         "tab=next, shift+tab=previous, x=exit, h=help");
706         nc_scr_frame_draw(&screen->scr);
707
708         scrollok(screen->scr.sub_ncw, true);
709
710         config_screen_draw(screen, config, sysinfo);
711
712         return screen;
713 }