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