diff options
Diffstat (limited to 'ui/twin/main-generic.c')
-rw-r--r-- | ui/twin/main-generic.c | 355 |
1 files changed, 355 insertions, 0 deletions
diff --git a/ui/twin/main-generic.c b/ui/twin/main-generic.c new file mode 100644 index 0000000..d314cbc --- /dev/null +++ b/ui/twin/main-generic.c @@ -0,0 +1,355 @@ +/* + * Petitboot twin bootloader + * + * Copyright Geoff Levand <geoff@infradead.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#define _GNU_SOURCE +#include <assert.h> +#include <errno.h> +#include <getopt.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <sys/time.h> + +#include "log/log.h" +#include "talloc/talloc.h" +#include "waiter/waiter.h" +#include "ui/common/timer.h" + +#include "pbt-client.h" +#include "pbt-main.h" + + +static struct pbt_client *client_from_item(struct pbt_item *item) +{ + return item->data; +} + +static int exit_to_shell_cb(struct pbt_item *item) +{ + struct pbt_client *client = client_from_item(item); + + client->signal_data.abort = 1; + return 0; +} + +static int edit_preferences_cb(struct pbt_item *item) +{ + struct pbt_client *client = client_from_item(item); + + (void)client; + + pb_log("%s: TODO\n", __func__); + + return 0; +} + +static struct pbt_item *setup_system_item(struct pbt_menu *menu, + struct pbt_client *client) +{ + struct pbt_item *top_item; + struct pbt_item *sub_item; + struct pbt_quad q; + + top_item = pbt_item_create_reduced(menu, "system", 0, + PB_ARTWORK_PATH "/system.png"); + + if (!top_item) + goto fail_top_item_create; + + /* sub_menu */ + + q.x = menu->window->pixmap->width; + q.y = 0; + q.width = menu->scr->tscreen->width - q.x; + q.height = menu->scr->tscreen->height; + + top_item->sub_menu = pbt_menu_create(top_item, "system", menu->scr, + menu, &q, &menu->layout); + + if (!top_item->sub_menu) + goto fail_sub_menu_create; + + sub_item = pbt_item_create(top_item->sub_menu, "Preferences", 0, + PB_ARTWORK_PATH "/system.png", "Preferences", + "Edit petitboot preferences"); + + if (!sub_item) + goto fail_sub_item_0; + + sub_item->on_execute = edit_preferences_cb; + sub_item->data = client; + pbt_menu_set_selected(top_item->sub_menu, sub_item); + + sub_item = pbt_item_create(top_item->sub_menu, "Exit to Shell", 1, + PB_ARTWORK_PATH "/system.png", "Exit to Shell", + "Exit to a system shell prompt"); + + if (!sub_item) + goto fail_sub_item_1; + + sub_item->on_execute = exit_to_shell_cb; + sub_item->data = client; + + top_item->sub_menu->n_items = 2; + + /* Set shell item as default */ + + pbt_menu_set_selected(top_item->sub_menu, sub_item); + + return top_item; + +fail_sub_item_1: +fail_sub_item_0: +fail_sub_menu_create: +// FIXME: todo +fail_top_item_create: +// FIXME: need cleanup + assert(0); + return NULL; +} + +static struct pbt_menu *menu_create(struct pbt_client *client) +{ + static struct pbt_menu_layout layout = { + .item_height = 64, + .item_space = 10, + .text_space = 5, + .title = {.font_size = 30, .color = 0xff000000,}, + .text = {.font_size = 18, .color = 0xff800000,}, + }; + + struct pbt_menu *device_menu; + struct pbt_item *system_item; + struct pbt_quad q; + twin_pixmap_t *icon; + const struct pbt_border *border; + + assert(client->frame.scr); + + icon = pbt_icon_load(NULL); + + if (!icon) + return NULL; + + assert((unsigned int)icon->height == layout.item_height); + + /* Create main (device) menu */ + + border = &pbt_right_border; + + q.x = 0; + q.y = 0; + q.width = icon->width + 2 * layout.item_space + border->left + + border->right; + q.height = client->frame.scr->tscreen->height; + + device_menu = pbt_menu_create(client, "device", client->frame.scr, NULL, + &q, &layout); + + if (!device_menu) + goto fail_menu; + + //FIXME: move to accessors + device_menu->background_color = 0x80000000; + device_menu->border = *border; + + /* Setup system item */ + + system_item = setup_system_item(device_menu, client); + + if (!system_item) + goto fail_system_item; + + device_menu->n_items++; + + /* Set system item as default */ + + pbt_menu_set_selected(device_menu, system_item); + pbt_menu_set_focus(device_menu, 1); + pbt_menu_show(device_menu, 1); + + pbt_menu_redraw(device_menu); + + return device_menu; + +fail_system_item: + // FIXME: need cleanup +fail_menu: + assert(0); + return NULL; +} + +static int kexec_cb(__attribute__((unused)) struct pbt_client *client, struct pb_opt_data *opt_data) +{ + int result; + + assert(opt_data); + + pb_log("%s: %s\n", __func__, opt_data->name); + + result = pb_run_kexec(opt_data->kd); + + return result; +} + +static int run(struct pbt_client *client) +{ + while (1) { + int result = waiter_poll(); + + if (result < 0 && errno != EINTR) { + pb_log("%s: poll: %s\n", __func__, strerror(errno)); + break; + } + + if (client->signal_data.abort) + break; + + ui_timer_process_sig(&client->signal_data.timer); + + while (client->signal_data.resize) { + client->signal_data.resize = 0; + pbt_client_resize(client); + } + } + + return 0; +} + +static struct pb_signal_data *_signal_data; + +static void set_signal_data(struct pb_signal_data *sd) +{ + _signal_data = sd; +} + +static struct pb_signal_data *get_signal_data(void) +{ + return _signal_data; +} + +static void sig_handler(int signum) +{ + DBGS("%d\n", signum); + + struct pb_signal_data *sd = get_signal_data(); + + if (!sd) + return; + + switch (signum) { + case SIGALRM: + ui_timer_sigalrm(&sd->timer); + break; + case SIGWINCH: + sd->resize = 1; + break; + default: + assert(0 && "unknown sig"); + /* fall through */ + case SIGINT: + case SIGHUP: + case SIGTERM: + sd->abort = 1; + break; + } +} + +/** + * main - twin bootloader main routine. + */ + +int main(int argc, char *argv[]) +{ + static struct sigaction sa; + static struct pbt_opts opts; + int result; + int ui_result; + FILE *log; + struct pbt_client *client; + + result = pbt_opts_parse(&opts, argc, argv); + + if (result) { + pbt_print_usage(); + return EXIT_FAILURE; + } + + if (opts.show_help == pbt_opt_yes) { + pbt_print_usage(); + return EXIT_SUCCESS; + } + + if (opts.show_version == pbt_opt_yes) { + pbt_print_version(); + return EXIT_SUCCESS; + } + + log = fopen(opts.log_file, "a"); + assert(log); + pb_log_set_stream(log); + +#if defined(DEBUG) + pb_log_always_flush(1); +#endif + + pb_log("--- pb-twin ---\n"); + + sa.sa_handler = sig_handler; + result = sigaction(SIGALRM, &sa, NULL); + result += sigaction(SIGHUP, &sa, NULL); + result += sigaction(SIGINT, &sa, NULL); + result += sigaction(SIGTERM, &sa, NULL); + result += sigaction(SIGWINCH, &sa, NULL); + + if (result) { + pb_log("%s sigaction failed.\n", __func__); + return EXIT_FAILURE; + } + + client = pbt_client_init(opts.backend, 900, 300, kexec_cb); + + if (!client) { + ui_result = EXIT_FAILURE; + goto done; + } + + set_signal_data(&client->signal_data); + + client->frame.top_menu = menu_create(client); + + if (!client->frame.top_menu) { + ui_result = EXIT_FAILURE; + goto done; + } + + twin_screen_update(client->frame.scr->tscreen); + ui_result = run(client); + +done: + talloc_free(client); + + pb_log("--- end ---\n"); + + return ui_result ? EXIT_FAILURE : EXIT_SUCCESS; +} |