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