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