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