diff options
author | Jeremy Kerr <jk@ozlabs.org> | 2008-12-15 15:22:34 +1100 |
---|---|---|
committer | Jeremy Kerr <jk@ozlabs.org> | 2008-12-15 15:22:34 +1100 |
commit | 32e6a41f33e5576716b351bd473a27939fe94fa1 (patch) | |
tree | 0d6b75ac0a02d2496416095405cb9498777c3beb /ui | |
parent | 000a92b4fa909c432732ac3ed8f28eeeaeac70ee (diff) | |
download | petitboot-32e6a41f33e5576716b351bd473a27939fe94fa1.zip petitboot-32e6a41f33e5576716b351bd473a27939fe94fa1.tar.gz |
Initial support for multiple UIs
Move the device discovery code from separate udev helpers to a single
process to listen on two sockets: one SOCK_DGRAM for incoming udev
events, and one SOCK_STREAM for UIs to connect.
Initial support for client/server infrastructure, still need to wire-up
the udev messages.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Diffstat (limited to 'ui')
-rw-r--r-- | ui/common/discover-client.c | 111 | ||||
-rw-r--r-- | ui/common/discover-client.h | 29 | ||||
-rw-r--r-- | ui/test/pb-test.c | 62 | ||||
-rw-r--r-- | ui/twin/artwork/background.jpg | bin | 0 -> 26285 bytes | |||
-rw-r--r-- | ui/twin/artwork/cdrom.png | bin | 0 -> 4848 bytes | |||
-rw-r--r-- | ui/twin/artwork/cursor.gz | bin | 0 -> 9167 bytes | |||
-rw-r--r-- | ui/twin/artwork/hdd.png | bin | 0 -> 4983 bytes | |||
-rw-r--r-- | ui/twin/artwork/tux.png | bin | 0 -> 4382 bytes | |||
-rw-r--r-- | ui/twin/artwork/usbpen.png | bin | 0 -> 5970 bytes | |||
-rw-r--r-- | ui/twin/pb-twin.c | 1194 |
10 files changed, 1396 insertions, 0 deletions
diff --git a/ui/common/discover-client.c b/ui/common/discover-client.c new file mode 100644 index 0000000..bc5a4fa --- /dev/null +++ b/ui/common/discover-client.c @@ -0,0 +1,111 @@ + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> + +#include <sys/socket.h> +#include <sys/un.h> +#include <asm/byteorder.h> + +#include <talloc/talloc.h> + +#include "ui/common/discover-client.h" +#include "ui/common/device.h" +#include "pb-protocol/pb-protocol.h" + +struct discover_client { + int fd; + struct discover_client_ops ops; +}; + +static int discover_client_destructor(void *arg) +{ + struct discover_client *client = arg; + + if (client->fd >= 0) + close(client->fd); + + return 0; +} + +struct discover_client* discover_client_init(struct discover_client_ops *ops) +{ + struct discover_client *client; + struct sockaddr_un addr; + + client = talloc(NULL, struct discover_client); + if (!client) + return NULL; + + memcpy(&client->ops, ops, sizeof(client->ops)); + + client->fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (!client->fd < 0) { + perror("socket"); + goto out_err; + } + + talloc_set_destructor(client, discover_client_destructor); + + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, PB_SOCKET_PATH); + + if (connect(client->fd, (struct sockaddr *)&addr, sizeof(addr))) { + perror("connect"); + goto out_err; + } + + return client; + +out_err: + talloc_free(client); + return NULL; +} + +int discover_client_get_fd(struct discover_client *client) +{ + return client->fd; +} + +void discover_client_destroy(struct discover_client *client) +{ + talloc_free(client); +} + +int discover_client_process(struct discover_client *client) +{ + struct pb_protocol_message *message; + struct device *dev; + char *dev_id; + + message = pb_protocol_read_message(client, client->fd); + + if (!message) + return 0; + + switch (message->action) { + case PB_PROTOCOL_ACTION_ADD: + dev = pb_protocol_deserialise_device(client, message); + if (!dev) { + printf("no device?\n"); + return 0; + } + client->ops.add_device(dev); + talloc_free(dev); + break; + case PB_PROTOCOL_ACTION_REMOVE: + dev_id = pb_protocol_deserialise_string(client, message); + if (!dev_id) { + printf("no device id?\n"); + return 0; + } + client->ops.remove_device(dev_id); + break; + default: + printf("unknown action %d\n", message->action); + } + + + return 0; +} diff --git a/ui/common/discover-client.h b/ui/common/discover-client.h new file mode 100644 index 0000000..3ce745b --- /dev/null +++ b/ui/common/discover-client.h @@ -0,0 +1,29 @@ +#ifndef _DISCOVER_CLIENT_H +#define _DISCOVER_CLIENT_H + +#include <pb-protocol/pb-protocol.h> +#include "ui/common/device.h" + +struct discover_client; + +struct discover_client_ops { + int (*add_device)(struct device *); + void (*remove_device)(char *); +}; + +struct discover_client *discover_client_init(struct discover_client_ops *ops); + +int discover_client_get_fd(struct discover_client *client); + +void discover_client_destroy(struct discover_client *client); + +/** + * Process data from the server. + * + * Will read from the client socket, and call add_device on any discovered + * devices. + * + */ +int discover_client_process(struct discover_client *client); + +#endif diff --git a/ui/test/pb-test.c b/ui/test/pb-test.c new file mode 100644 index 0000000..a36e991 --- /dev/null +++ b/ui/test/pb-test.c @@ -0,0 +1,62 @@ + +#include <stdio.h> + +#include "ui/common/discover-client.h" +#include "ui/common/device.h" + +static int print_device_add(struct device *device) +{ + int i; + + printf("new device:\n"); + printf("\tid: %s\n", device->id); + printf("\tname: %s\n", device->name); + printf("\tdesc: %s\n", device->description); + printf("\ticon: %s\n", device->icon_file); + + printf("\t%d boot options:\n", device->n_options); + for (i = 0; i < device->n_options; i++) { + struct boot_option *opt = &device->options[i]; + printf("\t\tid: %s\n", opt->id); + printf("\t\tname: %s\n", opt->name); + printf("\t\tdesc: %s\n", opt->description); + printf("\t\ticon: %s\n", opt->icon_file); + printf("\t\tboot: %s\n", opt->boot_image_file); + printf("\t\tinit: %s\n", opt->initrd_file); + printf("\t\targs: %s\n", opt->boot_args); + } + + return 0; +} + +static void print_device_remove(const char *dev_id) +{ + printf("removed device:\n"); + printf("\tid: %s\n", dev_id); +} + +struct discover_client_ops client_ops = { + .add_device = print_device_add, + .remove_device = print_device_remove, +}; + +int main(void) +{ + struct discover_client *client; + + printf("pid: %d\n", getpid()); + + client = discover_client_init(&client_ops); + if (!client) + return -1; + + for (;;) { + int rc; + + rc = discover_client_process(client); + if (rc) + break; + } + + return 0; +} diff --git a/ui/twin/artwork/background.jpg b/ui/twin/artwork/background.jpg Binary files differnew file mode 100644 index 0000000..9d54d31 --- /dev/null +++ b/ui/twin/artwork/background.jpg diff --git a/ui/twin/artwork/cdrom.png b/ui/twin/artwork/cdrom.png Binary files differnew file mode 100644 index 0000000..8b3eeb6 --- /dev/null +++ b/ui/twin/artwork/cdrom.png diff --git a/ui/twin/artwork/cursor.gz b/ui/twin/artwork/cursor.gz Binary files differnew file mode 100644 index 0000000..b73b72f --- /dev/null +++ b/ui/twin/artwork/cursor.gz diff --git a/ui/twin/artwork/hdd.png b/ui/twin/artwork/hdd.png Binary files differnew file mode 100644 index 0000000..2b1594c --- /dev/null +++ b/ui/twin/artwork/hdd.png diff --git a/ui/twin/artwork/tux.png b/ui/twin/artwork/tux.png Binary files differnew file mode 100644 index 0000000..99f3465 --- /dev/null +++ b/ui/twin/artwork/tux.png diff --git a/ui/twin/artwork/usbpen.png b/ui/twin/artwork/usbpen.png Binary files differnew file mode 100644 index 0000000..53a365e --- /dev/null +++ b/ui/twin/artwork/usbpen.png diff --git a/ui/twin/pb-twin.c b/ui/twin/pb-twin.c new file mode 100644 index 0000000..ee68930 --- /dev/null +++ b/ui/twin/pb-twin.c @@ -0,0 +1,1194 @@ + +#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_add_device(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_remove_device(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; +} |