Update twin ui to use discover server
authorGeoff Levand <geoff@infradead.org>
Wed, 15 Feb 2012 19:33:41 +0000 (11:33 -0800)
committerGeoff Levand <geoff@infradead.org>
Wed, 15 Feb 2012 19:33:41 +0000 (11:33 -0800)
Signed-off-by: Geoff Levand <geoff@infradead.org>
12 files changed:
ui/twin/Makefile.am
ui/twin/main-generic.c [new file with mode: 0644]
ui/twin/main-ps3.c [new file with mode: 0644]
ui/twin/pb-twin.c [deleted file]
ui/twin/pbt-client.c [new file with mode: 0644]
ui/twin/pbt-client.h [new file with mode: 0644]
ui/twin/pbt-main.c [new file with mode: 0644]
ui/twin/pbt-main.h [new file with mode: 0644]
ui/twin/pbt-menu.c [new file with mode: 0644]
ui/twin/pbt-menu.h [new file with mode: 0644]
ui/twin/pbt-scr.c [new file with mode: 0644]
ui/twin/pbt-scr.h [new file with mode: 0644]

index 605e05e3659f1bef3a9193218902c12edd551145..9e8f5de56c88df6f6332e851f0301fa013285751 100644 (file)
@@ -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 (file)
index 0000000..d314cbc
--- /dev/null
@@ -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 (file)
index 0000000..db0539a
--- /dev/null
@@ -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 (file)
index a5c0a27..0000000
+++ /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 (file)
index 0000000..a2c3e93
--- /dev/null
@@ -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 (file)
index 0000000..731bf03
--- /dev/null
@@ -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 (file)
index 0000000..cc69b4b
--- /dev/null
@@ -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 (file)
index 0000000..5f0e906
--- /dev/null
@@ -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 (file)
index 0000000..844b1e1
--- /dev/null
@@ -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 (file)
index 0000000..7547f16
--- /dev/null
@@ -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 (file)
index 0000000..8d6c498
--- /dev/null
@@ -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 (file)
index 0000000..c075aad
--- /dev/null
@@ -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