3 Subject: Update PS3 twin GUI program
5 Update the PS3 twin GUI program to work with petitboot-multi-ui.
7 Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>
12 ui/twin/ps3-twin.c | 1441 +++++++++++++++++++++++++++++++++++++++++++++++++++++
13 4 files changed, 1463 insertions(+), 5 deletions(-)
17 @@ -18,6 +18,7 @@ twin_LDFLAGS = @twin_LIBS@
20 ENABLE_PS3 = @ENABLE_PS3@
21 +ENABLE_X11 = @ENABLE_X11@
27 @@ -56,7 +56,17 @@ AS_IF([test "x$with_twin" != xno],
28 [if test "x$with_twin" != xcheck; then
29 AC_MSG_FAILURE([--with-twin was given, but test for twin failed])
33 + AC_CHECK_HEADERS([libtwin/twin_x11.h])])
36 + [AS_HELP_STRING([--enable-x11],
41 +AS_IF([test "x$enable_x11" != xno], [AC_SUBST([ENABLE_X11], ["y"])], [])
44 mkdir -p discover lib/list lib/log lib/pb-protocol lib/system lib/talloc \
45 lib/waiter test ui/common ui/ncurses ui/test ui/twin utils
48 @@ -54,7 +54,7 @@ ui_common_objs = ui/common/discover-clie
50 ncurses_objs = ui/ncurses/nc-scr.o ui/ncurses/nc-menu.o ui/ncurses/nc-ked.o \
52 -twin_objs = ui/twin/pb-twin.o
56 makefiles = Makefile $(top_srcdir)/rules.mk
57 @@ -89,11 +89,17 @@ $(pb_test): $(pb_test_objs)
61 -pb_twin_objs = $(client_objs) $(twin_objs) ui/twin/ps3-twin.o
62 +pb_twin_objs-y$(ENABLE_PS3) += ui/twin/pb-twin.o
63 +pb_twin_objs-$(ENABLE_PS3) += ui/twin/ps3-twin.o ui/common/ps3.o
64 +pb_twin_cflags-$(ENABLE_X11) += -DUSE_X11
65 +pb_twin_ldflags-$(ENABLE_PS3) += -lps3-utils
67 +pb_twin_objs = $(client_objs) $(twin_objs) $(pb_twin_objs-y)
68 $(pb_twin_objs): $(makefiles)
70 -$(pb_twin): LDFLAGS+=$(twin_LDFLAGS) $(LIBTWIN)
71 -$(pb_twin): CFLAGS+=$(twin_CFLAGS)
72 +$(pb_twin): LDFLAGS += $(pb_twin_ldflags-y) $(twin_LDFLAGS) $(LIBTWIN)
73 +$(pb_twin): CFLAGS += $(pb_twin_cflags-y) $(twin_CFLAGS) \
74 + -DPB_ARTWORK_PATH='"$(pkgdatadir)/artwork/"'
76 $(pb_twin): $(pb_twin_objs)
79 +++ b/ui/twin/ps3-twin.c
82 + * Petitboot twin bootloader for the PS3 game console
84 + * Copyright (C) 2009 Sony Computer Entertainment Inc.
85 + * Copyright 2009 Sony Corp.
87 + * This program is free software; you can redistribute it and/or modify
88 + * it under the terms of the GNU General Public License as published by
89 + * the Free Software Foundation; version 2 of the License.
91 + * This program is distributed in the hope that it will be useful,
92 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
93 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
94 + * GNU General Public License for more details.
96 + * You should have received a copy of the GNU General Public License
97 + * along with this program; if not, write to the Free Software
98 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
101 +#if defined(HAVE_CONFIG_H)
112 +#include <sys/time.h>
113 +#include <libtwin/twin.h>
114 +#include <libtwin/twin_fbdev.h>
115 +#include <libtwin/twin_jpeg.h>
116 +#include <libtwin/twin_linux_mouse.h>
117 +#include <libtwin/twin_linux_js.h>
118 +#include <libtwin/twin_png.h>
119 +#if defined(HAVE_LIBTWIN_TWIN_X11_H)
120 +# include <libtwin/twin_x11.h>
122 +#include <linux/input.h>
124 +#include "log/log.h"
125 +#include "talloc/talloc.h"
126 +#include "waiter/waiter.h"
127 +#include "ui/common/discover-client.h"
128 +#include "ui/common/ps3.h"
129 +#include "ui/common/timer.h"
131 +//------------------------------------------------------------------------------
134 +#define DBG(fmt, args...) pb_log("DBG: " fmt, ## args)
135 +#define DBGS(fmt, args...) \
136 + pb_log("DBG:%s:%d: " fmt, __func__, __LINE__, ## args)
138 +/* control to keyboard mappings for the sixaxis controller */
139 +uint8_t sixaxis_map[] = {
144 + KEY_UP, /* 4 Dpad Up */
145 + KEY_RIGHT, /* 5 Dpad Right */
146 + KEY_DOWN, /* 6 Dpad Down */
147 + KEY_LEFT, /* 7 Dpad Left */
152 + 0, /* 12 Triangle */
153 + KEY_ENTER, /* 13 Circle */
155 + KEY_DELETE, /* 15 Square */
156 + 0, /* 16 PS Button */
157 + 0, /* 17 nothing */
158 + 0, /* 18 nothing */
161 +#define PBOOT_LEFT_PANE_SIZE 160
162 +#define PBOOT_LEFT_PANE_COLOR 0x80000000
163 +#define PBOOT_LEFT_LINE_COLOR 0xff000000
165 +#define PBOOT_LEFT_FOCUS_WIDTH 80
166 +#define PBOOT_LEFT_FOCUS_HEIGHT 80
167 +#define PBOOT_LEFT_FOCUS_XOFF 40
168 +#define PBOOT_LEFT_FOCUS_YOFF 40
169 +#define PBOOT_LEFT_FOCUS_XRAD (6 * TWIN_FIXED_ONE)
170 +#define PBOOT_LEFT_FOCUS_YRAD (6 * TWIN_FIXED_ONE)
172 +#define PBOOT_RIGHT_FOCUS_XOFF 20
173 +#define PBOOT_RIGHT_FOCUS_YOFF 60
174 +#define PBOOT_RIGHT_FOCUS_HEIGHT 80
175 +#define PBOOT_RIGHT_FOCUS_XRAD (6 * TWIN_FIXED_ONE)
176 +#define PBOOT_RIGHT_FOCUS_YRAD (6 * TWIN_FIXED_ONE)
178 +#define PBOOT_LEFT_ICON_WIDTH 64
179 +#define PBOOT_LEFT_ICON_HEIGHT 64
180 +#define PBOOT_LEFT_ICON_XOFF 50
181 +#define PBOOT_LEFT_ICON_YOFF 50
182 +#define PBOOT_LEFT_ICON_STRIDE 100
184 +#define PBOOT_RIGHT_OPTION_LMARGIN 30
185 +#define PBOOT_RIGHT_OPTION_RMARGIN 30
186 +#define PBOOT_RIGHT_OPTION_TMARGIN 70
187 +#define PBOOT_RIGHT_OPTION_HEIGHT 64
188 +#define PBOOT_RIGHT_OPTION_STRIDE 100
189 +#define PBOOT_RIGHT_TITLE_TEXT_SIZE (30 * TWIN_FIXED_ONE)
190 +#define PBOOT_RIGHT_SUBTITLE_TEXT_SIZE (18 * TWIN_FIXED_ONE)
191 +#define PBOOT_RIGHT_TITLE_XOFFSET 80
192 +#define PBOOT_RIGHT_TITLE_YOFFSET 30
193 +#define PBOOT_RIGHT_SUBTITLE_XOFFSET 100
194 +#define PBOOT_RIGHT_SUBTITLE_YOFFSET 50
195 +#define PBOOT_RIGHT_BADGE_XOFFSET 2
196 +#define PBOOT_RIGHT_BADGE_YOFFSET 0
199 +#define PBOOT_RIGHT_TITLE_COLOR 0xff000000
200 +#define PBOOT_RIGHT_SUBTITLE_COLOR 0xff400000
202 +#define PBOOT_FOCUS_COLOR 0x10404040
204 +#define PBOOT_STATUS_PANE_COLOR 0x60606060
205 +#define PBOOT_STATUS_PANE_HEIGHT 20
206 +#define PBOOT_STATUS_PANE_XYMARGIN 20
207 +#define PBOOT_STATUS_TEXT_MARGIN 10
208 +#define PBOOT_STATUS_TEXT_SIZE (16 * TWIN_FIXED_ONE)
209 +#define PBOOT_STATUS_TEXT_COLOR 0xff000000
211 +#define PBOOT_MAX_OPTION 100
212 +#define PBOOT_MAX_DEV 10
218 + twin_pixmap_t *badge;
219 + twin_pixmap_t *cache;
227 + twin_pixmap_t *badge;
230 + struct pbt_option options[PBOOT_MAX_OPTION];
235 + pbt_menu_sig = 222,
236 + pbt_pane_sig = 333,
237 + pb_removed_sig = -555,
241 + twin_pixmap_t *pixmap;
248 + struct pbt_cursor cursor;
249 + twin_screen_t *tscreen;
250 +#if defined(HAVE_LIBTWIN_TWIN_X11_H)
253 + twin_fbdev_t *fbdev;
257 + twin_label_t *title;
258 + twin_label_t *help;
259 + twin_label_t *status;
263 + * struct pbt_pane - A twin menu pane.
268 + twin_window_t *window;
269 + twin_rect_t focus_box;
272 + int focus_curindex;
278 + * struct pbt_menu - A twin menu.
279 + * @sig: Sanity check signature.
280 + * @scr: The screen this menu is associated with.
281 + * @dp: The device pane instance.
282 + * @op: The option pane instance.
287 + struct pbt_scr *scr;
288 + struct pbt_pane *dp;
289 + struct pbt_pane *op;
292 +//==============================================================================
294 +//==============================================================================
296 +static struct pbt_scr *pbt_scr_from_tscreen(twin_screen_t *tscreen);
298 +static struct pbt_menu *pbt_menu_from_twindow(twin_window_t *twindow)
300 + struct pbt_menu *menu = twindow->client_data;
303 + assert(menu->sig == pbt_menu_sig);
308 +static struct pbt_menu *pbt_menu_from_arg(void *arg)
310 + struct pbt_menu *menu = arg;
313 + assert(menu->sig == pbt_menu_sig);
318 +static struct pbt_pane *pbt_pane_from_arg(void *arg)
320 + struct pbt_pane *pane = arg;
323 + assert(pane->sig == pbt_pane_sig);
327 +static twin_bool_t pbt_rect_intersect(twin_rect_t r1, twin_rect_t r2)
329 + // FIXME: move this to twin!!!
330 + return !(r1.left > r2.right ||
331 + r1.right < r2.left ||
332 + r1.top > r2.bottom ||
333 + r1.bottom < r2.top);
336 +static twin_pixmap_t * pbt_load_background(twin_screen_t *tscreen)
338 + static const char *bgd = PB_ARTWORK_PATH "/background.jpg";
339 + twin_pixmap_t *rawpix;
340 + twin_pixmap_t *scaledpix;
342 + rawpix = twin_jpeg_to_pixmap(bgd, TWIN_ARGB32);
345 + pb_log("%s: loading image %s failed\n", __func__, bgd);
346 + return twin_make_pattern();
349 + if (tscreen->height == rawpix->height &&
350 + tscreen->width == rawpix->width)
353 + /* Scale as needed. */
355 + twin_fixed_t sx, sy;
356 + twin_operand_t srcop;
358 + scaledpix = twin_pixmap_create(TWIN_ARGB32,
362 + pb_log("%s: scale %s failed\n", __func__, bgd);
363 + twin_pixmap_destroy(rawpix);
364 + return twin_make_pattern();
366 + sx = twin_fixed_div(twin_int_to_fixed(rawpix->width),
367 + twin_int_to_fixed(tscreen->width));
368 + sy = twin_fixed_div(twin_int_to_fixed(rawpix->height),
369 + twin_int_to_fixed(tscreen->height));
371 + twin_matrix_scale(&rawpix->transform, sx, sy);
372 + srcop.source_kind = TWIN_PIXMAP;
373 + srcop.u.pixmap = rawpix;
374 + twin_composite(scaledpix, 0, 0, &srcop, 0, 0,
375 + NULL, 0, 0, TWIN_SOURCE,
376 + tscreen->width, tscreen->height);
378 + twin_pixmap_destroy(rawpix);
382 +//==============================================================================
384 +//==============================================================================
386 +static void pbt_option_execute(struct pbt_menu *menu)
389 + pboot_device_t *dev = pboot_devices[pboot_dev_sel];
390 + pboot_option_t *opt = &dev->options[menu->op->focus_curindex];
392 + pb_log("Selected device %s\n", opt->title);
393 + pboot_message("booting %s...", opt->title);
395 + /* Give user feedback, make sure errors and panics will be seen */
396 + pboot_exec_option(opt->data);
400 +//==============================================================================
402 +//==============================================================================
404 +//==============================================================================
406 +//==============================================================================
408 +static twin_bool_t pbt_scr_event(twin_screen_t *tscreen, twin_event_t *event)
410 + struct pbt_scr *scr = pbt_scr_from_tscreen(tscreen);
412 + switch(event->kind) {
413 + case TwinEventEnter:
414 + case TwinEventMotion:
415 + case TwinEventLeave:
416 + case TwinEventButtonDown:
417 + case TwinEventButtonUp:
418 + if (scr->cursor.pixmap)
419 + twin_screen_set_cursor(tscreen, scr->cursor.pixmap,
420 + scr->cursor.hx, scr->cursor.hy);
422 + case TwinEventJoyButton:
423 + /* map joystick events into key events */
424 + if (event->u.js.control >= sizeof(sixaxis_map))
427 + event->u.key.key = sixaxis_map[event->u.js.control];
428 + if (event->u.js.value == 0) {
429 + event->kind = TwinEventKeyUp;
432 + event->kind = TwinEventKeyDown;
434 + /* fall through.. */
435 + case TwinEventKeyDown:
436 + switch(event->u.key.key) {
439 + case KEY_BACKSPACE:
443 + case TwinEventKeyUp:
444 + twin_screen_set_cursor(tscreen, NULL, 0, 0);
452 +//==============================================================================
454 +//==============================================================================
456 +static int pbt_pane_has_focus(const struct pbt_pane *pane)
458 + return pane->has_focus;
461 +static twin_time_t pbt_pane_timeout(twin_time_t now, void *closure)
463 + const int accel[11] = { 7, 4, 2, 1, 1, 1, 1, 1, 2, 2, 3 };
464 + struct pbt_pane *pane = pbt_pane_from_arg(closure);
465 + int dir = 1, dist, pos;
467 + dist = abs(pane->focus_target - pane->focus_start);
468 + dir = dist > 5 ? 5 : dist;
469 + pos = pane->focus_target - (int)pane->focus_box.top;
477 + twin_window_damage(pane->window,
478 + pane->focus_box.left,
479 + pane->focus_box.top,
480 + pane->focus_box.right,
481 + pane->focus_box.bottom);
483 + pane->focus_box.top += dir;
484 + pane->focus_box.bottom += dir;
486 + twin_window_damage(pane->window,
487 + pane->focus_box.left,
488 + pane->focus_box.top,
489 + pane->focus_box.right,
490 + pane->focus_box.bottom);
492 + twin_window_queue_paint(pane->window);
494 + return accel[(pos * 10) / dist];
497 +//==============================================================================
499 +//==============================================================================
502 + * pbt_menu_set_focus - Set the menu's pane of focus.
503 + * @menu: The menu to operate on.
504 + * @pane: The pane that will have the focus.
507 +static void pbt_menu_set_focus(struct pbt_menu *menu, struct pbt_pane *pane)
509 + assert(!pane->has_focus);
511 + if (pane == menu->dp) {
512 + menu->dp->has_focus = 1;
513 + menu->op->has_focus = 0;
514 + } else if (pane == menu->op) {
515 + menu->dp->has_focus = 0;
516 + menu->op->has_focus = 1;
517 +// pbt_menu_set_option_focus(menu, 0);
519 + assert(0 && "bad logic");
522 +static void pbt_menu_pane_select(struct pbt_menu *menu, struct pbt_pane *pane)
524 + if(pbt_pane_has_focus(pane))
527 + twin_screen_set_active(menu->scr->tscreen, pane->window->pixmap);
529 + twin_window_damage(menu->dp->window,
530 + menu->dp->focus_box.left,
531 + menu->dp->focus_box.top,
532 + menu->dp->focus_box.right,
533 + menu->dp->focus_box.bottom);
534 + twin_window_damage(menu->op->window,
535 + menu->op->focus_box.left,
536 + menu->op->focus_box.top,
537 + menu->op->focus_box.right,
538 + menu->op->focus_box.bottom);
540 + twin_window_queue_paint(menu->dp->window);
541 + twin_window_queue_paint(menu->op->window);
543 + pbt_menu_set_focus(menu, pane);
547 +static struct pbt_pane *pbt_device_pane_create(struct pbt_menu *menu);
548 +static struct pbt_pane *pbt_option_pane_create(struct pbt_menu *menu);
550 +static struct pbt_menu *pbt_menu_create(void *ctx, struct pbt_scr *scr)
552 + struct pbt_menu *menu = talloc_zero(ctx, struct pbt_menu);
557 + assert(scr && scr->sig == pbt_scr_sig);
559 + menu->sig = pbt_menu_sig;
562 + menu->dp = pbt_device_pane_create(menu);
567 + menu->op = pbt_option_pane_create(menu);
581 +//==============================================================================
583 +//==============================================================================
585 +static void pbt_device_pane_draw(twin_window_t *window)
587 + struct pbt_pane *dp = pbt_menu_from_twindow(window)->dp;
588 + twin_pixmap_t *px = window->pixmap;
590 + twin_fixed_t x, y, w, h;
593 + /* Fill background */
594 + twin_fill(px, PBOOT_LEFT_PANE_COLOR, TWIN_SOURCE, 0, 0, px->width,
597 + /* Create a path for use later */
598 + path = twin_path_create();
601 + /* Draw right line if needed */
602 + if (px->clip.right > (PBOOT_LEFT_PANE_SIZE - 4)) {
603 + x = twin_int_to_fixed(PBOOT_LEFT_PANE_SIZE - 4);
604 + y = twin_int_to_fixed(px->height);
605 + twin_path_rectangle(path, x, 0, 0x40000, y);
606 + twin_paint_path(px, PBOOT_LEFT_LINE_COLOR, path);
607 + twin_path_empty(path);
610 + /* Draw focus box */
611 + if (dp->focus_curindex >= 0 &&
612 + pbt_rect_intersect(dp->focus_box, px->clip)) {
613 + x = twin_int_to_fixed(dp->focus_box.left + 2);
614 + y = twin_int_to_fixed(dp->focus_box.top + 2);
615 + w = twin_int_to_fixed(dp->focus_box.right -
616 + dp->focus_box.left - 4);
617 + h = twin_int_to_fixed(dp->focus_box.bottom -
618 + dp->focus_box.top - 4);
619 + twin_path_rounded_rectangle(path, x, y, w, h,
620 + PBOOT_LEFT_FOCUS_XRAD,
621 + PBOOT_LEFT_FOCUS_YRAD);
622 + if (pbt_pane_has_focus(dp))
623 + twin_paint_path(px, PBOOT_FOCUS_COLOR, path);
625 + twin_paint_stroke(px, PBOOT_FOCUS_COLOR, path,
626 + 4 * TWIN_FIXED_ONE);
631 + for (i = 0; i < pboot_dev_count; i++) {
632 + pboot_device_t *dev = pboot_devices[i];
633 + twin_operand_t src;
635 + if (!twin_rect_intersect(dev->box, px->clip))
638 + src.source_kind = TWIN_PIXMAP;
639 + src.u.pixmap = dev->badge;
641 + twin_composite(px, dev->box.left, dev->box.top,
642 + &src, 0, 0, NULL, 0, 0, TWIN_OVER,
643 + dev->box.right - dev->box.left,
644 + dev->box.bottom - dev->box.top);
649 + twin_path_destroy(path);
653 +static void pbt_device_pane_set_focus(struct pbt_menu *menu, int index)
656 + if (index >= pboot_dev_count)
660 + menu->dp->focus_start = menu->dp->focus_box.top;
663 + menu->dp->focus_target = 0 - PBOOT_LEFT_FOCUS_HEIGHT;
665 + menu->dp->focus_target = PBOOT_LEFT_FOCUS_YOFF +
666 + PBOOT_LEFT_ICON_STRIDE * index;
668 + menu->dp->focus_curindex = index;
670 + twin_set_timeout(pbt_pane_timeout, 0, menu->dp);
673 +static void pbt_device_pane_mousetrack(struct pbt_menu *menu, twin_coord_t x,
676 + int candidate = -1;
677 + twin_coord_t icon_top;
679 + if (x < PBOOT_LEFT_ICON_XOFF ||
680 + x > (PBOOT_LEFT_ICON_XOFF + PBOOT_LEFT_ICON_WIDTH))
683 + if (y < PBOOT_LEFT_ICON_YOFF)
686 + candidate = (y - PBOOT_LEFT_ICON_YOFF) / PBOOT_LEFT_ICON_STRIDE;
689 + if (candidate >= pboot_dev_count) {
694 + if (candidate == menu->dp->mouse_target)
697 + icon_top = PBOOT_LEFT_ICON_YOFF + candidate * PBOOT_LEFT_ICON_STRIDE;
699 + if (y > (icon_top + PBOOT_LEFT_ICON_HEIGHT)) {
704 + /* The mouse hit an icon that wasn't the same
705 + * as the previous one, trigger a focus change.
708 + pbt_device_pane_set_focus(menu, candidate);
711 + menu->dp->mouse_target = candidate;
714 +static twin_bool_t pbt_device_pane_event(twin_window_t *window,
715 + twin_event_t *event)
717 + struct pbt_menu *menu = pbt_menu_from_twindow(window);
719 + /* filter out all mouse events */
720 + switch(event->kind) {
721 + case TwinEventEnter:
722 + case TwinEventMotion:
723 + case TwinEventLeave:
724 + pbt_menu_pane_select(menu, menu->dp);
725 + pbt_device_pane_mousetrack(menu, event->u.pointer.x,
726 + event->u.pointer.y);
728 + case TwinEventButtonDown:
729 + case TwinEventButtonUp:
731 + case TwinEventKeyDown:
732 + switch(event->u.key.key) {
734 + if (menu->dp->focus_curindex > 0)
735 + pbt_device_pane_set_focus(menu,
736 + menu->dp->focus_curindex - 1);
739 + pbt_device_pane_set_focus(menu, menu->dp->focus_curindex + 1);
742 + pbt_menu_pane_select(menu, menu->op);
754 +static struct pbt_pane *pbt_device_pane_create(struct pbt_menu *menu)
756 + struct pbt_pane *pane = talloc_zero(menu, struct pbt_pane);
761 + pane->sig = pbt_pane_sig;
762 + pane->window = twin_window_create(menu->scr->tscreen, TWIN_ARGB32,
763 + TwinWindowPlain, 0, 0, PBOOT_LEFT_PANE_SIZE,
764 + menu->scr->tscreen->height);
769 + pane->window->draw = pbt_device_pane_draw;
770 + pane->window->event = pbt_device_pane_event;
771 + pane->window->client_data = menu;
773 + pane->focus_curindex = -1;
774 + pane->focus_box.left = PBOOT_LEFT_FOCUS_XOFF;
775 + pane->focus_box.top = -2 * PBOOT_LEFT_FOCUS_HEIGHT;
776 + pane->focus_box.right = pane->focus_box.left + PBOOT_LEFT_FOCUS_WIDTH;
777 + pane->focus_box.bottom = pane->focus_box.top + PBOOT_LEFT_FOCUS_HEIGHT;
779 + pane->mouse_target = -1;
781 + twin_window_show(pane->window);
782 + twin_window_queue_paint(pane->window);
791 +//==============================================================================
793 +//==============================================================================
795 +static void pbt_option_pane_set_focus(struct pbt_menu *menu, int index)
798 + pboot_device_t *dev;
800 + if (pboot_dev_sel < 0 || pboot_dev_sel >= pboot_dev_count)
802 + dev = pboot_devices[pboot_dev_sel];
803 + if (index < 0 || index >= dev->option_count)
807 + menu->op->focus_start = menu->op->focus_box.top;
808 + menu->op->focus_target = PBOOT_RIGHT_FOCUS_YOFF +
809 + PBOOT_RIGHT_OPTION_STRIDE * index;
810 + menu->op->focus_curindex = index;
812 + twin_set_timeout(pbt_pane_timeout, 0, menu->op);
815 +static void pbt_option_pane_draw(twin_window_t *window)
817 + struct pbt_pane *op = pbt_menu_from_twindow(window)->op;
818 + twin_pixmap_t *px = window->pixmap;
819 +// pboot_device_t *dev;
821 + twin_fixed_t x, y, w, h;
824 + /* Fill background */
825 + twin_fill(px, 0x00000000, TWIN_SOURCE, 0, 0, px->width, px->height);
827 + /* Nothing to draw, return */
828 +// if (pboot_dev_sel < 0)
831 + /* Create a path for use later */
832 + path = twin_path_create();
835 + /* Draw focus box */
836 + if (op->focus_curindex >= 0 &&
837 + pbt_rect_intersect(op->focus_box, px->clip)) {
838 + x = twin_int_to_fixed(op->focus_box.left + 2);
839 + y = twin_int_to_fixed(op->focus_box.top + 2);
840 + w = twin_int_to_fixed(op->focus_box.right -
841 + op->focus_box.left - 4);
842 + h = twin_int_to_fixed(op->focus_box.bottom -
843 + op->focus_box.top - 4);
844 + twin_path_rounded_rectangle(path, x, y, w, h,
845 + PBOOT_RIGHT_FOCUS_XRAD,
846 + PBOOT_RIGHT_FOCUS_YRAD);
847 + if (pbt_pane_has_focus(op))
848 + twin_paint_path(px, PBOOT_FOCUS_COLOR, path);
850 + twin_paint_stroke(px, PBOOT_FOCUS_COLOR, path,
851 + 4 * TWIN_FIXED_ONE);
854 + /* Get device and iterate through options */
856 + dev = pboot_devices[pboot_dev_sel];
857 + for (i = 0; i < dev->option_count; i++) {
858 + pboot_option_t *opt = &dev->options[i];
859 + twin_operand_t src;
861 + if (opt->title == NULL)
863 + if (!pbt_rect_intersect(opt->box, px->clip))
865 + if (opt->cache == NULL)
866 + pboot_draw_option_cache(dev, opt, i);
868 + src.source_kind = TWIN_PIXMAP;
869 + src.u.pixmap = opt->cache;
871 + twin_composite(px, opt->box.left, opt->box.top,
872 + &src, 0, 0, NULL, 0, 0, TWIN_OVER,
873 + opt->box.right - opt->box.left,
874 + opt->box.bottom - opt->box.top);
878 + twin_path_destroy(path);
881 +static void pbt_option_pane_mousetrack(struct pbt_menu *menu, twin_coord_t x,
884 + int candidate = -1;
886 + if (y < PBOOT_RIGHT_OPTION_TMARGIN)
890 + pboot_device_t *dev;
891 + pboot_option_t *opt;
893 + if (pboot_dev_sel < 0 || pboot_dev_sel >= pboot_dev_count)
896 + dev = pboot_devices[pboot_dev_sel];
898 + candidate = (y - PBOOT_RIGHT_OPTION_TMARGIN) /
899 + PBOOT_RIGHT_OPTION_STRIDE;
901 + if (candidate >= dev->option_count) {
906 + if (candidate == op->mouse_target)
909 + opt = &dev->options[candidate];
911 + if (x < opt->box.left || x > opt->box.right ||
912 + y < opt->box.top || y > opt->box.bottom) {
918 + pbt_option_pane_set_focus(menu, candidate);
921 + menu->op->mouse_target = candidate;
924 +static twin_bool_t pbt_option_pane_event(twin_window_t *window,
925 + twin_event_t *event)
927 + struct pbt_menu *menu = pbt_menu_from_twindow(window);
929 + /* filter out all mouse events */
930 + switch(event->kind) {
931 + case TwinEventEnter:
932 + case TwinEventMotion:
933 + case TwinEventLeave:
934 + pbt_menu_pane_select(menu, menu->op);
935 + pbt_option_pane_mousetrack(menu, event->u.pointer.x,
936 + event->u.pointer.y);
938 + case TwinEventButtonDown:
939 + pbt_menu_pane_select(menu, menu->op);
940 + pbt_option_pane_mousetrack(menu, event->u.pointer.x,
941 + event->u.pointer.y);
942 + pbt_option_execute(menu);
944 + case TwinEventButtonUp:
946 + case TwinEventKeyDown:
947 + switch(event->u.key.key) {
949 + //pbt_menu_set_option_focus(menu, menu->op->focus_curindex - 1);
952 + //pbt_menu_set_option_focus(menu, menu->op->focus_curindex + 1);
955 + pbt_menu_pane_select(menu, menu->dp);
958 + pbt_option_execute(menu);
969 +static struct pbt_pane *pbt_option_pane_create(struct pbt_menu *menu)
971 + struct pbt_pane *pane = talloc_zero(menu, struct pbt_pane);
976 + pane->sig = pbt_pane_sig;
977 + pane->window = twin_window_create(menu->scr->tscreen, TWIN_ARGB32,
978 + TwinWindowPlain, PBOOT_LEFT_PANE_SIZE, 0,
979 + menu->scr->tscreen->width - PBOOT_LEFT_PANE_SIZE,
980 + menu->scr->tscreen->height);
985 + pane->window->draw = pbt_option_pane_draw;
986 + pane->window->event = pbt_option_pane_event;
987 + pane->window->client_data = menu;
989 + pane->focus_curindex = -1;
990 + pane->focus_box.left = PBOOT_RIGHT_FOCUS_XOFF;
991 + pane->focus_box.top = -2 * PBOOT_RIGHT_FOCUS_HEIGHT;
992 + pane->focus_box.right = pane->window->pixmap->width
993 + - 2 * PBOOT_RIGHT_FOCUS_XOFF;
994 + pane->focus_box.bottom = pane->focus_box.top + PBOOT_RIGHT_FOCUS_HEIGHT;
996 + pane->mouse_target = -1;
998 + twin_window_show(pane->window);
999 + twin_window_queue_paint(pane->window);
1004 + talloc_free(pane);
1008 +//==============================================================================
1010 +//==============================================================================
1014 +static pboot_device_t *pboot_devices[PBOOT_MAX_DEV];
1015 +static int pboot_dev_count;
1016 +static int pboot_dev_sel = -1;
1021 +static int pboot_vmode_change = -1;
1023 +static void pboot_message(const char *fmt, ...)
1028 + va_start(ap, fmt);
1029 + vasprintf(&msg, fmt, ap);
1035 +static void pboot_draw_option_cache(pboot_device_t *dev, pboot_option_t *opt,
1038 + twin_pixmap_t *px;
1039 + twin_path_t *path;
1040 + twin_fixed_t tx, ty;
1042 + /* Create pixmap */
1043 + px = twin_pixmap_create(TWIN_ARGB32, opt->box.right - opt->box.left,
1044 + opt->box.bottom - opt->box.top);
1048 + /* Fill background */
1049 + twin_fill(px, 0x00000000, TWIN_SOURCE, 0, 0, px->width, px->height);
1051 + /* Allocate a path for drawing */
1052 + path = twin_path_create();
1056 + /* TEST - Bounding rectangle */
1057 + twin_path_rectangle(path, 0, 0,
1058 + twin_int_to_fixed(px->width),
1059 + twin_int_to_fixed(px->height));
1060 + twin_paint_path(px, PBOOT_RIGHT_TITLE_COLOR, path);
1061 + twin_path_empty(path);
1062 + twin_fill(px, 0x00000000, TWIN_SOURCE, 2, 2,
1063 + px->width - 3, px->height - 3);
1067 + twin_path_set_font_size(path, PBOOT_RIGHT_TITLE_TEXT_SIZE);
1068 + twin_path_set_font_style(path, TWIN_TEXT_UNHINTED);
1069 + tx = twin_int_to_fixed(PBOOT_RIGHT_TITLE_XOFFSET);
1070 + ty = twin_int_to_fixed(PBOOT_RIGHT_TITLE_YOFFSET);
1071 + twin_path_move (path, tx, ty);
1072 + twin_path_utf8 (path, opt->title);
1073 + twin_paint_path (px, PBOOT_RIGHT_TITLE_COLOR, path);
1074 + twin_path_empty (path);
1076 + if (opt->subtitle) {
1077 + twin_path_set_font_size(path, PBOOT_RIGHT_SUBTITLE_TEXT_SIZE);
1078 + twin_path_set_font_style(path, TWIN_TEXT_UNHINTED);
1079 + tx = twin_int_to_fixed(PBOOT_RIGHT_SUBTITLE_XOFFSET);
1080 + ty = twin_int_to_fixed(PBOOT_RIGHT_SUBTITLE_YOFFSET);
1081 + twin_path_move (path, tx, ty);
1082 + twin_path_utf8 (path, opt->subtitle);
1083 + twin_paint_path (px, PBOOT_RIGHT_SUBTITLE_COLOR, path);
1084 + twin_path_empty (path);
1088 + twin_operand_t src;
1090 + src.source_kind = TWIN_PIXMAP;
1091 + src.u.pixmap = opt->badge;
1093 + twin_composite(px, PBOOT_RIGHT_BADGE_XOFFSET,
1094 + PBOOT_RIGHT_BADGE_YOFFSET,
1095 + &src, 0, 0, NULL, 0, 0, TWIN_OVER,
1096 + opt->badge->width, opt->badge->height);
1100 + /* Destroy path */
1101 + twin_path_destroy(path);
1106 +static int pboot_add_option(int devindex, const char *title,
1107 + const char *subtitle, twin_pixmap_t *badge, void *data)
1109 + pboot_device_t *dev;
1110 + pboot_option_t *opt;
1111 + twin_coord_t width;
1113 + struct pbt_menu *menu = NULL;
1115 + if (devindex < 0 || devindex >= pboot_dev_count)
1117 + dev = pboot_devices[devindex];
1119 + if (dev->option_count >= PBOOT_MAX_OPTION)
1121 + index = dev->option_count++;
1122 + opt = &dev->options[index];
1124 + opt->title = malloc(strlen(title) + 1);
1125 + strcpy(opt->title, title);
1128 + opt->subtitle = malloc(strlen(subtitle) + 1);
1129 + strcpy(opt->subtitle, subtitle);
1131 + opt->subtitle = NULL;
1133 + opt->badge = badge;
1134 + opt->cache = NULL;
1136 + width = menu->op->window->pixmap->width -
1137 + (PBOOT_RIGHT_OPTION_LMARGIN + PBOOT_RIGHT_OPTION_RMARGIN);
1139 + opt->box.left = PBOOT_RIGHT_OPTION_LMARGIN;
1140 + opt->box.right = opt->box.left + width;
1141 + opt->box.top = PBOOT_RIGHT_OPTION_TMARGIN +
1142 + index * PBOOT_RIGHT_OPTION_STRIDE;
1143 + opt->box.bottom = opt->box.top + PBOOT_RIGHT_OPTION_HEIGHT;
1149 +static void pboot_set_device_select(struct pbt_menu *menu, int sel, int force)
1151 + pb_log("%s: %d -> %d\n", __FUNCTION__, pboot_dev_sel, sel);
1152 + if (!force && sel == pboot_dev_sel)
1154 + if (sel >= pboot_dev_count)
1156 + pboot_dev_sel = sel;
1158 + menu->dp->focus_curindex = sel;
1160 + menu->dp->focus_target = 0 - PBOOT_LEFT_FOCUS_HEIGHT;
1162 + menu->dp->focus_target = PBOOT_LEFT_FOCUS_YOFF +
1163 + PBOOT_LEFT_ICON_STRIDE * sel;
1164 + menu->op->focus_box.bottom = menu->dp->focus_target;
1165 + menu->op->focus_box.bottom = menu->op->focus_box.top +
1166 + PBOOT_RIGHT_FOCUS_HEIGHT;
1167 + twin_window_damage(menu->dp->window,
1169 + menu->dp->window->pixmap->width,
1170 + menu->dp->window->pixmap->height);
1171 + twin_window_queue_paint(menu->dp->window);
1173 + menu->op->focus_curindex = -1;
1174 + menu->op->mouse_target = -1;
1175 + menu->op->focus_box.top = -2*PBOOT_RIGHT_FOCUS_HEIGHT;
1176 + menu->op->focus_box.bottom = menu->op->focus_box.top +
1177 + PBOOT_RIGHT_FOCUS_HEIGHT;
1178 + twin_window_damage(menu->op->window, 0, 0,
1179 + menu->op->window->pixmap->width,
1180 + menu->op->window->pixmap->height);
1181 + twin_window_queue_paint(menu->op->window);
1184 +static void pboot_quit(void)
1190 +static int pboot_add_device(const char *dev_id, twin_pixmap_t *pixmap)
1193 + pboot_device_t *dev;
1195 + struct pbt_menu *menu = NULL;
1197 + if (pboot_dev_count >= PBOOT_MAX_DEV)
1200 + index = pboot_dev_count++;
1202 + dev = malloc(sizeof(*dev));
1203 + memset(dev, 0, sizeof(*dev));
1204 + dev->id = malloc(strlen(dev_id) + 1);
1205 + strcpy(dev->id, dev_id);
1206 + dev->badge = pixmap;
1207 + dev->box.left = PBOOT_LEFT_ICON_XOFF;
1208 + dev->box.right = dev->box.left + PBOOT_LEFT_ICON_WIDTH;
1209 + dev->box.top = PBOOT_LEFT_ICON_YOFF +
1210 + PBOOT_LEFT_ICON_STRIDE * index;
1211 + dev->box.bottom = dev->box.top + PBOOT_LEFT_ICON_HEIGHT;
1213 + pboot_devices[index] = dev;
1215 + twin_window_damage(menu->dp->window,
1216 + dev->box.left, dev->box.top,
1217 + dev->box.right, dev->box.bottom);
1218 + twin_window_queue_paint(menu->dp->window);
1223 +static int pboot_remove_device(const char *dev_id)
1225 + pboot_device_t *dev = NULL;
1226 + int i, newsel = pboot_dev_sel;
1228 + struct pbt_menu *menu = NULL;
1230 + /* find the matching device */
1231 + for (i = 0; i < pboot_dev_count; i++) {
1232 + if (!strcmp(pboot_devices[i]->id, dev_id)) {
1233 + dev = pboot_devices[i];
1239 + return TWIN_FALSE;
1241 + memmove(pboot_devices + i, pboot_devices + i + 1,
1242 + sizeof(*pboot_devices) * (pboot_dev_count + i - 1));
1243 + pboot_devices[--pboot_dev_count] = NULL;
1245 + /* select the newly-focussed device */
1246 + if (pboot_dev_sel > i)
1247 + newsel = pboot_dev_sel - 1;
1248 + else if (pboot_dev_sel == i && i >= pboot_dev_count)
1249 + newsel = pboot_dev_count - 1;
1250 + pboot_set_device_select(menu, newsel, 1);
1252 + /* todo: free device & options */
1258 +//------------------------------------------------------------------------------
1260 +static void print_version(void)
1262 + printf("pb-twin (" PACKAGE_NAME ") " PACKAGE_VERSION "\n");
1265 +static void print_usage(void)
1269 +"Usage: pb-twin [-h, --help] [-l, --log log-file] [-r, --reset-defaults]\n"
1270 +" [-t, --timeout] [-V, --version]\n");
1274 + * enum opt_value - Tri-state options variables.
1277 +enum opt_value {opt_undef = 0, opt_yes, opt_no};
1280 + * struct opts - Values from command line options.
1284 + enum opt_value show_help;
1285 + const char *log_file;
1286 + enum opt_value reset_defaults;
1287 + enum opt_value use_timeout;
1288 + enum opt_value show_version;
1292 + * opts_parse - Parse the command line options.
1295 +static int opts_parse(struct opts *opts, int argc, char *argv[])
1297 + static const struct option long_options[] = {
1298 + {"help", no_argument, NULL, 'h'},
1299 + {"log", required_argument, NULL, 'l'},
1300 + {"reset-defaults", no_argument, NULL, 'r'},
1301 + {"timeout", no_argument, NULL, 't'},
1302 + {"version", no_argument, NULL, 'V'},
1303 + { NULL, 0, NULL, 0},
1305 + static const char short_options[] = "hl:trV";
1306 + static const struct opts default_values = {
1307 + .log_file = "pb-twin.log",
1310 + *opts = default_values;
1313 + int c = getopt_long(argc, argv, short_options, long_options,
1321 + opts->show_help = opt_yes;
1324 + opts->log_file = optarg;
1327 + opts->use_timeout = opt_yes;
1330 + opts->reset_defaults = opt_yes;
1333 + opts->show_version = opt_yes;
1336 + opts->show_help = opt_yes;
1341 + return optind != argc;
1345 + * struct ps3_gui - Main gui program instance.
1350 + struct ui_timer timer;
1351 + struct ps3_flash_values values;
1354 + struct pbt_scr scr;
1355 + struct pbt_frame frame;
1356 + struct pbt_menu *menu;
1359 +static struct ps3_gui ps3;
1361 +static struct pbt_scr *pbt_scr_from_tscreen(twin_screen_t *tscreen)
1363 + assert(ps3.scr.sig == pbt_scr_sig);
1364 + assert(ps3.scr.tscreen == tscreen);
1368 +static void sig_handler(int signum)
1370 + DBGS("%d\n", signum);
1374 + ui_timer_sigalrm(&ps3.timer);
1378 +// gui_resize(ps3.gui);
1381 + assert(0 && "unknown sig");
1382 + /* fall through */
1386 + exit(EXIT_FAILURE);
1388 +// gui_abort(ps3.gui);
1394 + * main - twin bootloader main routine.
1397 +int main(int argc, char *argv[])
1399 + static struct sigaction sa;
1400 + static struct opts opts;
1403 + unsigned int mode;
1406 + result = opts_parse(&opts, argc, argv);
1410 + return EXIT_FAILURE;
1413 + if (opts.show_help == opt_yes) {
1415 + return EXIT_SUCCESS;
1418 + if (opts.show_version == opt_yes) {
1420 + return EXIT_SUCCESS;
1423 + log = fopen(opts.log_file, "a");
1425 + pb_log_set_stream(log);
1428 + pb_log_always_flush(1);
1431 + pb_log("--- pb-twin ---\n");
1433 + sa.sa_handler = sig_handler;
1434 + result = sigaction(SIGALRM, &sa, NULL);
1435 + result += sigaction(SIGHUP, &sa, NULL);
1436 + result += sigaction(SIGINT, &sa, NULL);
1437 + result += sigaction(SIGTERM, &sa, NULL);
1438 + result += sigaction(SIGWINCH, &sa, NULL);
1441 + pb_log("%s sigaction failed.\n", __func__);
1442 + return EXIT_FAILURE;
1445 + ps3.values = ps3_flash_defaults;
1447 + if (opts.reset_defaults != opt_yes)
1448 + ps3.dirty_values = ps3_flash_get_values(&ps3.values);
1450 + twin_feature_init(); // need it???
1452 + /* Setup screen. */
1454 + ps3.scr.sig = pbt_scr_sig;
1456 +#if defined(HAVE_LIBTWIN_TWIN_X11_H)
1457 +# if defined(USE_X11)
1462 + ps3.scr.x11 = twin_x11_create(XOpenDisplay(0), 1024, 768);
1464 + if (!ps3.scr.x11) {
1465 + perror("failed to create x11 screen !\n");
1466 + return EXIT_FAILURE;
1469 + ps3.scr.tscreen = ps3.scr.x11->screen;
1474 + result = ps3_get_video_mode(&mode);
1476 + /* Current becomes default if ps3_flash_get_values() failed. */
1478 + if (ps3.dirty_values && !result)
1479 + ps3.values.video_mode = mode;
1481 + /* Set mode if not at default. */
1483 + if (!result && (ps3.values.video_mode != (uint16_t)mode))
1484 + ps3_set_video_mode(ps3.values.video_mode);
1486 + ps3.scr.fbdev = twin_fbdev_create(-1, SIGUSR1);
1488 + if (!ps3.scr.fbdev) {
1489 + perror("failed to create fbdev screen !\n");
1490 + return EXIT_FAILURE;
1493 + ps3.scr.tscreen = ps3.scr.fbdev->screen;
1496 + ps3.scr.tscreen->event_filter = pbt_scr_event;
1498 + twin_screen_set_background(ps3.scr.tscreen,
1499 + pbt_load_background(ps3.scr.tscreen));
1503 + ps3.menu = pbt_menu_create(NULL, &ps3.scr);
1505 + pbt_device_pane_set_focus(ps3.menu, 0);
1506 + twin_screen_set_active(ps3.scr.tscreen, ps3.menu->dp->window->pixmap);
1508 + /* Console switch */
1510 + if (ps3.scr.fbdev)
1511 + twin_fbdev_activate(ps3.scr.fbdev);
1515 +// twin_toplevel_show(ps3.toplevel);
1518 + pb_log("--- end ---\n");
1520 + return ui_result ? EXIT_FAILURE : EXIT_SUCCESS;