]> git.ozlabs.org Git - petitboot/blob - petitboot.c
dfb293cbcd1bbf5f3bfd14f34db30450dc82a792
[petitboot] / petitboot.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <signal.h>
5 #include <unistd.h>
6 #include <syscall.h>
7 #include <assert.h>
8
9 #include <linux/input.h>
10
11 #undef _USE_X11
12
13 #include <libtwin/twin.h>
14 #include <libtwin/twin_linux_mouse.h>
15 #include <libtwin/twin_png.h>
16
17 #include "petitboot.h"
18 #include "petitboot-paths.h"
19
20 #ifdef _USE_X11
21 #include <libtwin/twin_x11.h>
22 static twin_x11_t *pboot_x11;
23 #else
24 #include <libtwin/twin_fbdev.h>
25 static twin_fbdev_t *pboot_fbdev;
26 #endif
27
28 static twin_screen_t *pboot_screen;
29
30 #define PBOOT_LEFT_PANE_SIZE            200
31 #define PBOOT_LEFT_PANE_COLOR           0x80000000
32 #define PBOOT_LEFT_LINE_COLOR           0xff000000
33
34 #define PBOOT_LEFT_FOCUS_WIDTH          80
35 #define PBOOT_LEFT_FOCUS_HEIGHT         80
36 #define PBOOT_LEFT_FOCUS_XOFF           60
37 #define PBOOT_LEFT_FOCUS_YOFF           60
38 #define PBOOT_LEFT_FOCUS_XRAD           (6 * TWIN_FIXED_ONE)
39 #define PBOOT_LEFT_FOCUS_YRAD           (6 * TWIN_FIXED_ONE)
40
41 #define PBOOT_RIGHT_FOCUS_XOFF          20
42 #define PBOOT_RIGHT_FOCUS_YOFF          60
43 #define PBOOT_RIGHT_FOCUS_HEIGHT        80
44 #define PBOOT_RIGHT_FOCUS_XRAD          (6 * TWIN_FIXED_ONE)
45 #define PBOOT_RIGHT_FOCUS_YRAD          (6 * TWIN_FIXED_ONE)
46
47 #define PBOOT_LEFT_ICON_WIDTH           64
48 #define PBOOT_LEFT_ICON_HEIGHT          64
49 #define PBOOT_LEFT_ICON_XOFF            70
50 #define PBOOT_LEFT_ICON_YOFF            70
51 #define PBOOT_LEFT_ICON_STRIDE          100
52
53 #define PBOOT_RIGHT_OPTION_LMARGIN      30
54 #define PBOOT_RIGHT_OPTION_RMARGIN      30
55 #define PBOOT_RIGHT_OPTION_TMARGIN      70
56 #define PBOOT_RIGHT_OPTION_HEIGHT       64
57 #define PBOOT_RIGHT_OPTION_STRIDE       100
58 #define PBOOT_RIGHT_TITLE_TEXT_SIZE     (30 * TWIN_FIXED_ONE)
59 #define PBOOT_RIGHT_SUBTITLE_TEXT_SIZE  (18 * TWIN_FIXED_ONE)
60 #define PBOOT_RIGHT_TITLE_XOFFSET       80
61 #define PBOOT_RIGHT_TITLE_YOFFSET       30
62 #define PBOOT_RIGHT_SUBTITLE_XOFFSET    200
63 #define PBOOT_RIGHT_SUBTITLE_YOFFSET    50
64 #define PBOOT_RIGHT_BADGE_XOFFSET       2
65 #define PBOOT_RIGHT_BADGE_YOFFSET       0
66
67
68 #define PBOOT_RIGHT_TITLE_COLOR         0xff000000
69 #define PBOOT_RIGHT_SUBTITLE_COLOR      0xff400000
70
71 #define PBOOT_FOCUS_COLOR               0x10404040
72
73
74 typedef struct _pboot_option pboot_option_t;
75 typedef struct _pboot_device pboot_device_t;
76
77 struct _pboot_option
78 {
79         char            *title;
80         char            *subtitle;
81         twin_pixmap_t   *badge;
82         twin_pixmap_t   *cache;
83         twin_rect_t     box;
84         void            *data;
85 };
86
87 struct _pboot_device
88 {
89         char                    *id;
90         twin_pixmap_t           *badge;
91         twin_rect_t             box;
92         int                     option_count;
93         pboot_option_t          options[PBOOT_MAX_OPTION];
94 };
95
96 static twin_pixmap_t    *pboot_cursor;
97 static int              pboot_cursor_hx;
98 static int              pboot_cursor_hy;
99
100 static pboot_device_t   *pboot_devices[PBOOT_MAX_DEV];
101 static int              pboot_dev_count;
102 static int              pboot_dev_sel = -1;
103 static int              pboot_focus_lpane = 1;
104
105 typedef struct _pboot_lpane {
106         twin_window_t   *window;
107         twin_rect_t     focus_box;
108         int             focus_start;
109         int             focus_target;
110         int             focus_curindex;
111         int             mouse_target;
112 } pboot_lpane_t;
113
114 typedef struct _pboot_rpane {
115         twin_window_t   *window;
116         twin_rect_t     focus_box;
117         int             focus_start;
118         int             focus_target;
119         int             focus_curindex;
120         int             mouse_target;
121 } pboot_rpane_t;
122
123 static pboot_lpane_t    *pboot_lpane;
124 static pboot_rpane_t    *pboot_rpane;
125
126 /* XXX move to twin */
127 static inline twin_bool_t twin_rect_intersect(twin_rect_t r1,
128                                               twin_rect_t r2)
129 {
130         return !(r1.left > r2.right ||
131                  r1.right < r2.left ||
132                  r1.top > r2.bottom ||
133                  r1.bottom < r2.top);
134 }
135
136 static void pboot_draw_option_cache(pboot_device_t *dev, pboot_option_t *opt,
137                                     int index)
138 {
139         twin_pixmap_t   *px;
140         twin_path_t     *path;
141         twin_fixed_t    tx, ty;
142
143         /* Create pixmap */
144         px = twin_pixmap_create(TWIN_ARGB32, opt->box.right - opt->box.left,
145                                  opt->box.bottom - opt->box.top);
146         assert(px);
147         opt->cache = px;
148
149         /* Fill background */
150         twin_fill(px, 0x00000000, TWIN_SOURCE, 0, 0, px->width, px->height);
151
152         /* Allocate a path for drawing */
153         path = twin_path_create();
154         assert(path);
155
156 #if 0
157         /* TEST - Bounding rectangle */
158         twin_path_rectangle(path, 0, 0,
159                             twin_int_to_fixed(px->width),
160                             twin_int_to_fixed(px->height));
161         twin_paint_path(px, PBOOT_RIGHT_TITLE_COLOR, path);
162         twin_path_empty(path);
163         twin_fill(px, 0x00000000, TWIN_SOURCE, 2, 2,
164                   px->width - 3, px->height - 3);
165 #endif
166
167         /* Draw texts */
168         twin_path_set_font_size(path, PBOOT_RIGHT_TITLE_TEXT_SIZE);
169         twin_path_set_font_style(path, TWIN_TEXT_UNHINTED);
170         tx = twin_int_to_fixed(PBOOT_RIGHT_TITLE_XOFFSET);
171         ty = twin_int_to_fixed(PBOOT_RIGHT_TITLE_YOFFSET);
172         twin_path_move (path, tx, ty);
173         twin_path_utf8 (path, opt->title);
174         twin_paint_path (px, PBOOT_RIGHT_TITLE_COLOR, path);
175         twin_path_empty (path);
176
177         if (opt->subtitle) {
178                 twin_path_set_font_size(path, PBOOT_RIGHT_SUBTITLE_TEXT_SIZE);
179                 twin_path_set_font_style(path, TWIN_TEXT_UNHINTED);
180                 tx = twin_int_to_fixed(PBOOT_RIGHT_SUBTITLE_XOFFSET);
181                 ty = twin_int_to_fixed(PBOOT_RIGHT_SUBTITLE_YOFFSET);
182                 twin_path_move (path, tx, ty);
183                 twin_path_utf8 (path, opt->subtitle);
184                 twin_paint_path (px, PBOOT_RIGHT_SUBTITLE_COLOR, path);
185                 twin_path_empty (path);
186         }
187
188         if (opt->badge) {
189                 twin_operand_t  src;
190
191                 src.source_kind = TWIN_PIXMAP;
192                 src.u.pixmap = opt->badge;
193
194                 twin_composite(px, PBOOT_RIGHT_BADGE_XOFFSET,
195                                PBOOT_RIGHT_BADGE_YOFFSET,
196                                &src, 0, 0, NULL, 0, 0, TWIN_OVER,
197                                opt->badge->width, opt->badge->height);
198         }
199
200
201         /* Destroy path */
202         twin_path_destroy(path);
203 }
204
205 static void pboot_rpane_draw(twin_window_t *window)
206 {
207         twin_pixmap_t   *px = window->pixmap;
208         pboot_rpane_t   *rpane = window->client_data;
209         pboot_device_t  *dev;
210         twin_path_t     *path;
211         twin_fixed_t    x, y, w, h;
212         int             i;
213
214         /* Fill background */
215         twin_fill(px, 0x00000000, TWIN_SOURCE, 0, 0, px->width, px->height);
216
217         /* Nothing to draw, return */
218         if (pboot_dev_sel < 0)
219                 return;
220
221         /* Create a path for use later */
222         path = twin_path_create();
223         assert(path);
224
225         /* Draw focus box */
226         if (rpane->focus_curindex >= 0 &&
227             twin_rect_intersect(rpane->focus_box, px->clip)) {
228                 x = twin_int_to_fixed(rpane->focus_box.left + 2);
229                 y = twin_int_to_fixed(rpane->focus_box.top + 2);
230                 w = twin_int_to_fixed(rpane->focus_box.right -
231                                       rpane->focus_box.left - 4);
232                 h = twin_int_to_fixed(rpane->focus_box.bottom -
233                                       rpane->focus_box.top - 4);
234                 twin_path_rounded_rectangle(path, x, y, w, h,
235                                             PBOOT_RIGHT_FOCUS_XRAD,
236                                             PBOOT_RIGHT_FOCUS_YRAD);
237                 if (!pboot_focus_lpane)
238                         twin_paint_path(px, PBOOT_FOCUS_COLOR, path);
239                 else
240                         twin_paint_stroke(px, PBOOT_FOCUS_COLOR, path,
241                                           4 * TWIN_FIXED_ONE);
242         }
243
244         /* Get device and iterate through options */
245         dev = pboot_devices[pboot_dev_sel];
246         for (i = 0; i < dev->option_count; i++) {
247                 pboot_option_t  *opt = &dev->options[i];
248                 twin_operand_t  src;
249
250                 if (opt->title == NULL)
251                         continue;
252                 if (!twin_rect_intersect(opt->box, px->clip))
253                         continue;
254                 if (opt->cache == NULL)
255                         pboot_draw_option_cache(dev, opt, i);
256
257                 src.source_kind = TWIN_PIXMAP;
258                 src.u.pixmap = opt->cache;
259
260                 twin_composite(px, opt->box.left, opt->box.top,
261                                &src, 0, 0, NULL, 0, 0, TWIN_OVER,
262                                opt->box.right - opt->box.left,
263                                opt->box.bottom - opt->box.top);
264         }
265
266         /* Destroy path */
267         twin_path_destroy(path);
268 }
269
270 static twin_time_t pboot_rfocus_timeout (twin_time_t now, void *closure)
271 {
272         int dir = 1, dist, pos;
273         const int accel[11] = { 7, 4, 2, 1, 1, 1, 1, 2, 3, 4, 5 };
274
275         dist = abs(pboot_rpane->focus_target - pboot_rpane->focus_start);
276         dir = dist > 5 ? 5 : dist;
277         pos = pboot_rpane->focus_target - (int)pboot_rpane->focus_box.top;
278         if (pos == 0) {
279                 return -1;
280         }
281         if (pos < 0) {
282                 dir = -dir;
283                 pos = -pos;
284         }
285         twin_window_damage(pboot_rpane->window,
286                            pboot_rpane->focus_box.left,
287                            pboot_rpane->focus_box.top,
288                            pboot_rpane->focus_box.right,
289                            pboot_rpane->focus_box.bottom);
290
291         pboot_rpane->focus_box.top += dir;
292         pboot_rpane->focus_box.bottom += dir;
293
294         twin_window_damage(pboot_rpane->window,
295                            pboot_rpane->focus_box.left,
296                            pboot_rpane->focus_box.top,
297                            pboot_rpane->focus_box.right,
298                            pboot_rpane->focus_box.bottom);
299
300         twin_window_queue_paint(pboot_rpane->window);
301
302         return accel[(pos * 10) / dist];
303 }
304
305 static void pboot_set_rfocus(int index)
306 {
307         pboot_device_t  *dev;
308
309         if (pboot_dev_sel < 0 || pboot_dev_sel >= pboot_dev_count)
310                 return;
311         dev = pboot_devices[pboot_dev_sel];
312         if (index < 0 || index >= dev->option_count)
313                 return;
314
315         pboot_rpane->focus_start = pboot_rpane->focus_box.top;
316         pboot_rpane->focus_target = PBOOT_RIGHT_FOCUS_YOFF +
317                 PBOOT_RIGHT_OPTION_STRIDE * index;
318         pboot_rpane->focus_curindex = index;
319
320         twin_set_timeout(pboot_rfocus_timeout, 0, NULL);
321 }
322
323 static void pboot_select_rpane(void)
324 {
325         if (pboot_focus_lpane == 0)
326                 return;
327         pboot_focus_lpane = 0;
328
329         twin_screen_set_active(pboot_screen, pboot_rpane->window->pixmap);
330
331         twin_window_damage(pboot_lpane->window,
332                            pboot_lpane->focus_box.left,
333                            pboot_lpane->focus_box.top,
334                            pboot_lpane->focus_box.right,
335                            pboot_lpane->focus_box.bottom);
336
337         twin_window_damage(pboot_rpane->window,
338                            pboot_rpane->focus_box.left,
339                            pboot_rpane->focus_box.top,
340                            pboot_rpane->focus_box.right,
341                            pboot_rpane->focus_box.bottom);
342
343         twin_window_queue_paint(pboot_lpane->window);
344         twin_window_queue_paint(pboot_rpane->window);
345
346         pboot_set_rfocus(0);
347 }
348
349 static void pboot_select_lpane(void)
350 {
351         if (pboot_focus_lpane == 1)
352                 return;
353         pboot_focus_lpane = 1;
354
355         twin_screen_set_active(pboot_screen, pboot_lpane->window->pixmap);
356
357         twin_window_damage(pboot_lpane->window,
358                            pboot_lpane->focus_box.left,
359                            pboot_lpane->focus_box.top,
360                            pboot_lpane->focus_box.right,
361                            pboot_lpane->focus_box.bottom);
362
363         twin_window_damage(pboot_rpane->window,
364                            pboot_rpane->focus_box.left,
365                            pboot_rpane->focus_box.top,
366                            pboot_rpane->focus_box.right,
367                            pboot_rpane->focus_box.bottom);
368
369         twin_window_queue_paint(pboot_lpane->window);
370         twin_window_queue_paint(pboot_rpane->window);
371 }
372
373 static void pboot_rpane_mousetrack(twin_coord_t x, twin_coord_t y)
374 {
375         pboot_device_t  *dev;
376         pboot_option_t  *opt;
377         int             candidate = -1;
378
379         if (pboot_dev_sel < 0 || pboot_dev_sel >= pboot_dev_count)
380                 return;
381         dev = pboot_devices[pboot_dev_sel];
382
383         if (y < PBOOT_RIGHT_OPTION_TMARGIN)
384                 goto miss;
385         candidate = (y - PBOOT_RIGHT_OPTION_TMARGIN) /
386                 PBOOT_RIGHT_OPTION_STRIDE;
387         if (candidate >= dev->option_count) {
388                 candidate = -1;
389                 goto miss;
390         }
391         if (candidate == pboot_rpane->mouse_target)
392                 return;
393         opt = &dev->options[candidate];
394         if (x < opt->box.left || x > opt->box.right ||
395             y < opt->box.top || y > opt->box.bottom) {
396                 candidate = -1;
397                 goto miss;
398         }
399
400         /* Ok, so now, we know the mouse hit an icon that wasn't the same
401          * as the previous one, we trigger a focus change
402          */
403         pboot_set_rfocus(candidate);
404
405  miss:
406         pboot_rpane->mouse_target = candidate;
407 }
408
409 static void pboot_choose_option(void)
410 {
411         pboot_device_t *dev = pboot_devices[pboot_dev_sel];
412         pboot_option_t *opt = &dev->options[pboot_rpane->focus_curindex];
413
414         LOG("Selected device %s\n", opt->title);
415
416         /* Give user feedback, make sure errors and panics will be seen */
417         pboot_exec_option(opt->data);
418 }
419
420 static twin_bool_t pboot_rpane_event (twin_window_t         *window,
421                                       twin_event_t          *event)
422 {
423         /* filter out all mouse events */
424         switch(event->kind) {
425         case TwinEventEnter:
426         case TwinEventMotion:
427         case TwinEventLeave:
428                 pboot_select_rpane();
429                 pboot_rpane_mousetrack(event->u.pointer.x, event->u.pointer.y);
430                 return TWIN_TRUE;
431         case TwinEventButtonDown:
432                 pboot_select_rpane();
433                 pboot_rpane_mousetrack(event->u.pointer.x, event->u.pointer.y);
434                 pboot_choose_option();
435         case TwinEventButtonUp:
436                 return TWIN_TRUE;
437         case TwinEventKeyDown:
438                 switch(event->u.key.key) {
439                 case KEY_UP:
440                         pboot_set_rfocus(pboot_rpane->focus_curindex - 1);
441                         return TWIN_TRUE;
442                 case KEY_DOWN:
443                         pboot_set_rfocus(pboot_rpane->focus_curindex + 1);
444                         return TWIN_TRUE;
445                 case KEY_LEFT:
446                         pboot_select_lpane();
447                         return TWIN_TRUE;
448                 case KEY_ENTER:
449                         pboot_choose_option();
450                 default:
451                         break;
452                 }
453                 break;
454         default:
455                 break;
456         }
457         return TWIN_FALSE;
458 }
459
460
461 int pboot_add_option(int devindex, const char *title,
462                      const char *subtitle, twin_pixmap_t *badge, void *data)
463 {
464         pboot_device_t  *dev;
465         pboot_option_t  *opt;
466         twin_coord_t    width;
467         int             index;
468
469         if (devindex < 0 || devindex >= pboot_dev_count)
470                 return -1;
471         dev = pboot_devices[devindex];
472
473         if (dev->option_count >= PBOOT_MAX_OPTION)
474                 return -1;
475         index = dev->option_count++;
476         opt = &dev->options[index];
477
478         opt->title = malloc(strlen(title) + 1);
479         strcpy(opt->title, title);
480
481         if (subtitle) {
482                 opt->subtitle = malloc(strlen(subtitle) + 1);
483                 strcpy(opt->subtitle, subtitle);
484         } else
485                 opt->subtitle = NULL;
486
487         opt->badge = badge;
488         opt->cache = NULL;
489
490         width = pboot_rpane->window->pixmap->width -
491                 (PBOOT_RIGHT_OPTION_LMARGIN + PBOOT_RIGHT_OPTION_RMARGIN);
492
493         opt->box.left = PBOOT_RIGHT_OPTION_LMARGIN;
494         opt->box.right = opt->box.left + width;
495         opt->box.top = PBOOT_RIGHT_OPTION_TMARGIN +
496                 index * PBOOT_RIGHT_OPTION_STRIDE;
497         opt->box.bottom = opt->box.top + PBOOT_RIGHT_OPTION_HEIGHT;
498
499         opt->data = data;
500         return index;
501 }
502
503
504 static void pboot_set_device_select(int sel)
505 {
506         LOG("%s: %d -> %d\n", __FUNCTION__, pboot_dev_sel, sel);
507         if (sel == pboot_dev_sel || sel >= pboot_dev_count)
508                 return;
509         pboot_dev_sel = sel;
510         pboot_rpane->focus_curindex = -1;
511         pboot_rpane->mouse_target = -1;
512         pboot_rpane->focus_box.top = -2*PBOOT_RIGHT_FOCUS_HEIGHT;
513         pboot_rpane->focus_box.bottom = pboot_rpane->focus_box.top +
514                 PBOOT_RIGHT_FOCUS_HEIGHT;
515         twin_window_damage(pboot_rpane->window, 0, 0,
516                            pboot_rpane->window->pixmap->width,
517                            pboot_rpane->window->pixmap->height);
518         twin_window_queue_paint(pboot_rpane->window);
519 }
520
521 static twin_time_t pboot_lfocus_timeout (twin_time_t now, void *closure)
522 {
523         int dir = 1, dist, pos;
524         const int accel[11] = { 7, 4, 2, 1, 1, 1, 1, 2, 3, 4, 5 };
525
526         dist = abs(pboot_lpane->focus_target - pboot_lpane->focus_start);
527         pos = pboot_lpane->focus_target - (int)pboot_lpane->focus_box.top;
528         if (pos == 0) {
529                 pboot_set_device_select(pboot_lpane->focus_curindex);
530                 return -1;
531         }
532         if (pos < 0) {
533                 dir = -1;
534                 pos = -pos;
535         }
536         twin_window_damage(pboot_lpane->window,
537                            pboot_lpane->focus_box.left,
538                            pboot_lpane->focus_box.top,
539                            pboot_lpane->focus_box.right,
540                            pboot_lpane->focus_box.bottom);
541
542         pboot_lpane->focus_box.top += dir;
543         pboot_lpane->focus_box.bottom += dir;
544
545         twin_window_damage(pboot_lpane->window,
546                            pboot_lpane->focus_box.left,
547                            pboot_lpane->focus_box.top,
548                            pboot_lpane->focus_box.right,
549                            pboot_lpane->focus_box.bottom);
550
551         twin_window_queue_paint(pboot_lpane->window);
552
553         return accel[(pos * 10) / dist];
554 }
555
556 static void pboot_set_lfocus(int index)
557 {
558         if (index >= pboot_dev_count)
559                 return;
560
561         pboot_lpane->focus_start = pboot_lpane->focus_box.top;
562
563         if (index < 0)
564                 pboot_lpane->focus_target = 0 - PBOOT_LEFT_FOCUS_HEIGHT;
565         else
566                 pboot_lpane->focus_target = PBOOT_LEFT_FOCUS_YOFF +
567                         PBOOT_LEFT_ICON_STRIDE * index;
568
569         pboot_lpane->focus_curindex = index;
570
571         twin_set_timeout(pboot_lfocus_timeout, 0, NULL);
572 }
573
574 static void pboot_lpane_mousetrack(twin_coord_t x, twin_coord_t y)
575 {
576         int candidate = -1;
577         twin_coord_t icon_top;
578
579         if (x < PBOOT_LEFT_ICON_XOFF ||
580             x > (PBOOT_LEFT_ICON_XOFF + PBOOT_LEFT_ICON_WIDTH))
581                 goto miss;
582         if (y < PBOOT_LEFT_ICON_YOFF)
583                 goto miss;
584         candidate = (y - PBOOT_LEFT_ICON_YOFF) / PBOOT_LEFT_ICON_STRIDE;
585         if (candidate >= pboot_dev_count) {
586                 candidate = -1;
587                 goto miss;
588         }
589         if (candidate == pboot_lpane->mouse_target)
590                 return;
591         icon_top = PBOOT_LEFT_ICON_YOFF +
592                 candidate * PBOOT_LEFT_ICON_STRIDE;
593         if (y > (icon_top + PBOOT_LEFT_ICON_HEIGHT)) {
594                 candidate = -1;
595                 goto miss;
596         }
597
598         /* Ok, so now, we know the mouse hit an icon that wasn't the same
599          * as the previous one, we trigger a focus change
600          */
601         pboot_set_lfocus(candidate);
602
603  miss:
604         pboot_lpane->mouse_target = candidate;
605 }
606
607 static twin_bool_t pboot_lpane_event (twin_window_t         *window,
608                                       twin_event_t          *event)
609 {
610         /* filter out all mouse events */
611         switch(event->kind) {
612         case TwinEventEnter:
613         case TwinEventMotion:
614         case TwinEventLeave:
615                 pboot_select_lpane();
616                 pboot_lpane_mousetrack(event->u.pointer.x, event->u.pointer.y);
617                 return TWIN_TRUE;
618         case TwinEventButtonDown:
619         case TwinEventButtonUp:
620                 return TWIN_TRUE;
621         case TwinEventKeyDown:
622                 switch(event->u.key.key) {
623                 case KEY_UP:
624                         if (pboot_lpane->focus_curindex > 0)
625                                 pboot_set_lfocus(
626                                         pboot_lpane->focus_curindex - 1);
627                         return TWIN_TRUE;
628                 case KEY_DOWN:
629                         pboot_set_lfocus(pboot_lpane->focus_curindex + 1);
630                         return TWIN_TRUE;
631                 case KEY_RIGHT:
632                         pboot_select_rpane();
633                         return TWIN_TRUE;
634                 default:
635                         break;
636                 }
637                 break;
638         default:
639                 break;
640         }
641         return TWIN_FALSE;
642 }
643
644 twin_bool_t pboot_event_filter(twin_screen_t        *screen,
645                                twin_event_t         *event)
646 {
647         switch(event->kind) {
648         case TwinEventEnter:
649         case TwinEventMotion:
650         case TwinEventLeave:
651         case TwinEventButtonDown:
652         case TwinEventButtonUp:
653                 if (pboot_cursor != NULL)
654                         twin_screen_set_cursor(pboot_screen, pboot_cursor,
655                                                pboot_cursor_hx,
656                                                pboot_cursor_hy);
657                 break;
658         case TwinEventKeyDown:
659         case TwinEventKeyUp:
660                 twin_screen_set_cursor(pboot_screen, NULL, 0, 0);
661                 break;
662         default:
663                 break;
664         }
665         return TWIN_FALSE;
666 }
667
668 static void pboot_lpane_draw(twin_window_t *window)
669 {
670         twin_pixmap_t   *px = window->pixmap;
671         pboot_lpane_t   *lpane = window->client_data;
672         twin_path_t     *path;
673         twin_fixed_t    x, y, w, h;
674         int             i;
675
676         /* Fill background */
677         twin_fill(px, PBOOT_LEFT_PANE_COLOR, TWIN_SOURCE,
678                   0, 0, px->width, px->height);
679
680         /* Create a path for use later */
681         path = twin_path_create();
682         assert(path);
683
684         /* Draw right line if needed */
685         if (px->clip.right > (PBOOT_LEFT_PANE_SIZE - 4)) {
686                 x = twin_int_to_fixed(PBOOT_LEFT_PANE_SIZE - 4);
687                 y = twin_int_to_fixed(px->height);
688                 twin_path_rectangle(path, x, 0, 0x40000, y);
689                 twin_paint_path(px, PBOOT_LEFT_LINE_COLOR, path);
690                 twin_path_empty(path);
691         }
692
693         /* Draw focus box */
694         if (lpane->focus_curindex >= 0 &&
695             twin_rect_intersect(lpane->focus_box, px->clip)) {
696                 x = twin_int_to_fixed(lpane->focus_box.left + 2);
697                 y = twin_int_to_fixed(lpane->focus_box.top + 2);
698                 w = twin_int_to_fixed(lpane->focus_box.right -
699                                       lpane->focus_box.left - 4);
700                 h = twin_int_to_fixed(lpane->focus_box.bottom -
701                                       lpane->focus_box.top - 4);
702                 twin_path_rounded_rectangle(path, x, y, w, h,
703                                             PBOOT_LEFT_FOCUS_XRAD,
704                                             PBOOT_LEFT_FOCUS_YRAD);
705                 if (pboot_focus_lpane)
706                         twin_paint_path(px, PBOOT_FOCUS_COLOR, path);
707                 else
708                         twin_paint_stroke(px, PBOOT_FOCUS_COLOR, path,
709                                           4 * TWIN_FIXED_ONE);
710         }
711
712         /* Draw icons */
713         for (i = 0; i < pboot_dev_count; i++) {
714                 pboot_device_t  *dev = pboot_devices[i];
715                 twin_operand_t  src;
716
717                 if (!twin_rect_intersect(dev->box, px->clip))
718                         continue;
719
720                 src.source_kind = TWIN_PIXMAP;
721                 src.u.pixmap = dev->badge;
722
723                 twin_composite(px, dev->box.left, dev->box.top,
724                                &src, 0, 0, NULL, 0, 0, TWIN_OVER,
725                                dev->box.right - dev->box.left,
726                                dev->box.bottom - dev->box.top);
727
728         }
729
730         /* Destroy path */
731         twin_path_destroy(path);
732 }
733
734 static void pboot_create_panels(void)
735 {
736         /* left pane */
737         pboot_lpane = calloc(1, sizeof(pboot_lpane_t));
738         assert(pboot_lpane);
739
740         pboot_lpane->window = twin_window_create(pboot_screen, TWIN_ARGB32,
741                                                  TwinWindowPlain,
742                                                  0, 0, PBOOT_LEFT_PANE_SIZE,
743                                                  pboot_screen->height);
744         assert(pboot_lpane->window);
745
746         pboot_lpane->window->draw = pboot_lpane_draw;
747         pboot_lpane->window->event = pboot_lpane_event;
748         pboot_lpane->window->client_data = pboot_lpane;
749         pboot_lpane->focus_curindex = -1;
750         pboot_lpane->focus_box.left = PBOOT_LEFT_FOCUS_XOFF;
751         pboot_lpane->focus_box.top = -2*PBOOT_LEFT_FOCUS_HEIGHT;
752         pboot_lpane->focus_box.right = pboot_lpane->focus_box.left +
753                 PBOOT_LEFT_FOCUS_WIDTH;
754         pboot_lpane->focus_box.bottom = pboot_lpane->focus_box.top +
755                 PBOOT_LEFT_FOCUS_HEIGHT;
756         pboot_lpane->mouse_target = -1;
757         twin_window_show(pboot_lpane->window);
758
759         /* right pane */
760         pboot_rpane = calloc(1, sizeof(pboot_rpane_t));
761         assert(pboot_rpane);
762
763         pboot_rpane->window = twin_window_create(pboot_screen, TWIN_ARGB32,
764                                                  TwinWindowPlain,
765                                                  PBOOT_LEFT_PANE_SIZE, 0,
766                                                  pboot_screen->width -
767                                                    PBOOT_LEFT_PANE_SIZE,
768                                                  pboot_screen->height);
769         assert(pboot_rpane->window);
770
771         pboot_rpane->window->draw = pboot_rpane_draw;
772         pboot_rpane->window->event = pboot_rpane_event;
773         pboot_rpane->window->client_data = pboot_rpane;
774
775         pboot_rpane->focus_curindex = -1;
776         pboot_rpane->focus_box.left = PBOOT_RIGHT_FOCUS_XOFF;
777         pboot_rpane->focus_box.top = -2*PBOOT_RIGHT_FOCUS_HEIGHT;
778         pboot_rpane->focus_box.right = pboot_rpane->window->pixmap->width -
779                 2 * PBOOT_RIGHT_FOCUS_XOFF;
780         pboot_rpane->focus_box.bottom = pboot_rpane->focus_box.top +
781                 PBOOT_RIGHT_FOCUS_HEIGHT;
782         pboot_rpane->mouse_target = -1;
783         twin_window_show(pboot_rpane->window);
784 }
785
786 int pboot_add_device(const char *dev_id, const char *name,
787                 twin_pixmap_t *pixmap)
788 {
789         int             index;
790         pboot_device_t  *dev;
791
792         if (pboot_dev_count >= PBOOT_MAX_DEV)
793                 return -1;
794
795         index = pboot_dev_count++;
796
797         dev = malloc(sizeof(*dev));
798         memset(dev, 0, sizeof(*dev));
799         dev->id = malloc(strlen(dev_id) + 1);
800         strcpy(dev->id, dev_id);
801         dev->badge = pixmap;
802         dev->box.left = PBOOT_LEFT_ICON_XOFF;
803         dev->box.right = dev->box.left + PBOOT_LEFT_ICON_WIDTH;
804         dev->box.top = PBOOT_LEFT_ICON_YOFF +
805                 PBOOT_LEFT_ICON_STRIDE * index;
806         dev->box.bottom = dev->box.top + PBOOT_LEFT_ICON_HEIGHT;
807
808         pboot_devices[index] = dev;
809
810         twin_window_damage(pboot_lpane->window,
811                            dev->box.left, dev->box.top,
812                            dev->box.right, dev->box.bottom);
813         twin_window_queue_paint(pboot_lpane->window);
814
815         return index;
816 }
817
818 int pboot_remove_device(const char *dev_id)
819 {
820         int             i, new_dev_index;
821         pboot_device_t  *dev = NULL;
822
823         /* find the matching device */
824         for (i = 0; i < pboot_dev_count; i++) {
825                 if (!strcmp(pboot_devices[i]->id, dev_id)) {
826                         dev = pboot_devices[i];
827                         break;
828                 }
829         }
830
831         if (!dev)
832                 return TWIN_FALSE;
833
834         /* select the newly-focussed device */
835         if (i == pboot_dev_count - 1)
836                 new_dev_index = i - 1;
837         else
838                 new_dev_index = i + 1;
839
840         memmove(pboot_devices + i, pboot_devices + i + 1,
841                         sizeof(*pboot_devices) * (pboot_dev_count + i - 1));
842
843         pboot_devices[--pboot_dev_count] = NULL;
844
845         pboot_set_device_select(new_dev_index);
846         twin_window_damage(pboot_lpane->window,
847                            dev->box.left, dev->box.top,
848                            dev->box.right, dev->box.bottom);
849         twin_window_queue_paint(pboot_lpane->window);
850
851         /* todo: free device & options */
852
853         return TWIN_TRUE;
854 }
855
856 static void exitfunc(void)
857 {
858 #ifndef _USE_X11
859         if (pboot_fbdev)
860                 twin_fbdev_destroy(pboot_fbdev);
861         pboot_fbdev = NULL;
862 #endif
863 }
864
865 static void sigint(int sig)
866 {
867         exitfunc();
868         syscall(__NR_exit);
869 }
870
871 int main(int argc, char **argv)
872 {
873         twin_pixmap_t *pic;
874         const char *background_path;
875
876         atexit(exitfunc);
877         signal(SIGINT, sigint);
878
879 #ifdef _USE_X11
880         pboot_x11 = twin_x11_create(XOpenDisplay(0), 1024, 768);
881         if (pboot_x11 == NULL) {
882                 perror("failed to create x11 screen !\n");
883                 return 1;
884         }
885         pboot_screen = pboot_x11->screen;
886 #else
887         /* Create screen and mouse drivers */
888         pboot_fbdev = twin_fbdev_create(-1, SIGUSR1);
889         if (pboot_fbdev == NULL) {
890                 perror("failed to create fbdev screen !\n");
891                 return 1;
892         }
893         pboot_screen = pboot_fbdev->screen;
894         twin_linux_mouse_create(NULL, pboot_screen);
895
896         if (pboot_fbdev != NULL) {
897                 char *cursor_path = artwork_pathname("cursor");
898                 pboot_cursor = twin_load_X_cursor(cursor_path, 2,
899                                                   &pboot_cursor_hx,
900                                                   &pboot_cursor_hy);
901                 if (pboot_cursor == NULL)
902                         pboot_cursor =
903                                 twin_get_default_cursor(&pboot_cursor_hx,
904                                                         &pboot_cursor_hy);
905         }
906 #endif
907
908         /* Set background pixmap */
909         background_path = artwork_pathname("background.png");
910         LOG("loading background: %s...", background_path);
911         pic = twin_png_to_pixmap(background_path, TWIN_ARGB32);
912         LOG("%s\n", pic ? "ok" : "failed");
913         if (pic)
914                 twin_screen_set_background(pboot_screen, pic);
915
916         /* Init more stuffs */
917         pboot_create_panels();
918         twin_window_queue_paint(pboot_lpane->window);
919         twin_window_queue_paint(pboot_rpane->window);
920
921         if (!pboot_start_device_discovery()) {
922                 LOG("Couldn't start device discovery!\n");
923                 return 1;
924         }
925
926         pboot_set_lfocus(0);
927         twin_screen_set_active(pboot_screen, pboot_lpane->window->pixmap);
928         pboot_screen->event_filter = pboot_event_filter;
929
930         /* Console switch */
931 #ifndef _USE_X11
932         if (pboot_fbdev)
933                 twin_fbdev_activate(pboot_fbdev);
934 #endif
935
936         /* Process events */
937         twin_dispatch ();
938
939         return 0;
940 }