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