]> git.ozlabs.org Git - petitboot/blob - ui/twin/main-generic.c
28a96e461f1223b1db244578c25790b37844591e
[petitboot] / ui / twin / main-generic.c
1 /*
2  * Petitboot twin bootloader
3  *
4  *  Copyright Geoff Levand <geoff@infradead.org>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; version 2 of the License.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20 #if defined(HAVE_CONFIG_H)
21 #include "config.h"
22 #endif
23
24 #define _GNU_SOURCE
25 #include <assert.h>
26 #include <errno.h>
27 #include <getopt.h>
28 #include <signal.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/time.h>
32
33 #include "log/log.h"
34 #include "talloc/talloc.h"
35 #include "waiter/waiter.h"
36 #include "ui/common/timer.h"
37
38 #include "pbt-client.h"
39 #include "pbt-main.h"
40
41
42 static struct pbt_client *client_from_item(struct pbt_item *item)
43 {
44         return item->data;
45 }
46
47 static int exit_to_shell_cb(struct pbt_item *item)
48 {
49         struct pbt_client *client = client_from_item(item);
50
51         client->signal_data.abort = 1;
52         return 0;
53 }
54
55 static int edit_preferences_cb(struct pbt_item *item)
56 {
57         struct pbt_client *client = client_from_item(item);
58
59         (void)client;
60
61         pb_log("%s: TODO\n", __func__);
62
63         return 0;
64 }
65
66 static struct pbt_item *setup_system_item(struct pbt_menu *menu,
67         struct pbt_client *client)
68 {
69         struct pbt_item *top_item;
70         struct pbt_item *sub_item;
71         struct pbt_quad q;
72
73         top_item = pbt_item_create_reduced(menu, "system", 0,
74                 PB_ARTWORK_PATH "/applications-system.png");
75
76         if (!top_item)
77                 goto fail_top_item_create;
78
79         /* sub_menu */
80
81         q.x = menu->window->pixmap->width;
82         q.y = 0;
83         q.width = menu->scr->tscreen->width - q.x;
84         q.height = menu->scr->tscreen->height;
85
86         top_item->sub_menu = pbt_menu_create(top_item, "system", menu->scr,
87                 menu, &q, &menu->layout);
88
89         if (!top_item->sub_menu)
90                 goto fail_sub_menu_create;
91
92         sub_item = pbt_item_create(top_item->sub_menu, "Preferences", 0,
93                 PB_ARTWORK_PATH "/configure.png", "Preferences",
94                 "Edit petitboot preferences");
95
96         if (!sub_item)
97                 goto fail_sub_item_0;
98
99         sub_item->on_execute = edit_preferences_cb;
100         sub_item->data = client;
101         pbt_menu_set_selected(top_item->sub_menu, sub_item);
102
103         sub_item = pbt_item_create(top_item->sub_menu, "Exit to Shell", 1,
104                 PB_ARTWORK_PATH "/utilities-terminal.png", "Exit to Shell",
105                 "Exit to a system shell prompt");
106
107         if (!sub_item)
108                 goto fail_sub_item_1;
109
110         sub_item->on_execute = exit_to_shell_cb;
111         sub_item->data = client;
112
113         top_item->sub_menu->n_items = 2;
114
115         /* Set shell item as default */
116
117         pbt_menu_set_selected(top_item->sub_menu, sub_item);
118
119         return top_item;
120
121 fail_sub_item_1:
122 fail_sub_item_0:
123 fail_sub_menu_create:
124 // FIXME: todo
125 fail_top_item_create:
126 // FIXME: need cleanup
127         assert(0);
128         return NULL;
129 }
130
131 static struct pbt_menu *menu_create(struct pbt_client *client)
132 {
133         static struct pbt_menu_layout layout = {
134                 .item_height = 64,
135                 .item_space = 10,
136                 .text_space = 5,
137                 .title = {.font_size = 30, .color = 0xff000000,},
138                 .text = {.font_size = 18, .color = 0xff800000,},
139         };
140
141         struct pbt_menu *device_menu;
142         struct pbt_item *system_item;
143         struct pbt_quad q;
144         twin_pixmap_t *icon;
145         const struct pbt_border *border;
146
147         assert(client->frame.scr);
148
149         icon = pbt_icon_load(NULL);
150
151         if (!icon)
152                 return NULL;
153
154         assert((unsigned int)icon->height == layout.item_height);
155
156         /* Create main (device) menu */
157
158         border = &pbt_right_border;
159
160         q.x = 0;
161         q.y = 0;
162         q.width = icon->width + 2 * layout.item_space + border->left
163                 + border->right;
164         q.height = client->frame.scr->tscreen->height;
165
166         device_menu = pbt_menu_create(client, "device", client->frame.scr, NULL,
167                 &q, &layout);
168
169         if (!device_menu)
170                 goto fail_menu;
171
172         //FIXME: move to accessors
173         device_menu->background_color = 0x80000000;
174         device_menu->border = *border;
175
176         /* Setup system item */
177
178         system_item = setup_system_item(device_menu, client);
179
180         if (!system_item)
181                 goto fail_system_item;
182
183         device_menu->n_items++;
184
185         /* Set system item as default */
186
187         pbt_menu_set_selected(device_menu, system_item);
188         pbt_menu_set_focus(device_menu, 1);
189         pbt_menu_show(device_menu, 1);
190
191         pbt_menu_redraw(device_menu);
192
193         return device_menu;
194
195 fail_system_item:
196         // FIXME: need cleanup
197 fail_menu:
198         assert(0);
199         return NULL;
200 }
201
202 static int kexec_cb(struct pbt_client *client, struct pb_opt_data *opt_data)
203 {
204         int result;
205
206         assert(opt_data);
207
208         pb_log("%s: %s\n", __func__, opt_data->name);
209
210         result = pb_run_kexec(opt_data->bd, client->dry_run);
211
212         return result;
213 }
214
215 static int run(struct pbt_client *client)
216 {
217         while (1) {
218                 int result = waiter_poll(client->waitset);
219
220                 if (result < 0 && errno != EINTR) {
221                         pb_log("%s: poll: %s\n", __func__, strerror(errno));
222                         break;
223                 }
224
225                 if (client->signal_data.abort)
226                         break;
227
228                 ui_timer_process_sig(&client->signal_data.timer);
229
230                 while (client->signal_data.resize) {
231                         client->signal_data.resize = 0;
232                         pbt_client_resize(client);
233                 }
234         }
235
236         return 0;
237 }
238
239 static struct pb_signal_data *_signal_data;
240
241 static void set_signal_data(struct pb_signal_data *sd)
242 {
243         _signal_data = sd;
244 }
245
246 static struct pb_signal_data *get_signal_data(void)
247 {
248         return _signal_data;
249 }
250
251 static void sig_handler(int signum)
252 {
253         DBGS("%d\n", signum);
254
255         struct pb_signal_data *sd = get_signal_data();
256
257         if (!sd)
258                 return;
259
260         switch (signum) {
261         case SIGALRM:
262                 ui_timer_sigalrm(&sd->timer);
263                 break;
264         case SIGWINCH:
265                 sd->resize = 1;
266                 break;
267         default:
268                 assert(0 && "unknown sig");
269                 /* fall through */
270         case SIGINT:
271         case SIGHUP:
272         case SIGTERM:
273                 sd->abort = 1;
274                 break;
275         }
276 }
277
278 /**
279  * main - twin bootloader main routine.
280  */
281
282 int main(int argc, char *argv[])
283 {
284         static struct sigaction sa;
285         static struct pbt_opts opts;
286         int result;
287         int ui_result;
288         struct pbt_client *client;
289
290         result = pbt_opts_parse(&opts, argc, argv);
291
292         if (result) {
293                 pbt_print_usage();
294                 return EXIT_FAILURE;
295         }
296
297         if (opts.show_help == pbt_opt_yes) {
298                 pbt_print_usage();
299                 return EXIT_SUCCESS;
300         }
301
302         if (opts.show_version == pbt_opt_yes) {
303                 pbt_print_version();
304                 return EXIT_SUCCESS;
305         }
306
307         if (strcmp(opts.log_file, "-")) {
308                 FILE *log = fopen(opts.log_file, "a");
309
310                 assert(log);
311                 pb_log_set_stream(log);
312         } else
313                 pb_log_set_stream(stderr);
314
315 #if defined(DEBUG)
316         pb_log_always_flush(1);
317 #endif
318
319         pb_log("--- petitboot-twin ---\n");
320
321         sa.sa_handler = sig_handler;
322         result = sigaction(SIGALRM, &sa, NULL);
323         result += sigaction(SIGHUP, &sa, NULL);
324         result += sigaction(SIGINT, &sa, NULL);
325         result += sigaction(SIGTERM, &sa, NULL);
326         result += sigaction(SIGWINCH, &sa, NULL);
327
328         if (result) {
329                 pb_log("%s sigaction failed.\n", __func__);
330                 return EXIT_FAILURE;
331         }
332
333         client = pbt_client_init(opts.backend, 1024, 640, kexec_cb,
334                 opts.start_daemon, opts.dry_run);
335
336         if (!client) {
337                 ui_result = EXIT_FAILURE;
338                 goto done;
339         }
340
341         set_signal_data(&client->signal_data);
342
343         client->frame.top_menu = menu_create(client);
344
345         if (!client->frame.top_menu) {
346                 ui_result = EXIT_FAILURE;
347                 goto done;
348         }
349
350         twin_screen_update(client->frame.scr->tscreen);
351         ui_result = run(client);
352
353 done:
354         talloc_free(client);
355
356         pb_log("--- end ---\n");
357
358         return ui_result ? EXIT_FAILURE : EXIT_SUCCESS;
359 }