From bd0c684c9941c24ee4191f4550ec966d5b1fa8ab Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Wed, 15 Feb 2012 11:33:41 -0800 Subject: [PATCH 1/1] Update twin ui to use discover server Signed-off-by: Geoff Levand --- ui/twin/Makefile.am | 20 +- ui/twin/main-generic.c | 355 ++++++++++++ ui/twin/main-ps3.c | 494 +++++++++++++++++ ui/twin/pb-twin.c | 1194 ---------------------------------------- ui/twin/pbt-client.c | 323 +++++++++++ ui/twin/pbt-client.h | 59 ++ ui/twin/pbt-main.c | 101 ++++ ui/twin/pbt-main.h | 47 ++ ui/twin/pbt-menu.c | 515 +++++++++++++++++ ui/twin/pbt-menu.h | 167 ++++++ ui/twin/pbt-scr.c | 459 +++++++++++++++ ui/twin/pbt-scr.h | 174 ++++++ 12 files changed, 2708 insertions(+), 1200 deletions(-) create mode 100644 ui/twin/main-generic.c create mode 100644 ui/twin/main-ps3.c delete mode 100644 ui/twin/pb-twin.c create mode 100644 ui/twin/pbt-client.c create mode 100644 ui/twin/pbt-client.h create mode 100644 ui/twin/pbt-main.c create mode 100644 ui/twin/pbt-main.h create mode 100644 ui/twin/pbt-menu.c create mode 100644 ui/twin/pbt-menu.h create mode 100644 ui/twin/pbt-scr.c create mode 100644 ui/twin/pbt-scr.h diff --git a/ui/twin/Makefile.am b/ui/twin/Makefile.am index 605e05e..9e8f5de 100644 --- a/ui/twin/Makefile.am +++ b/ui/twin/Makefile.am @@ -34,19 +34,27 @@ common_libs = \ noinst_LTLIBRARIES = libpbt.la -libpbt_la_SOURCES = - -bin_PROGRAMS = pb-twin-generic +libpbt_la_SOURCES = \ + pbt-client.c \ + pbt-client.h \ + pbt-main.c \ + pbt-main.h \ + pbt-menu.c \ + pbt-menu.h \ + pbt-scr.c \ + pbt-scr.h + +bin_PROGRAMS = pb-twin if ENABLE_PS3 bin_PROGRAMS += pb-twin-ps3 endif -pb_twin_generic_SOURCES = -pb_twin_generic_LDADD = $(common_libs) +pb_twin_SOURCES = main-generic.c +pb_twin_LDADD = $(common_libs) -pb_twin_ps3_SOURCES = +pb_twin_ps3_SOURCES = main-ps3.c pb_twin_ps3_LDADD = $(common_libs) pb_twin_ps3_LDFLAGS = -lps3-utils 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 + * + * 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 +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/ui/twin/main-ps3.c b/ui/twin/main-ps3.c new file mode 100644 index 0000000..db0539a --- /dev/null +++ b/ui/twin/main-ps3.c @@ -0,0 +1,494 @@ +/* + * Petitboot twin bootloader for the PS3 game console + * + * Copyright Geoff Levand + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "log/log.h" +#include "talloc/talloc.h" +#include "waiter/waiter.h" +#include "ui/common/timer.h" +#include "ui/common/ps3.h" + +#include "pbt-client.h" +#include "pbt-main.h" + +/* control to keyboard mappings for the sixaxis controller */ + +static const uint8_t ps3_sixaxis_map[] = { + 0, /* 0 Select */ + 0, /* 1 L3 */ + 0, /* 2 R3 */ + 0, /* 3 Start */ + KEY_UP, /* 4 Dpad Up */ + KEY_RIGHT, /* 5 Dpad Right */ + KEY_DOWN, /* 6 Dpad Down */ + KEY_LEFT, /* 7 Dpad Left */ + 0, /* 8 L2 */ + 0, /* 9 R2 */ + 0, /* 10 L1 */ + 0, /* 11 R1 */ + 0, /* 12 Triangle */ + KEY_ENTER, /* 13 Circle */ + 0, /* 14 Cross */ + KEY_DELETE, /* 15 Square */ + 0, /* 16 PS Button */ + 0, /* 17 nothing */ + 0, /* 18 nothing */ +}; + +static struct pbt_item *ps3_setup_system_item(struct pbt_menu *menu, + const struct pbt_menu_layout *layout) +{ +#if 0 + struct _twin_rect r; + struct pbt_item *main_item; + struct pbt_item *sub_item; + twin_pixmap_t *icon; + + icon = pbt_icon_load(NULL); + + /* Main item */ + + pbt_view_get_item_rect(menu->main, layout, icon, 0, &r); + main_item = pbt_item_create(menu->main, menu->main, &r); + + if (!main_item) + goto fail; + + main_item->image = icon; + + list_add_tail(menu->main->items_list, &main_item->list); + + /* Sub items */ + + main_item->sub_items_list = talloc(main_item, struct list); + list_init(main_item->sub_items_list); + + pbt_view_get_item_rect(menu->sub, layout, icon, 0, &r); + sub_item = pbt_item_create(main_item, menu->sub, &r); + + if (!sub_item) + goto fail; + + sub_item->image = pbt_item_create_text_image(layout, pbt_rect_width(&r), + pbt_rect_height(&r), icon, "Boot GameOS", + "Reboot the PS3 into the GameOS"); + + list_add_tail(main_item->sub_items_list, &sub_item->list); + + pbt_view_get_item_rect(menu->sub, layout, icon, 1, &r); + sub_item = pbt_item_create(main_item, menu->sub, &r); + + if (!sub_item) + goto fail; + + sub_item->image = pbt_item_create_text_image(layout, pbt_rect_width(&r), + pbt_rect_height(&r), icon, "Set Video Mode", + "Set the current video mode"); + + list_add_tail(main_item->sub_items_list, &sub_item->list); + + pbt_view_get_item_rect(menu->sub, layout, icon, 2, &r); + sub_item = pbt_item_create(main_item, menu->sub, &r); + + if (!sub_item) + goto fail; + + sub_item->image = pbt_item_create_text_image(layout, pbt_rect_width(&r), + pbt_rect_height(&r), icon, "Exit to Shell", + "Exit to a system shell prompt"); + + list_add_tail(main_item->sub_items_list, &sub_item->list); + + menu->sub->selected = sub_item; + menu->sub->items_list = main_item->sub_items_list; + + return main_item; +fail: +#endif + // FIXME: need cleanup + assert(0); + return NULL; +} + +static int ps3_setup_test_item(struct pbt_menu *menu, + const struct pbt_menu_layout *layout) +{ +#if 0 + struct _twin_rect r; + struct pbt_item *main_item; + struct pbt_item *sub_item; + twin_pixmap_t *icon; + + icon = pbt_icon_load(PB_ARTWORK_PATH "/hdd.png"); + + /* Main item */ + + pbt_view_get_item_rect(menu->main, layout, icon, 1, &r); + main_item = pbt_item_create(menu->main, menu->main, &r); + + if (!main_item) + goto fail; + + main_item->image = icon; + + list_add_tail(menu->main->items_list, &main_item->list); + + /* Sub items */ + + main_item->sub_items_list = talloc(main_item, struct list); + list_init(main_item->sub_items_list); + + pbt_view_get_item_rect(menu->sub, layout, icon, 0, &r); + sub_item = pbt_item_create(main_item, menu->sub, &r); + + if (!sub_item) + goto fail; + + sub_item->active = 0; + sub_item->image = pbt_item_create_text_image(layout, pbt_rect_width(&r), + pbt_rect_height(&r), icon, "test 1", + "text for test 1"); + + list_add_tail(main_item->sub_items_list, &sub_item->list); + + pbt_view_get_item_rect(menu->sub, layout, icon, 1, &r); + sub_item = pbt_item_create(main_item, menu->sub, &r); + + if (!sub_item) + goto fail; + + sub_item->active = 0; + sub_item->image = pbt_item_create_text_image(layout, pbt_rect_width(&r), + pbt_rect_height(&r), icon, "test 2", + "text for test 2"); + + list_add_tail(main_item->sub_items_list, &sub_item->list); + + menu->sub->selected = sub_item; + menu->sub->items_list = main_item->sub_items_list; + + return 0; + +fail: +#endif + // FIXME: need cleanup + assert(0); + return -1; +} + +static struct pbt_menu *ps3_menu_create(void *ctx, struct pbt_scr *scr) +{ +#if 0 + struct pbt_menu *menu; + struct _twin_rect r; + twin_pixmap_t *icon; + const struct pbt_border *border; + static const struct pbt_menu_layout layout = { + .item_space = 10, + .icon_space = 6, + .title_font_size = 30, + .title_font_color = 0xff000000, + .text_font_size = 18, + .text_font_color = 0xff400000, + }; + + assert(scr && scr->sig == pbt_scr_sig); + + menu = talloc_zero(ctx, struct pbt_menu); + + if (!menu) + return NULL; + + menu->sig = pbt_menu_sig; + menu->scr = scr; + + icon = pbt_icon_load(NULL); + + if (!icon) + return NULL; + + /* Setup main view */ + + border = &pbt_right_border; + r.left = 0; + r.top = 0; + r.right = icon->width + 2 * layout.item_space + 2 * layout.icon_space + + border->left + border->right; + r.bottom = menu->scr->tscreen->height; + + menu->main = pbt_view_create(menu, &r); + + if (!menu->main) + goto fail_main; + + menu->main->background_color = 0x80000000; + menu->main->border = *border; + menu->main->items_list = talloc(menu->main, struct list); + list_init(menu->main->items_list); + + /* Setup sub view */ + + r.left = r.right; + r.top = 0; + r.right = menu->scr->tscreen->width; + r.bottom = menu->scr->tscreen->height; + + menu->sub = pbt_view_create(menu, &r); + + if (!menu->sub) + goto fail_sub; + + menu->sub->background_color = 0x40000000; + + /* Setup system item */ + + menu->main->selected = ps3_setup_system_item(menu, &layout); + + if (!menu->main->selected) + goto fail_main_item; + + //ps3_setup_test_item(menu, &layout); + + return menu; + +fail_main_item: + // FIXME: need cleanup +fail_sub: + // FIXME: need cleanup +fail_main: + talloc_free(menu); +#endif + assert(0); + return NULL; +} + +/** + * struct ps3_gui - Main gui program instance. + */ + + +struct ps3_gui { + struct ui_timer timer; + struct ps3_flash_values values; + int dirty_values; + + struct pbt_scr scr; + struct pbt_frame frame; + struct pbt_menu *menu; +}; + +static struct ps3_gui ps3; + +static struct pbt_scr *ps3_scr_from_tscreen(twin_screen_t *tscreen) +{ + assert(ps3.scr.tscreen == tscreen); + return &ps3.scr; +} + +static twin_bool_t ps3_scr_event_cb(twin_screen_t *tscreen, twin_event_t *event) +{ + struct pbt_scr *scr = ps3_scr_from_tscreen(tscreen); + + pbt_dump_event("scr", NULL, event); + + switch(event->kind) { + case TwinEventJoyButton: + /* convert joystick events to key events */ + // FIXME: need to test + if (event->u.js.control < sizeof(ps3_sixaxis_map)) { + event->u.key.key = ps3_sixaxis_map[event->u.js.control]; + event->kind = event->u.js.value ? TwinEventKeyUp + : TwinEventKeyDown; + } + break; + default: + break; + } + return TWIN_FALSE; +} + +static void sig_handler(int signum) +{ + DBGS("%d\n", signum); + + switch (signum) { + case SIGALRM: + ui_timer_sigalrm(&ps3.timer); + break; + case SIGWINCH: +// if (ps3.gui) +// gui_resize(ps3.gui); + break; + default: + assert(0 && "unknown sig"); + /* fall through */ + case SIGINT: + case SIGHUP: + case SIGTERM: + exit(EXIT_FAILURE); +// if (ps3.gui) +// gui_abort(ps3.gui); + 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 = -1; + unsigned int mode; + FILE *log; + + 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; + } + + ps3.values = ps3_flash_defaults; + + if (opts.reset_defaults != pbt_opt_yes) + ps3.dirty_values = ps3_flash_get_values(&ps3.values); + +#if 0 + twin_feature_init(); // need it??? + + /* Setup screen. */ + +#if defined(HAVE_LIBTWIN_TWIN_X11_H) +# if defined(USE_X11) + if (1) { +# else + if (0) { +# endif + //ps3.scr.x11 = twin_x11_create(XOpenDisplay(0), 1024, 768); + ps3.scr.x11 = twin_x11_create(XOpenDisplay(0), 512, 384); + + if (!ps3.scr.x11) { + perror("failed to create x11 screen !\n"); + return EXIT_FAILURE; + } + + ps3.scr.tscreen = ps3.scr.x11->screen; + } else { +#else + if (1) { +#endif + result = ps3_get_video_mode(&mode); + + /* Current becomes default if ps3_flash_get_values() failed. */ + + if (ps3.dirty_values && !result) + ps3.values.video_mode = mode; + + /* Set mode if not at default. */ + + if (!result && (ps3.values.video_mode != (uint16_t)mode)) + ps3_set_video_mode(ps3.values.video_mode); + + ps3.scr.fbdev = twin_fbdev_create(-1, SIGUSR1); + + if (!ps3.scr.fbdev) { + perror("failed to create fbdev screen !\n"); + return EXIT_FAILURE; + } + + ps3.scr.tscreen = ps3.scr.fbdev->screen; + } + + ps3.scr.tscreen->event_filter = pbt_scr_event; + + twin_screen_set_background(ps3.scr.tscreen, + pbt_background_load(ps3.scr.tscreen, NULL)); + + /* setup menu */ + + ps3.menu = ps3_menu_create(NULL, &ps3.scr); + + if (!ps3.menu) + return EXIT_FAILURE; + + /* Console switch */ + + if (ps3.scr.fbdev) + twin_fbdev_activate(ps3.scr.fbdev); + + /* run twin */ + + // need to hookup pb waiters to twin... + twin_dispatch(); + +#endif + + pb_log("--- end ---\n"); + + return ui_result ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/ui/twin/pb-twin.c b/ui/twin/pb-twin.c deleted file mode 100644 index a5c0a27..0000000 --- a/ui/twin/pb-twin.c +++ /dev/null @@ -1,1194 +0,0 @@ - -#define _GNU_SOURCE - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#undef _USE_X11 - -#include -#include -#include -#include -#include - -#include "petitboot.h" -#include "petitboot-paths.h" - -#ifdef _USE_X11 -#include -static twin_x11_t *pboot_x11; -#else -#include -static twin_fbdev_t *pboot_fbdev; -#endif - -static twin_screen_t *pboot_screen; - -#define PBOOT_INITIAL_MESSAGE \ - "keys: 0=safe 1=720p 2=1080i 3=1080p del=GameOS" - -#define PBOOT_LEFT_PANE_SIZE 160 -#define PBOOT_LEFT_PANE_COLOR 0x80000000 -#define PBOOT_LEFT_LINE_COLOR 0xff000000 - -#define PBOOT_LEFT_FOCUS_WIDTH 80 -#define PBOOT_LEFT_FOCUS_HEIGHT 80 -#define PBOOT_LEFT_FOCUS_XOFF 40 -#define PBOOT_LEFT_FOCUS_YOFF 40 -#define PBOOT_LEFT_FOCUS_XRAD (6 * TWIN_FIXED_ONE) -#define PBOOT_LEFT_FOCUS_YRAD (6 * TWIN_FIXED_ONE) - -#define PBOOT_RIGHT_FOCUS_XOFF 20 -#define PBOOT_RIGHT_FOCUS_YOFF 60 -#define PBOOT_RIGHT_FOCUS_HEIGHT 80 -#define PBOOT_RIGHT_FOCUS_XRAD (6 * TWIN_FIXED_ONE) -#define PBOOT_RIGHT_FOCUS_YRAD (6 * TWIN_FIXED_ONE) - -#define PBOOT_LEFT_ICON_WIDTH 64 -#define PBOOT_LEFT_ICON_HEIGHT 64 -#define PBOOT_LEFT_ICON_XOFF 50 -#define PBOOT_LEFT_ICON_YOFF 50 -#define PBOOT_LEFT_ICON_STRIDE 100 - -#define PBOOT_RIGHT_OPTION_LMARGIN 30 -#define PBOOT_RIGHT_OPTION_RMARGIN 30 -#define PBOOT_RIGHT_OPTION_TMARGIN 70 -#define PBOOT_RIGHT_OPTION_HEIGHT 64 -#define PBOOT_RIGHT_OPTION_STRIDE 100 -#define PBOOT_RIGHT_TITLE_TEXT_SIZE (30 * TWIN_FIXED_ONE) -#define PBOOT_RIGHT_SUBTITLE_TEXT_SIZE (18 * TWIN_FIXED_ONE) -#define PBOOT_RIGHT_TITLE_XOFFSET 80 -#define PBOOT_RIGHT_TITLE_YOFFSET 30 -#define PBOOT_RIGHT_SUBTITLE_XOFFSET 100 -#define PBOOT_RIGHT_SUBTITLE_YOFFSET 50 -#define PBOOT_RIGHT_BADGE_XOFFSET 2 -#define PBOOT_RIGHT_BADGE_YOFFSET 0 - - -#define PBOOT_RIGHT_TITLE_COLOR 0xff000000 -#define PBOOT_RIGHT_SUBTITLE_COLOR 0xff400000 - -#define PBOOT_FOCUS_COLOR 0x10404040 - -#define PBOOT_STATUS_PANE_COLOR 0x60606060 -#define PBOOT_STATUS_PANE_HEIGHT 20 -#define PBOOT_STATUS_PANE_XYMARGIN 20 -#define PBOOT_STATUS_TEXT_MARGIN 10 -#define PBOOT_STATUS_TEXT_SIZE (16 * TWIN_FIXED_ONE) -#define PBOOT_STATUS_TEXT_COLOR 0xff000000 - -typedef struct _pboot_option pboot_option_t; -typedef struct _pboot_device pboot_device_t; - -struct _pboot_option -{ - char *title; - char *subtitle; - twin_pixmap_t *badge; - twin_pixmap_t *cache; - twin_rect_t box; - void *data; -}; - -struct _pboot_device -{ - char *id; - twin_pixmap_t *badge; - twin_rect_t box; - int option_count; - pboot_option_t options[PBOOT_MAX_OPTION]; -}; - -static twin_pixmap_t *pboot_cursor; -static int pboot_cursor_hx; -static int pboot_cursor_hy; - -static pboot_device_t *pboot_devices[PBOOT_MAX_DEV]; -static int pboot_dev_count; -static int pboot_dev_sel = -1; -static int pboot_focus_lpane = 1; - -typedef struct _pboot_lpane { - twin_window_t *window; - twin_rect_t focus_box; - int focus_start; - int focus_target; - int focus_curindex; - int mouse_target; -} pboot_lpane_t; - -typedef struct _pboot_rpane { - twin_window_t *window; - twin_rect_t focus_box; - int focus_start; - int focus_target; - int focus_curindex; - int mouse_target; -} pboot_rpane_t; - -typedef struct _pboot_spane { - twin_window_t *window; - char *text; -} pboot_spane_t; - -static pboot_lpane_t *pboot_lpane; -static pboot_rpane_t *pboot_rpane; -static pboot_spane_t *pboot_spane; - -/* control to keyboard mappings for the sixaxis controller */ -uint8_t sixaxis_map[] = { - 0, /* 0 Select */ - 0, /* 1 L3 */ - 0, /* 2 R3 */ - 0, /* 3 Start */ - KEY_UP, /* 4 Dpad Up */ - KEY_RIGHT, /* 5 Dpad Right */ - KEY_DOWN, /* 6 Dpad Down */ - KEY_LEFT, /* 7 Dpad Left */ - 0, /* 8 L2 */ - 0, /* 9 R2 */ - 0, /* 10 L1 */ - 0, /* 11 R1 */ - 0, /* 12 Triangle */ - KEY_ENTER, /* 13 Circle */ - 0, /* 14 Cross */ - KEY_DELETE, /* 15 Square */ - 0, /* 16 PS Button */ - 0, /* 17 nothing */ - 0, /* 18 nothing */ -}; - - -static int pboot_vmode_change = -1; - -/* XXX move to twin */ -static inline twin_bool_t twin_rect_intersect(twin_rect_t r1, - twin_rect_t r2) -{ - return !(r1.left > r2.right || - r1.right < r2.left || - r1.top > r2.bottom || - r1.bottom < r2.top); -} - -static void pboot_draw_option_cache(pboot_device_t *dev, pboot_option_t *opt, - int index) -{ - twin_pixmap_t *px; - twin_path_t *path; - twin_fixed_t tx, ty; - - /* Create pixmap */ - px = twin_pixmap_create(TWIN_ARGB32, opt->box.right - opt->box.left, - opt->box.bottom - opt->box.top); - assert(px); - opt->cache = px; - - /* Fill background */ - twin_fill(px, 0x00000000, TWIN_SOURCE, 0, 0, px->width, px->height); - - /* Allocate a path for drawing */ - path = twin_path_create(); - assert(path); - -#if 0 - /* TEST - Bounding rectangle */ - twin_path_rectangle(path, 0, 0, - twin_int_to_fixed(px->width), - twin_int_to_fixed(px->height)); - twin_paint_path(px, PBOOT_RIGHT_TITLE_COLOR, path); - twin_path_empty(path); - twin_fill(px, 0x00000000, TWIN_SOURCE, 2, 2, - px->width - 3, px->height - 3); -#endif - - /* Draw texts */ - twin_path_set_font_size(path, PBOOT_RIGHT_TITLE_TEXT_SIZE); - twin_path_set_font_style(path, TWIN_TEXT_UNHINTED); - tx = twin_int_to_fixed(PBOOT_RIGHT_TITLE_XOFFSET); - ty = twin_int_to_fixed(PBOOT_RIGHT_TITLE_YOFFSET); - twin_path_move (path, tx, ty); - twin_path_utf8 (path, opt->title); - twin_paint_path (px, PBOOT_RIGHT_TITLE_COLOR, path); - twin_path_empty (path); - - if (opt->subtitle) { - twin_path_set_font_size(path, PBOOT_RIGHT_SUBTITLE_TEXT_SIZE); - twin_path_set_font_style(path, TWIN_TEXT_UNHINTED); - tx = twin_int_to_fixed(PBOOT_RIGHT_SUBTITLE_XOFFSET); - ty = twin_int_to_fixed(PBOOT_RIGHT_SUBTITLE_YOFFSET); - twin_path_move (path, tx, ty); - twin_path_utf8 (path, opt->subtitle); - twin_paint_path (px, PBOOT_RIGHT_SUBTITLE_COLOR, path); - twin_path_empty (path); - } - - if (opt->badge) { - twin_operand_t src; - - src.source_kind = TWIN_PIXMAP; - src.u.pixmap = opt->badge; - - twin_composite(px, PBOOT_RIGHT_BADGE_XOFFSET, - PBOOT_RIGHT_BADGE_YOFFSET, - &src, 0, 0, NULL, 0, 0, TWIN_OVER, - opt->badge->width, opt->badge->height); - } - - - /* Destroy path */ - twin_path_destroy(path); -} - -static void pboot_rpane_draw(twin_window_t *window) -{ - twin_pixmap_t *px = window->pixmap; - pboot_rpane_t *rpane = window->client_data; - pboot_device_t *dev; - twin_path_t *path; - twin_fixed_t x, y, w, h; - int i; - - /* Fill background */ - twin_fill(px, 0x00000000, TWIN_SOURCE, 0, 0, px->width, px->height); - - /* Nothing to draw, return */ - if (pboot_dev_sel < 0) - return; - - /* Create a path for use later */ - path = twin_path_create(); - assert(path); - - /* Draw focus box */ - if (rpane->focus_curindex >= 0 && - twin_rect_intersect(rpane->focus_box, px->clip)) { - x = twin_int_to_fixed(rpane->focus_box.left + 2); - y = twin_int_to_fixed(rpane->focus_box.top + 2); - w = twin_int_to_fixed(rpane->focus_box.right - - rpane->focus_box.left - 4); - h = twin_int_to_fixed(rpane->focus_box.bottom - - rpane->focus_box.top - 4); - twin_path_rounded_rectangle(path, x, y, w, h, - PBOOT_RIGHT_FOCUS_XRAD, - PBOOT_RIGHT_FOCUS_YRAD); - if (!pboot_focus_lpane) - twin_paint_path(px, PBOOT_FOCUS_COLOR, path); - else - twin_paint_stroke(px, PBOOT_FOCUS_COLOR, path, - 4 * TWIN_FIXED_ONE); - } - - /* Get device and iterate through options */ - dev = pboot_devices[pboot_dev_sel]; - for (i = 0; i < dev->option_count; i++) { - pboot_option_t *opt = &dev->options[i]; - twin_operand_t src; - - if (opt->title == NULL) - continue; - if (!twin_rect_intersect(opt->box, px->clip)) - continue; - if (opt->cache == NULL) - pboot_draw_option_cache(dev, opt, i); - - src.source_kind = TWIN_PIXMAP; - src.u.pixmap = opt->cache; - - twin_composite(px, opt->box.left, opt->box.top, - &src, 0, 0, NULL, 0, 0, TWIN_OVER, - opt->box.right - opt->box.left, - opt->box.bottom - opt->box.top); - } - - /* Destroy path */ - twin_path_destroy(path); -} - -static twin_time_t pboot_rfocus_timeout (twin_time_t now, void *closure) -{ - int dir = 1, dist, pos; - const int accel[11] = { 7, 4, 2, 1, 1, 1, 1, 1, 2, 2, 3 }; - - dist = abs(pboot_rpane->focus_target - pboot_rpane->focus_start); - dir = dist > 5 ? 5 : dist; - pos = pboot_rpane->focus_target - (int)pboot_rpane->focus_box.top; - if (pos == 0) { - return -1; - } - if (pos < 0) { - dir = -dir; - pos = -pos; - } - twin_window_damage(pboot_rpane->window, - pboot_rpane->focus_box.left, - pboot_rpane->focus_box.top, - pboot_rpane->focus_box.right, - pboot_rpane->focus_box.bottom); - - pboot_rpane->focus_box.top += dir; - pboot_rpane->focus_box.bottom += dir; - - twin_window_damage(pboot_rpane->window, - pboot_rpane->focus_box.left, - pboot_rpane->focus_box.top, - pboot_rpane->focus_box.right, - pboot_rpane->focus_box.bottom); - - twin_window_queue_paint(pboot_rpane->window); - - return accel[(pos * 10) / dist]; -} - -static void pboot_set_rfocus(int index) -{ - pboot_device_t *dev; - - if (pboot_dev_sel < 0 || pboot_dev_sel >= pboot_dev_count) - return; - dev = pboot_devices[pboot_dev_sel]; - if (index < 0 || index >= dev->option_count) - return; - - pboot_rpane->focus_start = pboot_rpane->focus_box.top; - pboot_rpane->focus_target = PBOOT_RIGHT_FOCUS_YOFF + - PBOOT_RIGHT_OPTION_STRIDE * index; - pboot_rpane->focus_curindex = index; - - twin_set_timeout(pboot_rfocus_timeout, 0, NULL); -} - -static void pboot_select_rpane(void) -{ - if (pboot_focus_lpane == 0) - return; - pboot_focus_lpane = 0; - - twin_screen_set_active(pboot_screen, pboot_rpane->window->pixmap); - - twin_window_damage(pboot_lpane->window, - pboot_lpane->focus_box.left, - pboot_lpane->focus_box.top, - pboot_lpane->focus_box.right, - pboot_lpane->focus_box.bottom); - - twin_window_damage(pboot_rpane->window, - pboot_rpane->focus_box.left, - pboot_rpane->focus_box.top, - pboot_rpane->focus_box.right, - pboot_rpane->focus_box.bottom); - - twin_window_queue_paint(pboot_lpane->window); - twin_window_queue_paint(pboot_rpane->window); - - pboot_set_rfocus(0); -} - -static void pboot_select_lpane(void) -{ - if (pboot_focus_lpane == 1) - return; - pboot_focus_lpane = 1; - - twin_screen_set_active(pboot_screen, pboot_lpane->window->pixmap); - - twin_window_damage(pboot_lpane->window, - pboot_lpane->focus_box.left, - pboot_lpane->focus_box.top, - pboot_lpane->focus_box.right, - pboot_lpane->focus_box.bottom); - - twin_window_damage(pboot_rpane->window, - pboot_rpane->focus_box.left, - pboot_rpane->focus_box.top, - pboot_rpane->focus_box.right, - pboot_rpane->focus_box.bottom); - - twin_window_queue_paint(pboot_lpane->window); - twin_window_queue_paint(pboot_rpane->window); -} - -static void pboot_rpane_mousetrack(twin_coord_t x, twin_coord_t y) -{ - pboot_device_t *dev; - pboot_option_t *opt; - int candidate = -1; - - if (pboot_dev_sel < 0 || pboot_dev_sel >= pboot_dev_count) - return; - dev = pboot_devices[pboot_dev_sel]; - - if (y < PBOOT_RIGHT_OPTION_TMARGIN) - goto miss; - candidate = (y - PBOOT_RIGHT_OPTION_TMARGIN) / - PBOOT_RIGHT_OPTION_STRIDE; - if (candidate >= dev->option_count) { - candidate = -1; - goto miss; - } - if (candidate == pboot_rpane->mouse_target) - return; - opt = &dev->options[candidate]; - if (x < opt->box.left || x > opt->box.right || - y < opt->box.top || y > opt->box.bottom) { - candidate = -1; - goto miss; - } - - /* Ok, so now, we know the mouse hit an icon that wasn't the same - * as the previous one, we trigger a focus change - */ - pboot_set_rfocus(candidate); - - miss: - pboot_rpane->mouse_target = candidate; -} - -static void pboot_choose_option(void) -{ - pboot_device_t *dev = pboot_devices[pboot_dev_sel]; - pboot_option_t *opt = &dev->options[pboot_rpane->focus_curindex]; - - LOG("Selected device %s\n", opt->title); - pboot_message("booting %s...", opt->title); - - /* Give user feedback, make sure errors and panics will be seen */ - pboot_exec_option(opt->data); -} - -static twin_bool_t pboot_rpane_event (twin_window_t *window, - twin_event_t *event) -{ - /* filter out all mouse events */ - switch(event->kind) { - case TwinEventEnter: - case TwinEventMotion: - case TwinEventLeave: - pboot_select_rpane(); - pboot_rpane_mousetrack(event->u.pointer.x, event->u.pointer.y); - return TWIN_TRUE; - case TwinEventButtonDown: - pboot_select_rpane(); - pboot_rpane_mousetrack(event->u.pointer.x, event->u.pointer.y); - pboot_choose_option(); - case TwinEventButtonUp: - return TWIN_TRUE; - case TwinEventKeyDown: - switch(event->u.key.key) { - case KEY_UP: - pboot_set_rfocus(pboot_rpane->focus_curindex - 1); - return TWIN_TRUE; - case KEY_DOWN: - pboot_set_rfocus(pboot_rpane->focus_curindex + 1); - return TWIN_TRUE; - case KEY_LEFT: - pboot_select_lpane(); - return TWIN_TRUE; - case KEY_ENTER: - pboot_choose_option(); - default: - break; - } - break; - default: - break; - } - return TWIN_FALSE; -} - - -int pboot_add_option(int devindex, const char *title, - const char *subtitle, twin_pixmap_t *badge, void *data) -{ - pboot_device_t *dev; - pboot_option_t *opt; - twin_coord_t width; - int index; - - if (devindex < 0 || devindex >= pboot_dev_count) - return -1; - dev = pboot_devices[devindex]; - - if (dev->option_count >= PBOOT_MAX_OPTION) - return -1; - index = dev->option_count++; - opt = &dev->options[index]; - - opt->title = malloc(strlen(title) + 1); - strcpy(opt->title, title); - - if (subtitle) { - opt->subtitle = malloc(strlen(subtitle) + 1); - strcpy(opt->subtitle, subtitle); - } else - opt->subtitle = NULL; - - opt->badge = badge; - opt->cache = NULL; - - width = pboot_rpane->window->pixmap->width - - (PBOOT_RIGHT_OPTION_LMARGIN + PBOOT_RIGHT_OPTION_RMARGIN); - - opt->box.left = PBOOT_RIGHT_OPTION_LMARGIN; - opt->box.right = opt->box.left + width; - opt->box.top = PBOOT_RIGHT_OPTION_TMARGIN + - index * PBOOT_RIGHT_OPTION_STRIDE; - opt->box.bottom = opt->box.top + PBOOT_RIGHT_OPTION_HEIGHT; - - opt->data = data; - return index; -} - - -static void pboot_set_device_select(int sel, int force) -{ - LOG("%s: %d -> %d\n", __FUNCTION__, pboot_dev_sel, sel); - if (!force && sel == pboot_dev_sel) - return; - if (sel >= pboot_dev_count) - return; - pboot_dev_sel = sel; - if (force) { - pboot_lpane->focus_curindex = sel; - if (sel < 0) - pboot_lpane->focus_target = 0 - PBOOT_LEFT_FOCUS_HEIGHT; - else - pboot_lpane->focus_target = PBOOT_LEFT_FOCUS_YOFF + - PBOOT_LEFT_ICON_STRIDE * sel; - pboot_rpane->focus_box.bottom = pboot_lpane->focus_target; - pboot_rpane->focus_box.bottom = pboot_rpane->focus_box.top + - PBOOT_RIGHT_FOCUS_HEIGHT; - twin_window_damage(pboot_lpane->window, - 0, 0, - pboot_lpane->window->pixmap->width, - pboot_lpane->window->pixmap->height); - twin_window_queue_paint(pboot_lpane->window); - } - pboot_rpane->focus_curindex = -1; - pboot_rpane->mouse_target = -1; - pboot_rpane->focus_box.top = -2*PBOOT_RIGHT_FOCUS_HEIGHT; - pboot_rpane->focus_box.bottom = pboot_rpane->focus_box.top + - PBOOT_RIGHT_FOCUS_HEIGHT; - twin_window_damage(pboot_rpane->window, 0, 0, - pboot_rpane->window->pixmap->width, - pboot_rpane->window->pixmap->height); - twin_window_queue_paint(pboot_rpane->window); -} - -static void pboot_create_rpane(void) -{ - pboot_rpane = calloc(1, sizeof(pboot_rpane_t)); - assert(pboot_rpane); - - pboot_rpane->window = twin_window_create(pboot_screen, TWIN_ARGB32, - TwinWindowPlain, - PBOOT_LEFT_PANE_SIZE, 0, - pboot_screen->width - - PBOOT_LEFT_PANE_SIZE, - pboot_screen->height); - assert(pboot_rpane->window); - - pboot_rpane->window->draw = pboot_rpane_draw; - pboot_rpane->window->event = pboot_rpane_event; - pboot_rpane->window->client_data = pboot_rpane; - - pboot_rpane->focus_curindex = -1; - pboot_rpane->focus_box.left = PBOOT_RIGHT_FOCUS_XOFF; - pboot_rpane->focus_box.top = -2*PBOOT_RIGHT_FOCUS_HEIGHT; - pboot_rpane->focus_box.right = pboot_rpane->window->pixmap->width - - 2 * PBOOT_RIGHT_FOCUS_XOFF; - pboot_rpane->focus_box.bottom = pboot_rpane->focus_box.top + - PBOOT_RIGHT_FOCUS_HEIGHT; - pboot_rpane->mouse_target = -1; - twin_window_show(pboot_rpane->window); - twin_window_queue_paint(pboot_rpane->window); -} - - -static twin_time_t pboot_lfocus_timeout (twin_time_t now, void *closure) -{ - int dir = 1, dist, pos; - const int accel[11] = { 7, 4, 2, 1, 1, 1, 1, 1, 2, 2, 3 }; - - dist = abs(pboot_lpane->focus_target - pboot_lpane->focus_start); - dir = dist > 2 ? 2 : dist; - pos = pboot_lpane->focus_target - (int)pboot_lpane->focus_box.top; - if (pos == 0) { - pboot_set_device_select(pboot_lpane->focus_curindex, 0); - return -1; - } - if (pos < 0) { - dir = -1; - pos = -pos; - } - twin_window_damage(pboot_lpane->window, - pboot_lpane->focus_box.left, - pboot_lpane->focus_box.top, - pboot_lpane->focus_box.right, - pboot_lpane->focus_box.bottom); - - pboot_lpane->focus_box.top += dir; - pboot_lpane->focus_box.bottom += dir; - - twin_window_damage(pboot_lpane->window, - pboot_lpane->focus_box.left, - pboot_lpane->focus_box.top, - pboot_lpane->focus_box.right, - pboot_lpane->focus_box.bottom); - - twin_window_queue_paint(pboot_lpane->window); - - return accel[(pos * 10) / dist]; -} - -static void pboot_set_lfocus(int index) -{ - if (index >= pboot_dev_count) - return; - - pboot_lpane->focus_start = pboot_lpane->focus_box.top; - - if (index < 0) - pboot_lpane->focus_target = 0 - PBOOT_LEFT_FOCUS_HEIGHT; - else - pboot_lpane->focus_target = PBOOT_LEFT_FOCUS_YOFF + - PBOOT_LEFT_ICON_STRIDE * index; - - pboot_lpane->focus_curindex = index; - - twin_set_timeout(pboot_lfocus_timeout, 0, NULL); -} - -static void pboot_lpane_mousetrack(twin_coord_t x, twin_coord_t y) -{ - int candidate = -1; - twin_coord_t icon_top; - - if (x < PBOOT_LEFT_ICON_XOFF || - x > (PBOOT_LEFT_ICON_XOFF + PBOOT_LEFT_ICON_WIDTH)) - goto miss; - if (y < PBOOT_LEFT_ICON_YOFF) - goto miss; - candidate = (y - PBOOT_LEFT_ICON_YOFF) / PBOOT_LEFT_ICON_STRIDE; - if (candidate >= pboot_dev_count) { - candidate = -1; - goto miss; - } - if (candidate == pboot_lpane->mouse_target) - return; - icon_top = PBOOT_LEFT_ICON_YOFF + - candidate * PBOOT_LEFT_ICON_STRIDE; - if (y > (icon_top + PBOOT_LEFT_ICON_HEIGHT)) { - candidate = -1; - goto miss; - } - - /* Ok, so now, we know the mouse hit an icon that wasn't the same - * as the previous one, we trigger a focus change - */ - pboot_set_lfocus(candidate); - - miss: - pboot_lpane->mouse_target = candidate; -} - -static twin_bool_t pboot_lpane_event (twin_window_t *window, - twin_event_t *event) -{ - /* filter out all mouse events */ - switch(event->kind) { - case TwinEventEnter: - case TwinEventMotion: - case TwinEventLeave: - pboot_select_lpane(); - pboot_lpane_mousetrack(event->u.pointer.x, event->u.pointer.y); - return TWIN_TRUE; - case TwinEventButtonDown: - case TwinEventButtonUp: - return TWIN_TRUE; - case TwinEventKeyDown: - switch(event->u.key.key) { - case KEY_UP: - if (pboot_lpane->focus_curindex > 0) - pboot_set_lfocus( - pboot_lpane->focus_curindex - 1); - return TWIN_TRUE; - case KEY_DOWN: - pboot_set_lfocus(pboot_lpane->focus_curindex + 1); - return TWIN_TRUE; - case KEY_RIGHT: - pboot_select_rpane(); - return TWIN_TRUE; - default: - break; - } - break; - default: - break; - } - return TWIN_FALSE; -} - -static void pboot_quit(void) -{ - kill(0, SIGINT); -} - -twin_bool_t pboot_event_filter(twin_screen_t *screen, - twin_event_t *event) -{ - switch(event->kind) { - case TwinEventEnter: - case TwinEventMotion: - case TwinEventLeave: - case TwinEventButtonDown: - case TwinEventButtonUp: - if (pboot_cursor != NULL) - twin_screen_set_cursor(pboot_screen, pboot_cursor, - pboot_cursor_hx, - pboot_cursor_hy); - break; - case TwinEventJoyButton: - /* map joystick events into key events */ - if (event->u.js.control >= sizeof(sixaxis_map)) - break; - - event->u.key.key = sixaxis_map[event->u.js.control]; - if (event->u.js.value == 0) { - event->kind = TwinEventKeyUp; - break; - } else { - event->kind = TwinEventKeyDown; - } - - /* fall through.. */ - case TwinEventKeyDown: - switch(event->u.key.key) { - /* Gross hack for video modes, need something better ! */ - case KEY_0: - pboot_vmode_change = 0; /* auto */ - pboot_quit(); - return TWIN_TRUE; - case KEY_1: - pboot_vmode_change = 3; /* 720p */ - pboot_quit(); - return TWIN_TRUE; - case KEY_2: - pboot_vmode_change = 4; /* 1080i */ - pboot_quit(); - return TWIN_TRUE; - case KEY_3: - pboot_vmode_change = 5; /* 1080p */ - pboot_quit(); - return TWIN_TRUE; - - /* Another gross hack for booting back to gameos */ - case KEY_BACKSPACE: - case KEY_DELETE: - pboot_message("booting to GameOS..."); - system(BOOT_GAMEOS_BIN); - } - case TwinEventKeyUp: - twin_screen_set_cursor(pboot_screen, NULL, 0, 0); - break; - default: - break; - } - return TWIN_FALSE; -} - -static void pboot_lpane_draw(twin_window_t *window) -{ - twin_pixmap_t *px = window->pixmap; - pboot_lpane_t *lpane = window->client_data; - twin_path_t *path; - twin_fixed_t x, y, w, h; - int i; - - /* Fill background */ - twin_fill(px, PBOOT_LEFT_PANE_COLOR, TWIN_SOURCE, - 0, 0, px->width, px->height); - - /* Create a path for use later */ - path = twin_path_create(); - assert(path); - - /* Draw right line if needed */ - if (px->clip.right > (PBOOT_LEFT_PANE_SIZE - 4)) { - x = twin_int_to_fixed(PBOOT_LEFT_PANE_SIZE - 4); - y = twin_int_to_fixed(px->height); - twin_path_rectangle(path, x, 0, 0x40000, y); - twin_paint_path(px, PBOOT_LEFT_LINE_COLOR, path); - twin_path_empty(path); - } - - /* Draw focus box */ - if (lpane->focus_curindex >= 0 && - twin_rect_intersect(lpane->focus_box, px->clip)) { - x = twin_int_to_fixed(lpane->focus_box.left + 2); - y = twin_int_to_fixed(lpane->focus_box.top + 2); - w = twin_int_to_fixed(lpane->focus_box.right - - lpane->focus_box.left - 4); - h = twin_int_to_fixed(lpane->focus_box.bottom - - lpane->focus_box.top - 4); - twin_path_rounded_rectangle(path, x, y, w, h, - PBOOT_LEFT_FOCUS_XRAD, - PBOOT_LEFT_FOCUS_YRAD); - if (pboot_focus_lpane) - twin_paint_path(px, PBOOT_FOCUS_COLOR, path); - else - twin_paint_stroke(px, PBOOT_FOCUS_COLOR, path, - 4 * TWIN_FIXED_ONE); - } - - /* Draw icons */ - for (i = 0; i < pboot_dev_count; i++) { - pboot_device_t *dev = pboot_devices[i]; - twin_operand_t src; - - if (!twin_rect_intersect(dev->box, px->clip)) - continue; - - src.source_kind = TWIN_PIXMAP; - src.u.pixmap = dev->badge; - - twin_composite(px, dev->box.left, dev->box.top, - &src, 0, 0, NULL, 0, 0, TWIN_OVER, - dev->box.right - dev->box.left, - dev->box.bottom - dev->box.top); - - } - - /* Destroy path */ - twin_path_destroy(path); -} - -static void pboot_create_lpane(void) -{ - pboot_lpane = calloc(1, sizeof(pboot_lpane_t)); - assert(pboot_lpane); - - pboot_lpane->window = twin_window_create(pboot_screen, TWIN_ARGB32, - TwinWindowPlain, - 0, 0, PBOOT_LEFT_PANE_SIZE, - pboot_screen->height); - assert(pboot_lpane->window); - - pboot_lpane->window->draw = pboot_lpane_draw; - pboot_lpane->window->event = pboot_lpane_event; - pboot_lpane->window->client_data = pboot_lpane; - pboot_lpane->focus_curindex = -1; - pboot_lpane->focus_box.left = PBOOT_LEFT_FOCUS_XOFF; - pboot_lpane->focus_box.top = -2*PBOOT_LEFT_FOCUS_HEIGHT; - pboot_lpane->focus_box.right = pboot_lpane->focus_box.left + - PBOOT_LEFT_FOCUS_WIDTH; - pboot_lpane->focus_box.bottom = pboot_lpane->focus_box.top + - PBOOT_LEFT_FOCUS_HEIGHT; - pboot_lpane->mouse_target = -1; - twin_window_show(pboot_lpane->window); - twin_window_queue_paint(pboot_lpane->window); -} - -static void pboot_spane_draw(twin_window_t *window) -{ - twin_pixmap_t *px = window->pixmap; - pboot_spane_t *spane = window->client_data; - twin_path_t *path; - twin_fixed_t tx, ty; - - /* Fill background */ - twin_fill(px, PBOOT_STATUS_PANE_COLOR, TWIN_SOURCE, - 0, 0, px->width, px->height); - - path = twin_path_create(); - assert(path); - - twin_path_set_font_size(path, PBOOT_STATUS_TEXT_SIZE); - twin_path_set_font_style(path, TWIN_TEXT_UNHINTED); - tx = twin_int_to_fixed(PBOOT_STATUS_TEXT_MARGIN); - ty = twin_int_to_fixed(PBOOT_STATUS_PANE_HEIGHT - 2); - twin_path_move (path, tx, ty); - twin_path_utf8 (path, spane->text); - twin_paint_path (px, PBOOT_STATUS_TEXT_COLOR, path); - - twin_path_destroy(path); -} - -void pboot_message(const char *fmt, ...) -{ - va_list ap; - char *msg; - - if (pboot_spane->text) - free(pboot_spane->text); - - va_start(ap, fmt); - vasprintf(&msg, fmt, ap); - va_end(ap); - - pboot_spane->text = msg; - twin_window_damage(pboot_spane->window, - 0, 0, - pboot_spane->window->pixmap->width, - pboot_spane->window->pixmap->height); - twin_window_draw(pboot_spane->window); -} - -static void pboot_create_spane(void) -{ - pboot_spane = calloc(1, sizeof(pboot_spane_t)); - assert(pboot_spane); - - pboot_spane->window = twin_window_create(pboot_screen, TWIN_ARGB32, - TwinWindowPlain, - PBOOT_LEFT_PANE_SIZE + - PBOOT_STATUS_PANE_XYMARGIN, - pboot_screen->height - - PBOOT_STATUS_PANE_HEIGHT, - pboot_screen->width - - PBOOT_LEFT_PANE_SIZE - - 2*PBOOT_STATUS_PANE_XYMARGIN, - PBOOT_STATUS_PANE_HEIGHT); - assert(pboot_spane->window); - - pboot_spane->window->draw = pboot_spane_draw; - pboot_spane->window->client_data = pboot_spane; - pboot_spane->text = strdup(PBOOT_INITIAL_MESSAGE); - twin_window_show(pboot_spane->window); - twin_window_queue_paint(pboot_spane->window); -} - -int pboot_device_add(const char *dev_id, const char *name, - twin_pixmap_t *pixmap) -{ - int index; - pboot_device_t *dev; - - if (pboot_dev_count >= PBOOT_MAX_DEV) - return -1; - - index = pboot_dev_count++; - - dev = malloc(sizeof(*dev)); - memset(dev, 0, sizeof(*dev)); - dev->id = malloc(strlen(dev_id) + 1); - strcpy(dev->id, dev_id); - dev->badge = pixmap; - dev->box.left = PBOOT_LEFT_ICON_XOFF; - dev->box.right = dev->box.left + PBOOT_LEFT_ICON_WIDTH; - dev->box.top = PBOOT_LEFT_ICON_YOFF + - PBOOT_LEFT_ICON_STRIDE * index; - dev->box.bottom = dev->box.top + PBOOT_LEFT_ICON_HEIGHT; - - pboot_devices[index] = dev; - - twin_window_damage(pboot_lpane->window, - dev->box.left, dev->box.top, - dev->box.right, dev->box.bottom); - twin_window_queue_paint(pboot_lpane->window); - - return index; -} - -int pboot_device_remove(const char *dev_id) -{ - pboot_device_t *dev = NULL; - int i, newsel = pboot_dev_sel; - - /* find the matching device */ - for (i = 0; i < pboot_dev_count; i++) { - if (!strcmp(pboot_devices[i]->id, dev_id)) { - dev = pboot_devices[i]; - break; - } - } - - if (!dev) - return TWIN_FALSE; - - memmove(pboot_devices + i, pboot_devices + i + 1, - sizeof(*pboot_devices) * (pboot_dev_count + i - 1)); - pboot_devices[--pboot_dev_count] = NULL; - - /* select the newly-focussed device */ - if (pboot_dev_sel > i) - newsel = pboot_dev_sel - 1; - else if (pboot_dev_sel == i && i >= pboot_dev_count) - newsel = pboot_dev_count - 1; - pboot_set_device_select(newsel, 1); - - /* todo: free device & options */ - - return TWIN_TRUE; -} - -static void pboot_make_background(void) -{ - twin_pixmap_t *filepic, *scaledpic; - const char *background_path; - - /* Set background pixmap */ - LOG("loading background..."); - background_path = artwork_pathname("background.jpg"); - filepic = twin_jpeg_to_pixmap(background_path, TWIN_ARGB32); - LOG("%s\n", filepic ? "ok" : "failed"); - - if (filepic == NULL) - return; - - if (pboot_screen->height == filepic->height && - pboot_screen->width == filepic->width) - scaledpic = filepic; - else { - twin_fixed_t sx, sy; - twin_operand_t srcop; - - scaledpic = twin_pixmap_create(TWIN_ARGB32, - pboot_screen->width, - pboot_screen->height); - if (scaledpic == NULL) { - twin_pixmap_destroy(filepic); - return; - } - sx = twin_fixed_div(twin_int_to_fixed(filepic->width), - twin_int_to_fixed(pboot_screen->width)); - sy = twin_fixed_div(twin_int_to_fixed(filepic->height), - twin_int_to_fixed(pboot_screen->height)); - - twin_matrix_scale(&filepic->transform, sx, sy); - srcop.source_kind = TWIN_PIXMAP; - srcop.u.pixmap = filepic; - twin_composite(scaledpic, 0, 0, &srcop, 0, 0, - NULL, 0, 0, TWIN_SOURCE, - pboot_screen->width, pboot_screen->height); - twin_pixmap_destroy(filepic); - - } - twin_screen_set_background(pboot_screen, scaledpic); -} - -#define PS3FB_IOCTL_SETMODE _IOW('r', 1, int) -#define PS3FB_IOCTL_GETMODE _IOR('r', 2, int) - -static void exitfunc(void) -{ -#ifndef _USE_X11 - if (pboot_fbdev) - twin_fbdev_destroy(pboot_fbdev); - pboot_fbdev = NULL; - if (pboot_vmode_change != -1) { - int fd = open("/dev/fb0", O_RDWR); - if (fd >= 0) - ioctl(fd, PS3FB_IOCTL_SETMODE, - (unsigned long)&pboot_vmode_change); - close(fd); - } -#endif -} - -static void sigint(int sig) -{ - exitfunc(); - syscall(__NR_exit); -} - -static void usage(const char *progname) -{ - fprintf(stderr, "Usage: %s [-u] [-h]\n", progname); -} - -int main(int argc, char **argv) -{ - int c; - int udev_trigger = 0; - - for (;;) { - c = getopt(argc, argv, "u::h"); - if (c == -1) - break; - - switch (c) { - case 'u': - udev_trigger = 1; - break; - case 'h': - usage(argv[0]); - return EXIT_SUCCESS; - default: - fprintf(stderr, "Unknown option '%c'\n", c); - usage(argv[0]); - return EXIT_FAILURE; - } - } - - atexit(exitfunc); - signal(SIGINT, sigint); - -#ifdef _USE_X11 - pboot_x11 = twin_x11_create(XOpenDisplay(0), 1024, 768); - if (pboot_x11 == NULL) { - perror("failed to create x11 screen !\n"); - return 1; - } - pboot_screen = pboot_x11->screen; -#else - /* Create screen and mouse drivers */ - pboot_fbdev = twin_fbdev_create(-1, SIGUSR1); - if (pboot_fbdev == NULL) { - perror("failed to create fbdev screen !\n"); - return 1; - } - pboot_screen = pboot_fbdev->screen; - twin_linux_mouse_create(NULL, pboot_screen); - twin_linux_js_create(pboot_screen); - - if (pboot_fbdev != NULL) { - char *cursor_path = artwork_pathname("cursor.gz"); - pboot_cursor = twin_load_X_cursor(cursor_path, 2, - &pboot_cursor_hx, - &pboot_cursor_hy); - if (pboot_cursor == NULL) - pboot_cursor = - twin_get_default_cursor(&pboot_cursor_hx, - &pboot_cursor_hy); - } -#endif - - /* Set background pixmap */ - pboot_make_background(); - - /* Init more stuffs */ - pboot_create_lpane(); - pboot_create_rpane(); - pboot_create_spane(); - - if (!pboot_start_device_discovery(udev_trigger)) { - LOG("Couldn't start device discovery!\n"); - return 1; - } - - pboot_set_lfocus(0); - twin_screen_set_active(pboot_screen, pboot_lpane->window->pixmap); - pboot_screen->event_filter = pboot_event_filter; - - /* Console switch */ -#ifndef _USE_X11 - if (pboot_fbdev) - twin_fbdev_activate(pboot_fbdev); -#endif - - /* Process events */ - twin_dispatch (); - - return 0; -} diff --git a/ui/twin/pbt-client.c b/ui/twin/pbt-client.c new file mode 100644 index 0000000..a2c3e93 --- /dev/null +++ b/ui/twin/pbt-client.c @@ -0,0 +1,323 @@ +/* + * Copyright Geoff Levand + * + * 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 +#include + +#include "pbt-client.h" + +#include "log/log.h" +#include "talloc/talloc.h" +#include "waiter/waiter.h" +#include "ui/common/discover-client.h" + +static struct pb_opt_data *pbt_opt_data_from_item(struct pbt_item *item) +{ + return item->data; +} + +void pbt_client_resize(struct pbt_client *client) +{ + (void)client; // TODO +} + +void pbt_frame_status_printf(struct pbt_frame *frame, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + // TODO + va_end(ap); +} + +static int pbt_client_run_kexec(struct pbt_item *item) +{ + int result; + struct pb_opt_data *opt_data = pbt_opt_data_from_item(item); + + pb_log("%s: %s\n", __func__, pbt_item_name(item)); + + pbt_frame_status_printf(&item->pbt_client->frame, "Booting %s...", + pbt_item_name(item)); + + assert(item->pbt_client->kexec_cb); + result = item->pbt_client->kexec_cb(item->pbt_client, opt_data); + + if (!result) { + //mvaddstr(1, 0, "system is going down now..."); + sleep(60); + } + + pb_log("%s: failed: %s\n", __func__, opt_data->kd->image); + + pbt_frame_status_printf(&item->pbt_client->frame, "Failed: kexec %s", + opt_data->kd->image); + + return 0; +} + +static int pbt_client_on_edit(struct pbt_item *item) +{ + DBGS("*** %s ***\n", pbt_item_name(item)); + return 0; +} + +static int pbt_device_add(struct device *dev, struct pbt_client *client) +{ + struct pbt_frame *const frame = &client->frame; + struct pbt_item *device_item; + struct boot_option *opt; + struct pbt_quad q; + const char *icon_file; + struct pbt_item *selected_item = NULL; + + pb_log("%s: %p %s: n_options %d\n", __func__, dev, dev->id, + dev->n_options); + + pb_protocol_dump_device(dev, "", pb_log_get_stream()); + + /* device_item */ + + // FIXME: check for existing dev->id + + icon_file = dev->icon_file ? dev->icon_file : pbt_icon_chooser(dev->id); + + device_item = pbt_item_create_reduced(frame->top_menu, dev->id, + frame->top_menu->n_items, icon_file); + + if (!device_item) + goto fail_device_item_create; + + device_item->pb_device = dev; + frame->top_menu->n_items++; + + /* sub_menu */ + + q.x = frame->top_menu->window->pixmap->width; + q.y = 0; + q.width = frame->top_menu->scr->tscreen->width - q.x; + q.height = frame->top_menu->scr->tscreen->height; + + device_item->sub_menu = pbt_menu_create(device_item, dev->id, + frame->top_menu->scr, frame->top_menu, &q, + &frame->top_menu->layout); + if (!device_item->sub_menu) + goto fail_sub_menu_create; + + list_for_each_entry(&dev->boot_options, opt, list) { + struct pbt_item *i; + struct pb_opt_data *opt_data; + + i = pbt_item_create(device_item->sub_menu, + opt->id, device_item->sub_menu->n_items++, + opt->icon_file, opt->name, opt->description); + + if (!i) { + assert(0); + break; + } + + i->pb_opt = opt; + i->pbt_client = client; + i->on_execute = pbt_client_run_kexec; + i->on_edit = pbt_client_on_edit; + + i->data = opt_data = talloc(i, struct pb_opt_data); + opt_data->name = opt->name; + opt_data->kd = talloc(i, struct pb_kexec_data); + opt_data->kd->image = talloc_strdup(opt_data->kd, + opt->boot_image_file); + opt_data->kd->initrd = talloc_strdup(opt_data->kd, + opt->initrd_file); + opt_data->kd->args = talloc_strdup(opt_data->kd, + opt->boot_args); + opt_data->dev = dev; + opt_data->opt = opt; + opt_data->opt_hash = pb_opt_hash(dev, opt); + + /* Select the first item as default. */ + + if (!selected_item) + selected_item = i; + + /* If this is the default_item select it and start timer. */ + + if (opt_data->opt_hash + == device_item->sub_menu->default_item_hash) { + selected_item = i; + ui_timer_kick(&client->signal_data.timer); + } + } + + pbt_menu_set_selected(device_item->sub_menu, selected_item); + + pbt_menu_show(frame->top_menu, 1); + twin_screen_update(client->frame.scr->tscreen); + + return 0; + +fail_sub_menu_create: +fail_device_item_create: + assert(0); + return -1; +} + +static void pbt_device_remove(struct device *dev, struct pbt_client *client) +{ + struct pbt_frame *const frame = &client->frame; + struct list *i_list = frame->top_menu->item_list; + struct pbt_item *removed_item; + struct pbt_item *prev_item; + struct pbt_item *next_item; + struct pbt_item *i; + twin_window_t *last_window; + struct boot_option *opt; + + pb_log("%s: %p %s: n_options %d\n", __func__, dev, dev->id, + dev->n_options); + + pb_protocol_dump_device(dev, "", pb_log_get_stream()); + + removed_item = NULL; + list_for_each_entry(i_list, i, list) { + if (i->pb_device == dev) { + removed_item = i; + break; + } + } + + if (!removed_item) { + pb_log("%s: %p %s: unknown device\n", __func__, dev, dev->id); + assert(0 && "unknown device"); + return; + } + + prev_item = list_prev_entry(i_list, removed_item, list); + next_item = list_next_entry(i_list, removed_item, list); + + if (removed_item == frame->top_menu->selected) { + if (prev_item) + pbt_menu_set_selected(frame->top_menu, prev_item); + else if (next_item) + pbt_menu_set_selected(frame->top_menu, next_item); + else + assert(0 && "empty list"); + } + + if (next_item) { + + /* Shift items up. */ + + i = next_item; + list_for_each_entry_continue(i_list, i, list) { + last_window = i->window; + i->window = list_prev_entry(i_list, i, list)->window; + twin_window_set_name(i->window, last_window->name); + i->window->client_data = last_window->client_data; + } + } + + twin_window_hide(last_window); + twin_window_destroy(last_window); + + list_remove(&removed_item->list); + removed_item->window = NULL; + talloc_free(removed_item); + frame->top_menu->n_items--; + + pbt_menu_show(frame->top_menu, 1); + twin_screen_update(client->frame.scr->tscreen); +} + +static struct discover_client_ops pbt_client_ops = { + .device_add = (void *)pbt_device_add, + .device_remove = (void *)pbt_device_remove, +}; + +static void pbt_client_destructor(struct pbt_client *client) +{ + pb_log("%s\n", __func__); + + // move to screen twin_x11_destroy(twin_ctx); + talloc_free(client->discover_client); + memset(client, 0, sizeof(*client)); +} + +struct pbt_client *pbt_client_init(enum pbt_twin_backend backend, unsigned int width, + unsigned int height, + int (*kexec_cb)(struct pbt_client *, struct pb_opt_data *)) +{ + struct pbt_client *pbt_client; + unsigned int i; + + pbt_client = talloc_zero(NULL, struct pbt_client); + + if (!pbt_client) { + pb_log("%s: alloc pbt_client failed.\n", __func__); + fprintf(stderr, "%s: alloc pbt_client failed.\n", __func__); + goto fail_alloc; + } + + talloc_set_destructor(pbt_client, (void *)pbt_client_destructor); + + pbt_client->sig = "pbt_client"; + pbt_client->kexec_cb = kexec_cb; + pbt_client->frame.scr = pbt_scr_init(pbt_client, backend, width, height, + NULL, NULL); + + if (!pbt_client->frame.scr) + goto fail_scr_init; + + /* Loop here for scripts that just started the server. */ +if (1) { + for (i = 10; i; i--) { + pbt_client->discover_client + = discover_client_init(&pbt_client_ops, pbt_client); + + if (pbt_client->discover_client) + break; + + pb_log("%s: waiting for server %d\n", __func__, i); + sleep(1); + } + + if (!pbt_client->discover_client) { + pb_log("%s: discover_client_init failed.\n", __func__); + fprintf(stderr, "%s: error: discover_client_init failed.\n", + __func__); + fprintf(stderr, "check that pb-discover, " + "the petitboot daemon is running.\n"); + goto fail_client_init; + } + + waiter_register(discover_client_get_fd(pbt_client->discover_client), + WAIT_IN, (waiter_cb)discover_client_process, + pbt_client->discover_client); +} + return pbt_client; + +fail_client_init: + talloc_free(pbt_client); +fail_scr_init: +fail_alloc: + return NULL; +} diff --git a/ui/twin/pbt-client.h b/ui/twin/pbt-client.h new file mode 100644 index 0000000..731bf03 --- /dev/null +++ b/ui/twin/pbt-client.h @@ -0,0 +1,59 @@ +/* + * Copyright Geoff Levand + * + * 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(_PBT_CLIENT_H) +#define _PBT_CLIENT_H + +#include "ui/common/ui-system.h" +#include "pbt-menu.h" +#include "pbt-scr.h" + +/** + * struct pbt_decor - Provides title, help and status bars. + */ + +struct pbt_decor { + twin_label_t *title; + twin_label_t *help; + twin_label_t *status; +}; + +struct pbt_frame { + struct pbt_scr *scr; + struct pbt_menu *top_menu; + struct pbt_decor decor; +}; + +void pbt_frame_status_printf(struct pbt_frame *frame, const char *format, ...); + +struct pbt_client { + const char *sig; + struct pb_signal_data signal_data; + void *client_data; + int (*kexec_cb)(struct pbt_client *pbt_client, struct pb_opt_data *pod); + + struct pbt_frame frame; + struct discover_client *discover_client; +}; + +struct pbt_client *pbt_client_init(enum pbt_twin_backend backend, + unsigned int width, unsigned int height, + int (*kexec_cb)(struct pbt_client *, struct pb_opt_data *)); +void pbt_client_destroy(struct pbt_client *client); +void pbt_client_resize(struct pbt_client *client); + +#endif diff --git a/ui/twin/pbt-main.c b/ui/twin/pbt-main.c new file mode 100644 index 0000000..cc69b4b --- /dev/null +++ b/ui/twin/pbt-main.c @@ -0,0 +1,101 @@ +/* + * Copyright Geoff Levand + * + * 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 +#include + +#include "pbt-main.h" + +void pbt_print_version(void) +{ + printf("pb-twin (" PACKAGE_NAME ") " PACKAGE_VERSION "\n"); +} + +void pbt_print_usage(void) +{ + pbt_print_version(); + printf( +"Usage: pb-twin [-h, --help] [-l, --log log-file] [-r, --reset-defaults]\n" +" [-t, --timeout] [-V, --version] [[-f --fbdev] | [-x --x11]]\n"); +} + +/** + * pbt_opts_parse - Parse the command line options. + */ + +int pbt_opts_parse(struct pbt_opts *opts, int argc, char *argv[]) +{ + static const struct option long_options[] = { + {"fbdev", no_argument, NULL, 'f'}, + {"help", no_argument, NULL, 'h'}, + {"log", required_argument, NULL, 'l'}, + {"reset-defaults", no_argument, NULL, 'r'}, + {"timeout", no_argument, NULL, 't'}, + {"version", no_argument, NULL, 'V'}, + {"x11", no_argument, NULL, 'x'}, + { NULL, 0, NULL, 0}, + }; + static const char short_options[] = "fhl:trVx"; + static const struct pbt_opts default_values = { + .backend = pbt_twin_x11, + .log_file = "pb-twin.log", + }; + + *opts = default_values; + + while (1) { + int c = getopt_long(argc, argv, short_options, long_options, + NULL); + + if (c == EOF) + break; + + switch (c) { + case 'f': + opts->backend = pbt_twin_fbdev; + break; + case 'h': + opts->show_help = pbt_opt_yes; + break; + case 'l': + opts->log_file = optarg; + break; + case 't': + opts->use_timeout = pbt_opt_yes; + break; + case 'r': + opts->reset_defaults = pbt_opt_yes; + break; + case 'V': + opts->show_version = pbt_opt_yes; + break; + case 'x': + opts->backend = pbt_twin_x11; + break; + default: + opts->show_help = pbt_opt_yes; + return -1; + } + } + + return optind != argc; +} diff --git a/ui/twin/pbt-main.h b/ui/twin/pbt-main.h new file mode 100644 index 0000000..5f0e906 --- /dev/null +++ b/ui/twin/pbt-main.h @@ -0,0 +1,47 @@ +/* + * Copyright Geoff Levand + * + * 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(_PBT_MAIN_H) +#define _PBT_MAIN_H + +#include "pbt-scr.h" + +/** + * enum opt_value - Tri-state options variables. + */ + +enum pbt_opt_value {pbt_opt_undef = 0, pbt_opt_yes, pbt_opt_no}; + +/** + * struct opts - Values from command line options. + */ + +struct pbt_opts { + enum pbt_twin_backend backend; + enum pbt_opt_value show_help; + const char *log_file; + enum pbt_opt_value reset_defaults; + enum pbt_opt_value use_timeout; + enum pbt_opt_value show_version; +}; + + +void pbt_print_version(void); +void pbt_print_usage(void); +int pbt_opts_parse(struct pbt_opts *opts, int argc, char *argv[]); + +#endif diff --git a/ui/twin/pbt-menu.c b/ui/twin/pbt-menu.c new file mode 100644 index 0000000..844b1e1 --- /dev/null +++ b/ui/twin/pbt-menu.c @@ -0,0 +1,515 @@ +/* + * Copyright Geoff Levand + * + * 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 +#include +#include + +#include "log/log.h" +#include "talloc/talloc.h" +#include "ui/common/ui-system.h" + +#include "pbt-menu.h" + +static void pbt_item_draw_cb(twin_window_t *window) +{ + struct pbt_item *item = pbt_item_from_window(window); + twin_pixmap_t *image; + + assert(window == item->window); + + pbt_dump_item(item); + + //pbt_dump_pixmap(window->pixmap); + + if (pbt_item_is_selected(item) && item->menu->has_focus) + image = item->pixmap_active; + else if (pbt_item_is_selected(item) && !item->menu->has_focus) + image = item->pixmap_selected; + else + image = item->pixmap_idle; + + pbt_image_draw(window->pixmap, image); +} + +static twin_bool_t pbt_item_event_cb(twin_window_t *window, + twin_event_t *event) +{ + struct pbt_item *item = pbt_item_from_window(window); + + pbt_dump_event(pbt_item_name(item), window, event); + + switch(event->kind) { + case TwinEventButtonDown: + if (item->on_execute) + item->on_execute(item); + break; + case TwinEventButtonUp: + break; + case TwinEventMotion: + /* prevent window drag */ + return TWIN_TRUE; + case TwinEventEnter: + pbt_item_set_as_selected(item); + break; + case TwinEventLeave: + break; + case TwinEventKeyDown: + switch(event->u.key.key) { + case (twin_keysym_t)XK_Return: + case (twin_keysym_t)KEY_ENTER: + if (item->on_execute) + item->on_execute(item); + break; + case (twin_keysym_t)'e': + if (item->on_edit) + item->on_edit(item); + break; + default: + break; + } + break; + default: + break; + } + return TWIN_FALSE; +} + +int pbt_item_editor(struct pbt_item *item) +{ + (void)item; + return -1; +} + +struct pbt_item *pbt_item_create(struct pbt_menu *menu, const char *name, + unsigned int position, const char *icon_filename, const char *title, + const char *text) +{ + struct pbt_quad q; + struct pbt_item *item; + twin_pixmap_t *icon; + twin_operand_t src; + const struct pbt_menu_layout *layout = &menu->layout; + twin_path_t *path; + static const twin_argb32_t focus_color = 0x10404040; + enum { + corner_rounding = 8, + stroke_width = 2, + }; + + assert(menu); + + DBGS("%d:%s (%s)\n", position, name, icon_filename); + + item = talloc_zero(menu, struct pbt_item); + + if (!item) + return NULL; + + item->menu = menu; + item->on_edit = pbt_item_editor; + + pbt_menu_get_item_quad(menu, position, &q); + + item->window = twin_window_create(menu->scr->tscreen, + TWIN_ARGB32, TwinWindowPlain, + q.x, q.y, q.width, q.height); + + if (!item->window) + goto fail_window_create; + + twin_window_set_name(item->window, name); + item->window->client_data = item; + item->window->draw = pbt_item_draw_cb; + item->window->event = pbt_item_event_cb; + + item->pixmap_idle = twin_pixmap_create(TWIN_ARGB32, q.width, q.height); + assert(item->pixmap_idle); + + item->pixmap_selected = twin_pixmap_create(TWIN_ARGB32, q.width, + q.height); + assert(item->pixmap_selected); + + item->pixmap_active = twin_pixmap_create(TWIN_ARGB32, q.width, + q.height); + assert(item->pixmap_active); + + if (!item->pixmap_idle || !item->pixmap_selected || !item->pixmap_active) + goto fail_pixmap_create; + + twin_fill(item->pixmap_idle, 0x01000000, TWIN_SOURCE, 0, 0, q.width, + q.height); + + /* Add item icon */ + + icon = pbt_icon_load(icon_filename); + + if (!icon) + goto fail_icon; + + src.source_kind = TWIN_PIXMAP; + src.u.pixmap = icon; + + twin_composite(item->pixmap_idle, + //0, (item->pixmap_idle->height - icon->height) / 2, + 0, 0, + &src, 0, 0, + NULL, 0, 0, + TWIN_SOURCE, + icon->width, icon->height); + + /* Add item text */ + + path = twin_path_create(); + assert(path); + + if (title) { + twin_path_set_font_size(path, + twin_int_to_fixed(layout->title.font_size)); + twin_path_set_font_style(path, TWIN_TEXT_UNHINTED); + + twin_path_move(path, + twin_int_to_fixed(icon->width + layout->text_space), + twin_int_to_fixed(layout->title.font_size + + layout->text_space)); + twin_path_utf8(path, title); + twin_paint_path(item->pixmap_idle, layout->title.color, path); + twin_path_empty(path); + } + + if (text) { + twin_path_set_font_size(path, + twin_int_to_fixed(layout->text.font_size)); + twin_path_move(path, + twin_int_to_fixed(icon->width + layout->text_space), + twin_int_to_fixed(layout->title.font_size + + layout->text.font_size + + layout->text_space)); + twin_path_utf8(path, text); + twin_paint_path(item->pixmap_idle, layout->text.color, path); + twin_path_empty(path); + } + + pbt_image_draw(item->pixmap_selected, item->pixmap_idle); + pbt_image_draw(item->pixmap_active, item->pixmap_idle); + +if (0) { + static const struct pbt_border grey_border = { + .right = 1, + .left = 1, + .top = 1, + .bottom = 1, + .fill_color = 0xffe0e0e0, + }; + + //pbt_border_draw(item->pixmap_idle, &pbt_blue_debug_border); + pbt_border_draw(item->pixmap_selected, &grey_border); + pbt_border_draw(item->pixmap_active, &pbt_green_debug_border); +} else { + assert(!(stroke_width % 2)); + + /* pixmap_selected */ + + twin_path_rounded_rectangle(path, + twin_int_to_fixed(stroke_width / 2), + twin_int_to_fixed(stroke_width / 2), + twin_int_to_fixed(item->pixmap_selected->width - stroke_width), + twin_int_to_fixed(item->pixmap_selected->height - stroke_width), + twin_int_to_fixed(corner_rounding), + twin_int_to_fixed(corner_rounding)); + + twin_paint_stroke(item->pixmap_selected, focus_color, path, + twin_int_to_fixed(stroke_width)); + + twin_path_empty(path); + + /* pixmap_active */ + + twin_path_rounded_rectangle(path, 0, 0, + twin_int_to_fixed(item->pixmap_active->width), + twin_int_to_fixed(item->pixmap_active->height), + twin_int_to_fixed(corner_rounding), + twin_int_to_fixed(corner_rounding)); + + twin_paint_path(item->pixmap_active, focus_color, path); + + twin_path_empty(path); // FIXME: need it??? +} + twin_path_destroy(path); + + list_add_tail(menu->item_list, &item->list); + + pbt_item_redraw(item); + + return item; + +fail_window_create: +fail_pixmap_create: +fail_icon: + return NULL; +} + +void _pbt_dump_item(const struct pbt_item* item, const char *func, int line) +{ + DBG("%s:%d: %p: %sselected, %sfocus\n", func, line, item, + (pbt_item_is_selected(item) ? "+" : "-"), + (item->menu->has_focus ? "+" : "-")); +} + +/** + * pbt_menu_get_item_quad - Return item coords relative to screen origin. + */ + +struct pbt_quad *pbt_menu_get_item_quad(const struct pbt_menu *menu, + unsigned int pos, struct pbt_quad *q) +{ + const struct pbt_menu_layout *layout = &menu->layout; + + q->x = menu->window->pixmap->x + layout->item_space; + + q->width = menu->window->pixmap->width - 2 * layout->item_space; + + q->y = menu->window->pixmap->y + layout->item_space + + pos * (layout->item_height + layout->item_space); + + q->height = layout->item_height; + + return q; +} + +static void pbt_menu_draw_cb(twin_window_t *window) +{ + struct pbt_menu *menu = pbt_menu_from_window(window); + twin_path_t *path = twin_path_create(); + + assert(path); + + pbt_dump_pixmap(window->pixmap); + + twin_fill(window->pixmap, menu->background_color, TWIN_SOURCE, + 0, 0, window->pixmap->width, window->pixmap->height); + + pbt_border_draw(window->pixmap, &menu->border); + + twin_path_destroy(path); +} + +static twin_bool_t pbt_menu_event_cb(twin_window_t *window, + twin_event_t *event) +{ + struct pbt_menu *menu = pbt_menu_from_window(window); + struct pbt_item *i; + + pbt_dump_event(pbt_menu_name(menu), window, event); + + switch(event->kind) { + case TwinEventButtonDown: + case TwinEventButtonUp: + case TwinEventMotion: + /* prevent window drag */ + return TWIN_TRUE; + case TwinEventEnter: + pbt_menu_set_focus(menu, 1); + break; + case TwinEventLeave: + if (!pbt_window_contains(window, event)) + pbt_menu_set_focus(menu, 0); + break; + case TwinEventKeyDown: + switch(event->u.key.key) { + case (twin_keysym_t)XK_Up: + case (twin_keysym_t)KEY_UP: + i = list_prev_entry(menu->item_list, menu->selected, + list); + if (i) + pbt_item_set_as_selected(i); + break; + case (twin_keysym_t)XK_Down: + case (twin_keysym_t)KEY_DOWN: + i = list_next_entry(menu->item_list, menu->selected, + list); + if (i) + pbt_item_set_as_selected(i); + break; + case (twin_keysym_t)XK_Left: + case (twin_keysym_t)KEY_LEFT: + if (menu->parent) { + pbt_menu_set_focus(menu, 0); + pbt_menu_set_focus(menu->parent, 1); + } else + DBGS("no parent\n"); + break; + case (twin_keysym_t)XK_Right: + case (twin_keysym_t)KEY_RIGHT: + if (menu->selected->sub_menu) { + pbt_menu_set_focus(menu, 0); + pbt_menu_set_focus(menu->selected->sub_menu, 1); + } else + DBGS("no sub_menu\n"); + break; + default: + return pbt_item_event_cb(menu->selected->window, event); + } + break; + default: + break; + } + return TWIN_FALSE; +} + +struct pbt_menu *pbt_menu_create(void *talloc_ctx, const char *name, + struct pbt_scr *scr, struct pbt_menu *parent, const struct pbt_quad *q, + const struct pbt_menu_layout *layout) +{ + struct pbt_menu *menu; + + assert(scr); + + DBGS("%s\n", name); + + menu = talloc_zero(talloc_ctx, struct pbt_menu); + + if (!menu) + return NULL; + + menu->scr = scr; + menu->parent = parent; + menu->layout = *layout; + + menu->item_list = talloc(menu, struct list); + list_init(menu->item_list); + + menu->window = twin_window_create(scr->tscreen, TWIN_ARGB32, + TwinWindowPlain, q->x, q->y, + q->width, q->height); + + if (!menu->window) + goto fail_window; + + DBGS("window = %p\n", menu->window); + + twin_window_set_name(menu->window, name); + + menu->background_color = 0x01000000; //FIXME: what value??? + + menu->window->draw = pbt_menu_draw_cb; + menu->window->event = pbt_menu_event_cb; + menu->window->client_data = menu; + + pbt_dump_pixmap(menu->window->pixmap); + + pbt_menu_redraw(menu); + + return menu; + +fail_window: + assert(0); + talloc_free(menu); + return NULL; +} + +void pbt_menu_set_focus(struct pbt_menu *menu, int focus) +{ + DBGS("%s(%p): %d -> %d\n", pbt_menu_name(menu), menu, menu->has_focus, + focus); + + assert(menu->selected); + + if (!menu->has_focus == !focus) + return; + + menu->has_focus = !!focus; + + /* Route key events to menu with focus. */ + + if (menu->has_focus) + menu->scr->tscreen->active = menu->window->pixmap; + + pbt_item_redraw(menu->selected); +} + +void pbt_menu_hide(struct pbt_menu *menu) +{ + struct pbt_item *item; + + if (!menu) + return; + + list_for_each_entry(menu->item_list, item, list) { + if (item->sub_menu) + pbt_menu_hide(item->sub_menu); + + twin_window_hide(item->window); + //twin_window_queue_paint(item->window); + } + + twin_window_hide(menu->window); + //twin_window_queue_paint(menu->window); +} + +void pbt_menu_show(struct pbt_menu *menu, int hide) +{ + struct pbt_item *item; + + if (!menu) + return; + + twin_window_show(menu->window); + pbt_menu_redraw(menu); + + list_for_each_entry(menu->item_list, item, list) { + twin_window_show(item->window); + pbt_item_redraw(item); + + if (item->sub_menu) { + if (pbt_item_is_selected(item)) + pbt_menu_show(item->sub_menu, hide); + else if (hide) + pbt_menu_hide(item->sub_menu); + } + } +} + +void pbt_menu_set_selected(struct pbt_menu *menu, struct pbt_item *item) +{ + struct pbt_item *last_selected; + + assert(item); + + DBGS("%s(%p): %s(%p) -> %s(%p)\n", pbt_menu_name(menu), menu, + (menu->selected ? pbt_menu_name(menu) : "none"), + menu->selected, pbt_item_name(item), item); + + if (menu->selected == item) + return; + + last_selected = menu->selected; + menu->selected = item; + + if (last_selected) { + pbt_menu_hide(last_selected->sub_menu); + pbt_item_redraw(last_selected); + } + + pbt_item_redraw(item); + pbt_menu_show(item->sub_menu, 0); +} diff --git a/ui/twin/pbt-menu.h b/ui/twin/pbt-menu.h new file mode 100644 index 0000000..7547f16 --- /dev/null +++ b/ui/twin/pbt-menu.h @@ -0,0 +1,167 @@ +/* + * Copyright Geoff Levand + * + * 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(_PBT_MENU_H) +#define _PBT_MENU_H + +#include "list/list.h" +#include "pb-protocol/pb-protocol.h" + +#include "pbt-scr.h" + + +/** + * struct pbt_item - A menu item. + */ + +struct pbt_item +{ + struct list_item list; + + struct pbt_menu *menu; // convinence pointer + struct pbt_client *pbt_client; // convinence pointer + + twin_window_t *window; + twin_pixmap_t *pixmap_idle; + twin_pixmap_t *pixmap_selected; + twin_pixmap_t *pixmap_active; + + struct pbt_menu *sub_menu; + + int (*on_execute)(struct pbt_item *item); + int (*on_edit)(struct pbt_item *item); + + union { + struct device *pb_device; + struct boot_option *pb_opt; + }; + void *data; +}; + +struct pbt_item *pbt_item_create(struct pbt_menu *menu, const char *name, + unsigned int position, const char *icon_filename, const char *title, + const char *text); + +static inline struct pbt_item *pbt_item_create_reduced(struct pbt_menu *menu, + const char *name, unsigned int position, const char *icon_filename) +{ + return pbt_item_create(menu, name, position, icon_filename, NULL, + NULL); +} + +static inline const char *pbt_item_name(const struct pbt_item *item) +{ + return item->window->name; +} + +#define pbt_dump_item(_i) _pbt_dump_item(_i, __func__, __LINE__) +void _pbt_dump_item(const struct pbt_item* item, const char *func, + int line); + +struct pbt_text_layout { + twin_argb32_t color; + unsigned int font_size; +}; + +struct pbt_menu_layout { + unsigned int item_height; + unsigned int item_space; + unsigned int text_space; + //unsigned int icon_height; + //unsigned int icon_width; + struct pbt_text_layout title; + struct pbt_text_layout text; +}; + + /** + * struct pbt_menu - A twin menu screen. + */ + +struct pbt_menu { + struct pbt_scr *scr; // convinence pointer + struct pbt_menu *parent; + twin_window_t *window; + twin_pixmap_t *pixmap; + + struct pbt_border border; + twin_argb32_t background_color; + struct pbt_menu_layout layout; + + struct list* item_list; + unsigned int n_items; + uint32_t default_item_hash; + + int has_focus; + struct pbt_item *selected; + int (*on_open)(struct pbt_menu *menu); +}; + +struct pbt_menu *pbt_menu_create(void *talloc_ctx, const char *name, + struct pbt_scr *scr, struct pbt_menu *parent, const struct pbt_quad *q, + const struct pbt_menu_layout *layout); +void pbt_menu_set_focus(struct pbt_menu *menu, int focus); +void pbt_menu_set_selected(struct pbt_menu *menu, struct pbt_item *item); +struct pbt_quad *pbt_menu_get_item_quad(const struct pbt_menu *menu, + unsigned int pos, struct pbt_quad *q); +void pbt_menu_hide(struct pbt_menu *menu); +void pbt_menu_show(struct pbt_menu *menu, int hide); + +static inline const char *pbt_menu_name(const struct pbt_menu *menu) +{ + return menu->window->name; +} + +static inline struct pbt_menu *pbt_menu_from_window(twin_window_t *window) +{ + struct pbt_menu *menu = window->client_data; + + assert(menu); + return menu; +} + +static inline struct pbt_item *pbt_item_from_window(twin_window_t *window) +{ + struct pbt_item *item = window->client_data; + + assert(item); + return item; +} + +static inline int pbt_item_is_selected(const struct pbt_item* item) +{ + return item == item->menu->selected; +} + +static inline void pbt_item_set_as_selected(struct pbt_item* item) +{ + pbt_menu_set_selected(item->menu, item); +} + +int pbt_item_editor(struct pbt_item *item); + +static inline void pbt_item_redraw(struct pbt_item *item) +{ + pbt_window_redraw(item->window); +} + +static inline void pbt_menu_redraw(struct pbt_menu *menu) +{ + pbt_window_redraw(menu->window); +} + + +#endif diff --git a/ui/twin/pbt-scr.c b/ui/twin/pbt-scr.c new file mode 100644 index 0000000..8d6c498 --- /dev/null +++ b/ui/twin/pbt-scr.c @@ -0,0 +1,459 @@ +/* + * Copyright Geoff Levand + * + * 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 + */ + +#include "config.h" +#define _GNU_SOURCE +#include + +#include +#include + +#include "list/list.h" +#include "log/log.h" +#include "talloc/talloc.h" +#include "waiter/waiter.h" +#include "ui/common/ui-system.h" +#include "pbt-scr.h" + +void _pbt_dump_event(const char *text, twin_window_t *twindow, + const twin_event_t *tevent, const char *func, int line) +{ + switch(tevent->kind) { + case TwinEventButtonDown: + DBG("%s:%d: %s@%p: TwinEventButtonDown %x\n", func, line, text, + twindow, tevent->kind); + return; + case TwinEventButtonUp: + DBG("%s:%d: %s@%p: TwinEventButtonUp %x\n", func, line, text, + twindow, tevent->kind); + return; + case TwinEventMotion: + //DBG("%s:%d:%s@%p: TwinEventMotion %x\n", func, line, text, + // twindow, tevent->kind); + return; + case TwinEventEnter: + DBG("%s:%d: %s@%p: TwinEventEnter %x\n", func, line, text, + twindow, tevent->kind); + return; + case TwinEventLeave: + DBG("%s:%d: %s@%p: TwinEventLeave %x\n", func, line, text, + twindow, tevent->kind); + return; + case TwinEventKeyDown: + case TwinEventKeyUp: + { + const char *kind = (tevent->kind == TwinEventKeyDown) + ? "TwinEventKeyDown" : "TwinEventKeyUp "; + + switch(tevent->u.key.key) { + case (twin_keysym_t)XK_Up: + case (twin_keysym_t)KEY_UP: + DBG("%s:%d: %s@%p: %s = 'KEY_UP'\n", func, line, text, + twindow, kind); + return; + case (twin_keysym_t)XK_Down: + case (twin_keysym_t)KEY_DOWN: + DBG("%s:%d: %s@%p: %s = 'KEY_DOWN'\n", func, line, text, + twindow, kind); + return; + case (twin_keysym_t)XK_Right: + case (twin_keysym_t)KEY_RIGHT: + DBG("%s:%d: %s@%p: %s = 'KEY_RIGHT'\n", func, line, text, + twindow, kind); + return; + case (twin_keysym_t)XK_Left: + case (twin_keysym_t)KEY_LEFT: + DBG("%s:%d: %s@%p: %s = 'KEY_LEFT'\n", func, line, text, + twindow, kind); + return; + case (twin_keysym_t)XK_Escape: + case (twin_keysym_t)KEY_ESC: + DBG("%s:%d: %s@%p: %s = 'KEY_ESC'\n", func, line, text, + twindow, kind); + return; + case (twin_keysym_t)XK_Return: + case (twin_keysym_t)KEY_ENTER: + DBG("%s:%d: %s@%p: %s = 'KEY_ENTER'\n", func, line, text, + twindow, kind); + return; + case (twin_keysym_t)XK_Delete: + case (twin_keysym_t)KEY_DELETE: + DBG("%s:%d: %s@%p: %s = 'KEY_DELETE'\n", func, line, text, + twindow, kind); + return; + case (twin_keysym_t)XK_BackSpace: + case (twin_keysym_t)KEY_BACKSPACE: + DBG("%s:%d: %s@%p: %s = 'KEY_BACKSPACE'\n", func, line, text, + twindow, kind); + return; + default: + DBG("%s:%d: %s@%p: %s = %d (%xh) = '%c'\n", func, line, text, twindow, + kind, + tevent->u.key.key, tevent->u.key.key, + (char)tevent->u.key.key); + } + return; + } + default: + DBG("%s:%d: %s@%p: %x\n", func, line, text, twindow, tevent->kind); + break; + } +} + +/** + * pbt_background_load - Load the background pixmap from storage. + * @filename: File name of a jpg background. + * + * Returns the default background if @filename is NULL. Returns a default + * pattern if the load of @filename fails. + */ + +twin_pixmap_t *pbt_background_load(twin_screen_t *tscreen, + const char *filename) +{ + static const char *default_background_file = + PB_ARTWORK_PATH "/background.jpg"; + twin_pixmap_t *raw_background; + twin_pixmap_t *scaled_background; + + if (!filename) + filename = default_background_file; + + raw_background = twin_jpeg_to_pixmap(filename, TWIN_ARGB32); + + if (!raw_background) { + pb_log("%s: loading image '%s' failed\n", __func__, filename); + + /* Fallback to a default pattern */ + + return twin_make_pattern(); + } + + if (tscreen->height == raw_background->height && + tscreen->width == raw_background->width) + return raw_background; + + /* Scale as needed. */ + + twin_fixed_t sx, sy; + twin_operand_t srcop; + + scaled_background = twin_pixmap_create(TWIN_ARGB32, + tscreen->width, + tscreen->height); + if (!scaled_background) { + pb_log("%s: scale '%s' failed\n", __func__, filename); + twin_pixmap_destroy(raw_background); + return twin_make_pattern(); + } + sx = twin_fixed_div(twin_int_to_fixed(raw_background->width), + twin_int_to_fixed(tscreen->width)); + sy = twin_fixed_div(twin_int_to_fixed(raw_background->height), + twin_int_to_fixed(tscreen->height)); + + twin_matrix_scale(&raw_background->transform, sx, sy); + srcop.source_kind = TWIN_PIXMAP; + srcop.u.pixmap = raw_background; + twin_composite(scaled_background, 0, 0, &srcop, 0, 0, + NULL, 0, 0, TWIN_SOURCE, + tscreen->width, tscreen->height); + + twin_pixmap_destroy(raw_background); + + return scaled_background; +} + +const char *pbt_icon_chooser(const char *hint) +{ + if (strstr(hint, "net")) + return PB_ARTWORK_PATH "/network.png"; + + return NULL; +} + +/** + * pbt_icon_load - Load an icon pixmap from storage. + * @filename: File name of a png icon. + * + * Returns the default icon if @filename is NULL, or if the load + * of @filename fails. + * Caches pixmaps based on a hash of the @filename string. + */ + +twin_pixmap_t *pbt_icon_load(const char *filename) +{ + static const char *default_icon_file = PB_ARTWORK_PATH "/tux.png"; + struct cache_entry { + struct list_item list; + int hash; + twin_pixmap_t *icon; + }; + STATIC_LIST(icon_cache); + struct cache_entry new; + struct cache_entry *i; + + if (!filename) + filename = default_icon_file; + +retry: + new.hash = pb_elf_hash(filename); + + list_for_each_entry(&icon_cache, i, list) { + if (i->hash == new.hash) { + DBGS("found %p\n", i->icon); + return i->icon; + } + } + + new.icon = twin_png_to_pixmap(filename, TWIN_ARGB32); + + if (!new.icon) { + pb_log("%s: loading image '%s' failed\n", __func__, filename); + + if (filename == default_icon_file) + return NULL; + + filename = default_icon_file; + goto retry; + } + + DBGS("new %p\n", new.icon); + + i = talloc(NULL, struct cache_entry); + *i = new; + list_add(&icon_cache, &i->list); + + pbt_dump_pixmap(new.icon); + + return new.icon; +} + +/** + * pbt_border_draw - Draw a border on a pixmap. + * @pixmap: The image to operate on. + * @border: The border to draw. + */ + +void pbt_border_draw(twin_pixmap_t *pixmap, const struct pbt_border *border) +{ + twin_path_t *path = twin_path_create(); + twin_argb32_t fill = border->fill_color ? border->fill_color + : 0xff000000; /* default to black */ + + assert(path); + + //pbt_dump_pixmap(pixmap); + + if (border->left) { + twin_path_rectangle(path, 0, 0, + twin_int_to_fixed(border->left), + twin_int_to_fixed(pixmap->height)); + } + + if (border->right) { + twin_path_rectangle(path, + twin_int_to_fixed(pixmap->width - border->right), + 0, + twin_int_to_fixed(pixmap->width), + twin_int_to_fixed(pixmap->height)); + } + + if (border->top) { + twin_path_rectangle(path, 0, 0, + twin_int_to_fixed(pixmap->width), + twin_int_to_fixed(border->top)); + } + + if (border->bottom) { + twin_path_rectangle(path, 0, + twin_int_to_fixed(pixmap->height - border->bottom), + twin_int_to_fixed(pixmap->width), + twin_int_to_fixed(border->bottom)); + } + + twin_paint_path(pixmap, fill, path); + twin_path_empty(path); +} + +int pbt_window_contains(const twin_window_t *window, const twin_event_t *event) +{ + pbt_dump_pixmap(window->pixmap); + + if (event->u.pointer.x < window->pixmap->x) { + DBGS("%p: {%d,%d} left miss\n", window, event->u.pointer.x, event->u.pointer.y); + return 0; + } + if (event->u.pointer.x >= window->pixmap->x + window->pixmap->width) { + DBGS("%p: {%d,%d} right miss\n", window, event->u.pointer.x, event->u.pointer.y); + return 0; + } + if (event->u.pointer.y < window->pixmap->y) { + DBGS("%p: {%d,%d} high miss\n", window, event->u.pointer.x, event->u.pointer.y); + return 0; + } + if (event->u.pointer.y >= window->pixmap->y + window->pixmap->height){ + DBGS("%p: {%d,%d} low miss\n", window, event->u.pointer.x, event->u.pointer.y); + return 0; + } + + DBGS("%p: {%d,%d} hit\n", window, event->u.pointer.x, event->u.pointer.y); + return 1; +} + + +static __attribute__((unused)) void pbt_image_copy(twin_pixmap_t *dest, twin_pixmap_t *src) +{ + twin_operand_t op; + + assert(dest->height >= src->height); + + op.source_kind = TWIN_PIXMAP; + op.u.pixmap = src; + + twin_composite(dest, 0, 0, &op, 0, 0, NULL, + 0, 0, TWIN_SOURCE, src->width, src->height); +} + +void pbt_image_draw(twin_pixmap_t *dest, twin_pixmap_t *image) +{ + twin_operand_t src; + int offset; + + assert(dest->height >= image->height); + + src.source_kind = TWIN_PIXMAP; + src.u.pixmap = image; + + /* Center the image in the window. */ + + offset = (dest->height - image->height) / 2; + + twin_composite(dest, offset, offset, &src, 0, 0, NULL, + 0, 0, TWIN_SOURCE, image->width, image->height); +} + +static int pbt_twin_waiter_cb(struct pbt_twin_ctx *twin_ctx) +{ +#if defined(HAVE_LIBTWIN_TWIN_X11_H) + if (twin_ctx->backend == pbt_twin_x11) + twin_x11_process_events(twin_ctx->x11); +#endif +#if defined(HAVE_LIBTWIN_TWIN_FBDEV_H) + if (twin_ctx->backend == pbt_twin_fbdev) + twin_fbdev_process_events(twin_ctx->fbdev); +#endif + return 0; +}; + +static void pbt_scr_destructor(struct pbt_scr *scr) +{ + pb_log("%s\n", __func__); + + twin_x11_destroy(scr->twin_ctx.x11); + // FIXME: need cursor cleanup??? + memset(scr, 0, sizeof(*scr)); +} + +struct pbt_scr *pbt_scr_init(void *talloc_ctx, enum pbt_twin_backend backend, + unsigned int width, unsigned int height, + const char *filename_background, + twin_bool_t (*scr_event_cb)(twin_screen_t *tscreen, + twin_event_t *event)) +{ + struct pbt_scr *scr = talloc_zero(talloc_ctx, struct pbt_scr); + int waiter_fd = -1; + + assert(backend && backend < 3); + assert(width > 100); + assert(height > 100); + + if (!scr) { + pb_log("%s: alloc pbt_scr failed.\n", __func__); + goto fail_alloc; + } + + talloc_set_destructor(scr, (void *)pbt_scr_destructor); + + twin_feature_init(); // FIXME: need it??? + + scr->twin_ctx.backend = backend; + + if (backend == pbt_twin_x11) { + pb_log("%s: using twin x11 backend.\n", __func__); +#if !defined(HAVE_LIBTWIN_TWIN_X11_H) + assert(0); +#else + scr->twin_ctx.x11 = twin_x11_create_ext(XOpenDisplay(0), width, + height, 0); + + if (!scr->twin_ctx.x11) { + pb_log("%s: twin_x11_create_ext failed.\n", __func__); + perror("failed to create twin x11 context\n"); + goto fail_ctx_create; + } + + pb_log("%s: x11: %p\n", __func__, scr->twin_ctx.x11); + + assert(scr->twin_ctx.x11->screen); + scr->tscreen = scr->twin_ctx.x11->screen; + waiter_fd = ConnectionNumber(scr->twin_ctx.x11->dpy); +#endif + } else if (backend == pbt_twin_fbdev) { + pb_log("%s: using twin fbdev backend.\n", __func__); +#if !defined(HAVE_LIBTWIN_TWIN_FBDEV_H) + assert(0); +#else + scr->twin_ctx.fbdev = twin_fbdev_create_ext(-1, SIGUSR1, 0); + + if (!scr->twin_ctx.fbdev) { + pb_log("%s: twin_fbdev_create_ext failed.\n", __func__); + perror("failed to create twin fbdev context\n"); + goto fail_ctx_create; + } + + assert(scr->twin_ctx.fbdev->screen); + scr->tscreen = scr->twin_ctx.fbdev->screen; + waiter_fd = scr->twin_ctx.fbdev->vt_fd; + + twin_fbdev_activate(scr->twin_ctx.fbdev); +#endif + } + + scr->tscreen->event_filter = scr_event_cb; + + twin_screen_set_background(scr->tscreen, + pbt_background_load(scr->tscreen, filename_background)); + + assert(waiter_fd != -1); + + waiter_register(waiter_fd, WAIT_IN, (void *)pbt_twin_waiter_cb, + &scr->twin_ctx); + + return scr; + +fail_ctx_create: +fail_alloc: + return NULL; +} + +void pbt_window_redraw(twin_window_t *twindow) +{ + twin_window_damage(twindow, 0, 0, twindow->pixmap->width, + twindow->pixmap->height); + //twin_window_queue_paint(twindow); + twin_window_draw(twindow); +} diff --git a/ui/twin/pbt-scr.h b/ui/twin/pbt-scr.h new file mode 100644 index 0000000..c075aad --- /dev/null +++ b/ui/twin/pbt-scr.h @@ -0,0 +1,174 @@ +/* + * Copyright Geoff Levand + * + * 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(_PBT_SCR_H) +#define _PBT_SCR_H + +#include +#include +#include +#include +#include + +#if defined(HAVE_LIBTWIN_TWIN_X11_H) +# include +#endif +#if defined(HAVE_LIBTWIN_TWIN_FBDEV_H) +# include +#endif + +#define DBG(fmt, args...) pb_log("DBG: " fmt, ## args) +#define DBGS(fmt, args...) \ + pb_log("DBG:%s:%d: " fmt, __func__, __LINE__, ## args) + +struct pbt_quad { + twin_coord_t x; + twin_coord_t y; + twin_coord_t width; + twin_coord_t height; +}; + +/** + * struct pbt_border - A window border. + * @left: Pixel count for left side. + * @fill_color: Border fill color. + */ + +struct pbt_border { + unsigned int left; + unsigned int right; + unsigned int top; + unsigned int bottom; + twin_argb32_t fill_color; +}; + +enum { + pbt_debug_red = 0x00800000, + pbt_debug_green = 0x00008000, + pbt_debug_blue = 0x00000080, +}; + +static const struct pbt_border pbt_thin_border = { + .right = 2, + .left = 2, + .top = 2, + .bottom = 2, +}; + +static const struct pbt_border pbt_right_border = { + .right = 2 +}; + +static const struct pbt_border pbt_red_debug_border = { + .right = 1, + .left = 1, + .top = 1, + .bottom = 1, + .fill_color = pbt_debug_red, +}; + +static const struct pbt_border pbt_green_debug_border = { + .right = 1, + .left = 1, + .top = 1, + .bottom = 1, + .fill_color = pbt_debug_green, +}; + +static const struct pbt_border pbt_blue_debug_border = { + .right = 1, + .left = 1, + .top = 1, + .bottom = 1, + .fill_color = pbt_debug_blue, +}; + +static const struct pbt_border pbt_yellow_debug_border = { + .right = 1, + .left = 1, + .top = 1, + .bottom = 1, + .fill_color = pbt_debug_green + pbt_debug_red, +}; + +void pbt_border_draw(twin_pixmap_t *pixmap, const struct pbt_border *border); + +struct pbt_cursor { + twin_pixmap_t *pixmap; + int hx; + int hy; +}; + +enum pbt_twin_backend { + pbt_twin_x11 = 1, + pbt_twin_fbdev, +}; + +struct pbt_twin_ctx { + union { + void *ptr; +#if defined(HAVE_LIBTWIN_TWIN_X11_H) + twin_x11_t *x11; +#endif +#if defined(HAVE_LIBTWIN_TWIN_FBDEV_H) + twin_fbdev_t *fbdev; +#endif + }; + enum pbt_twin_backend backend; +}; + +struct pbt_scr { + struct pbt_twin_ctx twin_ctx; + twin_screen_t *tscreen; + twin_pixmap_t *cursor; +}; + +struct pbt_scr *pbt_scr_init(void *talloc_ctx, enum pbt_twin_backend backend, + unsigned int width, unsigned int height, + const char *filename_background, + twin_bool_t (*scr_event_cb)(twin_screen_t *tscreen, + twin_event_t *event)); + +static inline struct pbt_scr *pbt_scr_from_tscreen(twin_screen_t *tscreen) +{ + size_t offset = (size_t)&((struct pbt_scr *)0)->tscreen; + return (struct pbt_scr *)((char *)tscreen - offset); +} + +void pbt_image_draw(twin_pixmap_t *dest, twin_pixmap_t *image); + +#define pbt_dump_event(_s, _w, _e) _pbt_dump_event(_s, _w, _e, __func__, __LINE__) +void _pbt_dump_event(const char *text, twin_window_t *twindow, + const twin_event_t *tevent, const char *func, int line); + +twin_pixmap_t *pbt_background_load(twin_screen_t *tscreen, + const char *filename); +twin_pixmap_t *pbt_icon_load(const char *filename); +const char *pbt_icon_chooser(const char *hint); +int pbt_window_contains(const twin_window_t *window, const twin_event_t *event); +void pbt_window_redraw(twin_window_t *twindow); + +#define pbt_dump_pixmap(_p) \ + DBGS("pixmap(%p): {x,y,w,h} = {%d,%d,%d,%d}\n", \ + _p, \ + _p->x, \ + _p->y, \ + _p->width, \ + _p->height) + + +#endif \ No newline at end of file -- 2.39.2