summaryrefslogtreecommitdiffstats
path: root/ui/twin
diff options
context:
space:
mode:
authorGeoff Levand <geoff@infradead.org>2012-02-15 11:33:41 -0800
committerGeoff Levand <geoff@infradead.org>2012-02-15 11:33:41 -0800
commitbd0c684c9941c24ee4191f4550ec966d5b1fa8ab (patch)
treeecf731010991d0f5201894c885a06188fb73b109 /ui/twin
parent5cceb42c773aa404456417685ea50b2ada5570a2 (diff)
downloadtalos-petitboot-bd0c684c9941c24ee4191f4550ec966d5b1fa8ab.tar.gz
talos-petitboot-bd0c684c9941c24ee4191f4550ec966d5b1fa8ab.zip
Update twin ui to use discover server
Signed-off-by: Geoff Levand <geoff@infradead.org>
Diffstat (limited to 'ui/twin')
-rw-r--r--ui/twin/Makefile.am20
-rw-r--r--ui/twin/main-generic.c355
-rw-r--r--ui/twin/main-ps3.c494
-rw-r--r--ui/twin/pb-twin.c1194
-rw-r--r--ui/twin/pbt-client.c323
-rw-r--r--ui/twin/pbt-client.h59
-rw-r--r--ui/twin/pbt-main.c101
-rw-r--r--ui/twin/pbt-main.h47
-rw-r--r--ui/twin/pbt-menu.c515
-rw-r--r--ui/twin/pbt-menu.h167
-rw-r--r--ui/twin/pbt-scr.c459
-rw-r--r--ui/twin/pbt-scr.h174
12 files changed, 2708 insertions, 1200 deletions
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 <geoff@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#define _GNU_SOURCE
+#include <assert.h>
+#include <errno.h>
+#include <getopt.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include "log/log.h"
+#include "talloc/talloc.h"
+#include "waiter/waiter.h"
+#include "ui/common/timer.h"
+
+#include "pbt-client.h"
+#include "pbt-main.h"
+
+
+static struct pbt_client *client_from_item(struct pbt_item *item)
+{
+ return item->data;
+}
+
+static int exit_to_shell_cb(struct pbt_item *item)
+{
+ struct pbt_client *client = client_from_item(item);
+
+ client->signal_data.abort = 1;
+ return 0;
+}
+
+static int edit_preferences_cb(struct pbt_item *item)
+{
+ struct pbt_client *client = client_from_item(item);
+
+ (void)client;
+
+ pb_log("%s: TODO\n", __func__);
+
+ return 0;
+}
+
+static struct pbt_item *setup_system_item(struct pbt_menu *menu,
+ struct pbt_client *client)
+{
+ struct pbt_item *top_item;
+ struct pbt_item *sub_item;
+ struct pbt_quad q;
+
+ top_item = pbt_item_create_reduced(menu, "system", 0,
+ PB_ARTWORK_PATH "/system.png");
+
+ if (!top_item)
+ goto fail_top_item_create;
+
+ /* sub_menu */
+
+ q.x = menu->window->pixmap->width;
+ q.y = 0;
+ q.width = menu->scr->tscreen->width - q.x;
+ q.height = menu->scr->tscreen->height;
+
+ top_item->sub_menu = pbt_menu_create(top_item, "system", menu->scr,
+ menu, &q, &menu->layout);
+
+ if (!top_item->sub_menu)
+ goto fail_sub_menu_create;
+
+ sub_item = pbt_item_create(top_item->sub_menu, "Preferences", 0,
+ PB_ARTWORK_PATH "/system.png", "Preferences",
+ "Edit petitboot preferences");
+
+ if (!sub_item)
+ goto fail_sub_item_0;
+
+ sub_item->on_execute = edit_preferences_cb;
+ sub_item->data = client;
+ pbt_menu_set_selected(top_item->sub_menu, sub_item);
+
+ sub_item = pbt_item_create(top_item->sub_menu, "Exit to Shell", 1,
+ PB_ARTWORK_PATH "/system.png", "Exit to Shell",
+ "Exit to a system shell prompt");
+
+ if (!sub_item)
+ goto fail_sub_item_1;
+
+ sub_item->on_execute = exit_to_shell_cb;
+ sub_item->data = client;
+
+ top_item->sub_menu->n_items = 2;
+
+ /* Set shell item as default */
+
+ pbt_menu_set_selected(top_item->sub_menu, sub_item);
+
+ return top_item;
+
+fail_sub_item_1:
+fail_sub_item_0:
+fail_sub_menu_create:
+// FIXME: todo
+fail_top_item_create:
+// FIXME: need cleanup
+ assert(0);
+ return NULL;
+}
+
+static struct pbt_menu *menu_create(struct pbt_client *client)
+{
+ static struct pbt_menu_layout layout = {
+ .item_height = 64,
+ .item_space = 10,
+ .text_space = 5,
+ .title = {.font_size = 30, .color = 0xff000000,},
+ .text = {.font_size = 18, .color = 0xff800000,},
+ };
+
+ struct pbt_menu *device_menu;
+ struct pbt_item *system_item;
+ struct pbt_quad q;
+ twin_pixmap_t *icon;
+ const struct pbt_border *border;
+
+ assert(client->frame.scr);
+
+ icon = pbt_icon_load(NULL);
+
+ if (!icon)
+ return NULL;
+
+ assert((unsigned int)icon->height == layout.item_height);
+
+ /* Create main (device) menu */
+
+ border = &pbt_right_border;
+
+ q.x = 0;
+ q.y = 0;
+ q.width = icon->width + 2 * layout.item_space + border->left
+ + border->right;
+ q.height = client->frame.scr->tscreen->height;
+
+ device_menu = pbt_menu_create(client, "device", client->frame.scr, NULL,
+ &q, &layout);
+
+ if (!device_menu)
+ goto fail_menu;
+
+ //FIXME: move to accessors
+ device_menu->background_color = 0x80000000;
+ device_menu->border = *border;
+
+ /* Setup system item */
+
+ system_item = setup_system_item(device_menu, client);
+
+ if (!system_item)
+ goto fail_system_item;
+
+ device_menu->n_items++;
+
+ /* Set system item as default */
+
+ pbt_menu_set_selected(device_menu, system_item);
+ pbt_menu_set_focus(device_menu, 1);
+ pbt_menu_show(device_menu, 1);
+
+ pbt_menu_redraw(device_menu);
+
+ return device_menu;
+
+fail_system_item:
+ // FIXME: need cleanup
+fail_menu:
+ assert(0);
+ return NULL;
+}
+
+static int kexec_cb(__attribute__((unused)) struct pbt_client *client, struct pb_opt_data *opt_data)
+{
+ int result;
+
+ assert(opt_data);
+
+ pb_log("%s: %s\n", __func__, opt_data->name);
+
+ result = pb_run_kexec(opt_data->kd);
+
+ return result;
+}
+
+static int run(struct pbt_client *client)
+{
+ while (1) {
+ int result = waiter_poll();
+
+ if (result < 0 && errno != EINTR) {
+ pb_log("%s: poll: %s\n", __func__, strerror(errno));
+ break;
+ }
+
+ if (client->signal_data.abort)
+ break;
+
+ ui_timer_process_sig(&client->signal_data.timer);
+
+ while (client->signal_data.resize) {
+ client->signal_data.resize = 0;
+ pbt_client_resize(client);
+ }
+ }
+
+ return 0;
+}
+
+static struct pb_signal_data *_signal_data;
+
+static void set_signal_data(struct pb_signal_data *sd)
+{
+ _signal_data = sd;
+}
+
+static struct pb_signal_data *get_signal_data(void)
+{
+ return _signal_data;
+}
+
+static void sig_handler(int signum)
+{
+ DBGS("%d\n", signum);
+
+ struct pb_signal_data *sd = get_signal_data();
+
+ if (!sd)
+ return;
+
+ switch (signum) {
+ case SIGALRM:
+ ui_timer_sigalrm(&sd->timer);
+ break;
+ case SIGWINCH:
+ sd->resize = 1;
+ break;
+ default:
+ assert(0 && "unknown sig");
+ /* fall through */
+ case SIGINT:
+ case SIGHUP:
+ case SIGTERM:
+ sd->abort = 1;
+ break;
+ }
+}
+
+/**
+ * main - twin bootloader main routine.
+ */
+
+int main(int argc, char *argv[])
+{
+ static struct sigaction sa;
+ static struct pbt_opts opts;
+ int result;
+ int ui_result;
+ FILE *log;
+ struct pbt_client *client;
+
+ result = pbt_opts_parse(&opts, argc, argv);
+
+ if (result) {
+ pbt_print_usage();
+ return EXIT_FAILURE;
+ }
+
+ if (opts.show_help == pbt_opt_yes) {
+ pbt_print_usage();
+ return EXIT_SUCCESS;
+ }
+
+ if (opts.show_version == pbt_opt_yes) {
+ pbt_print_version();
+ return EXIT_SUCCESS;
+ }
+
+ log = fopen(opts.log_file, "a");
+ assert(log);
+ pb_log_set_stream(log);
+
+#if defined(DEBUG)
+ pb_log_always_flush(1);
+#endif
+
+ pb_log("--- pb-twin ---\n");
+
+ sa.sa_handler = sig_handler;
+ result = sigaction(SIGALRM, &sa, NULL);
+ result += sigaction(SIGHUP, &sa, NULL);
+ result += sigaction(SIGINT, &sa, NULL);
+ result += sigaction(SIGTERM, &sa, NULL);
+ result += sigaction(SIGWINCH, &sa, NULL);
+
+ if (result) {
+ pb_log("%s sigaction failed.\n", __func__);
+ return EXIT_FAILURE;
+ }
+
+ client = pbt_client_init(opts.backend, 900, 300, kexec_cb);
+
+ if (!client) {
+ ui_result = EXIT_FAILURE;
+ goto done;
+ }
+
+ set_signal_data(&client->signal_data);
+
+ client->frame.top_menu = menu_create(client);
+
+ if (!client->frame.top_menu) {
+ ui_result = EXIT_FAILURE;
+ goto done;
+ }
+
+ twin_screen_update(client->frame.scr->tscreen);
+ ui_result = run(client);
+
+done:
+ talloc_free(client);
+
+ pb_log("--- end ---\n");
+
+ return ui_result ? EXIT_FAILURE : EXIT_SUCCESS;
+}
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 <geoff@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#define _GNU_SOURCE
+#include <assert.h>
+#include <errno.h>
+#include <getopt.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <linux/input.h>
+#include <sys/time.h>
+
+#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 <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <unistd.h>
-#include <syscall.h>
-#include <assert.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-
-#include <linux/input.h>
-
-#undef _USE_X11
-
-#include <libtwin/twin.h>
-#include <libtwin/twin_linux_mouse.h>
-#include <libtwin/twin_linux_js.h>
-#include <libtwin/twin_png.h>
-#include <libtwin/twin_jpeg.h>
-
-#include "petitboot.h"
-#include "petitboot-paths.h"
-
-#ifdef _USE_X11
-#include <libtwin/twin_x11.h>
-static twin_x11_t *pboot_x11;
-#else
-#include <libtwin/twin_fbdev.h>
-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 <geoff@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#define _GNU_SOURCE
+#include <assert.h>
+#include <string.h>
+
+#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 <geoff@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#if !defined(_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 <geoff@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <getopt.h>
+
+#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 <geoff@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#if !defined(_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 <geoff@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#define _GNU_SOURCE
+#include <assert.h>
+#include <string.h>
+#include <linux/input.h>
+
+#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 <geoff@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#if !defined(_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 <geoff@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "config.h"
+#define _GNU_SOURCE
+#include <assert.h>
+
+#include <string.h>
+#include <linux/input.h>
+
+#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 <geoff@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#if !defined(_PBT_SCR_H)
+#define _PBT_SCR_H
+
+#include <libtwin/twin.h>
+#include <libtwin/twin_jpeg.h>
+#include <libtwin/twin_linux_mouse.h>
+#include <libtwin/twin_linux_js.h>
+#include <libtwin/twin_png.h>
+
+#if defined(HAVE_LIBTWIN_TWIN_X11_H)
+# include <libtwin/twin_x11.h>
+#endif
+#if defined(HAVE_LIBTWIN_TWIN_FBDEV_H)
+# include <libtwin/twin_fbdev.h>
+#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
OpenPOWER on IntegriCloud