ui/ncurses: implement global keys
[petitboot] / ui / twin / pbt-scr.c
1 /*
2  *  Copyright Geoff Levand <geoff@infradead.org>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; version 2 of the License.
7  *
8  *  This program is distributed in the hope that it will be useful,
9  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  *  GNU General Public License for more details.
12  *
13  *  You should have received a copy of the GNU General Public License
14  *  along with this program; if not, write to the Free Software
15  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17
18 #include "config.h"
19 #define _GNU_SOURCE
20 #include <assert.h>
21
22 #include <string.h>
23 #include <linux/input.h>
24
25 #include "list/list.h"
26 #include "log/log.h"
27 #include "talloc/talloc.h"
28 #include "waiter/waiter.h"
29 #include "ui/common/ui-system.h"
30 #include "pbt-scr.h"
31
32 void _pbt_dump_event(const char *text, twin_window_t *twindow,
33         const twin_event_t *tevent, const char *func,  int line)
34 {
35         switch(tevent->kind) {
36         case TwinEventButtonDown:
37                 DBG("%s:%d: %s@%p: TwinEventButtonDown %x\n", func, line, text,
38                         twindow, tevent->kind);
39                 return;
40         case TwinEventButtonUp:
41                 DBG("%s:%d: %s@%p: TwinEventButtonUp %x\n", func, line, text,
42                         twindow, tevent->kind);
43                 return;
44         case TwinEventMotion:
45                 //DBG("%s:%d:%s@%p: TwinEventMotion %x\n", func, line, text,
46                 //      twindow, tevent->kind);
47                 return;
48         case TwinEventEnter:
49                 DBG("%s:%d: %s@%p: TwinEventEnter %x\n", func, line, text,
50                         twindow, tevent->kind);
51                 return;
52         case TwinEventLeave:
53                 DBG("%s:%d: %s@%p: TwinEventLeave %x\n", func, line, text,
54                         twindow, tevent->kind);
55                 return;
56         case TwinEventKeyDown:
57         case TwinEventKeyUp:
58         {
59                 const char *kind = (tevent->kind == TwinEventKeyDown)
60                         ? "TwinEventKeyDown" : "TwinEventKeyUp  ";
61
62                 switch(tevent->u.key.key) {
63                 case (twin_keysym_t)XK_Up:
64                 case (twin_keysym_t)KEY_UP:
65                         DBG("%s:%d: %s@%p: %s = 'KEY_UP'\n", func, line, text,
66                                 twindow, kind);
67                         return;
68                 case (twin_keysym_t)XK_Down:
69                 case (twin_keysym_t)KEY_DOWN:
70                         DBG("%s:%d: %s@%p: %s = 'KEY_DOWN'\n", func, line, text,
71                                 twindow, kind);
72                         return;
73                 case (twin_keysym_t)XK_Right:
74                 case (twin_keysym_t)KEY_RIGHT:
75                         DBG("%s:%d: %s@%p: %s = 'KEY_RIGHT'\n", func, line, text,
76                                 twindow, kind);
77                         return;
78                 case (twin_keysym_t)XK_Left:
79                 case (twin_keysym_t)KEY_LEFT:
80                         DBG("%s:%d: %s@%p: %s = 'KEY_LEFT'\n", func, line, text,
81                                 twindow, kind);
82                         return;
83                 case (twin_keysym_t)XK_Escape:
84                 case (twin_keysym_t)KEY_ESC:
85                         DBG("%s:%d: %s@%p: %s = 'KEY_ESC'\n", func, line, text,
86                                 twindow, kind);
87                         return;
88                 case (twin_keysym_t)XK_Return:
89                 case (twin_keysym_t)KEY_ENTER:
90                         DBG("%s:%d: %s@%p: %s = 'KEY_ENTER'\n", func, line, text,
91                                 twindow, kind);
92                         return;
93                 case (twin_keysym_t)XK_Delete:
94                 case (twin_keysym_t)KEY_DELETE:
95                         DBG("%s:%d: %s@%p: %s = 'KEY_DELETE'\n", func, line, text,
96                                 twindow, kind);
97                         return;
98                 case (twin_keysym_t)XK_BackSpace:
99                 case (twin_keysym_t)KEY_BACKSPACE:
100                         DBG("%s:%d: %s@%p: %s = 'KEY_BACKSPACE'\n", func, line, text,
101                                 twindow, kind);
102                         return;
103                 default:
104                 DBG("%s:%d: %s@%p: %s = %d (%xh) = '%c'\n", func, line, text, twindow,
105                         kind,
106                         tevent->u.key.key, tevent->u.key.key,
107                         (char)tevent->u.key.key);
108                 }
109                 return;
110         }
111         default:
112                 DBG("%s:%d: %s@%p: %x\n", func, line, text, twindow, tevent->kind);
113                 break;
114         }
115 }
116
117 /**
118  * pbt_background_load - Load the background pixmap from storage.
119  * @filename: File name of a jpg background.
120  *
121  * Returns the default background if @filename is NULL.  Returns a default
122  * pattern if the load of @filename fails.
123  */
124
125 twin_pixmap_t *pbt_background_load(twin_screen_t *tscreen,
126         const char *filename)
127 {
128         static const char *default_background_file =
129                 PB_ARTWORK_PATH "/background.jpg";
130         twin_pixmap_t *raw_background;
131         twin_pixmap_t *scaled_background;
132
133         if (!filename)
134                 filename = default_background_file;
135
136         raw_background = twin_jpeg_to_pixmap(filename, TWIN_ARGB32);
137
138         if (!raw_background) {
139                 pb_log("%s: loading image '%s' failed\n", __func__, filename);
140
141                 /* Fallback to a default pattern */
142
143                 return twin_make_pattern();
144         }
145
146         if (tscreen->height == raw_background->height &&
147                 tscreen->width == raw_background->width)
148                 return raw_background;
149
150         /* Scale as needed. */
151
152         twin_fixed_t sx, sy;
153         twin_operand_t srcop;
154
155         scaled_background = twin_pixmap_create(TWIN_ARGB32,
156                                 tscreen->width,
157                                 tscreen->height);
158         if (!scaled_background) {
159                 pb_log("%s: scale '%s' failed\n", __func__, filename);
160                 twin_pixmap_destroy(raw_background);
161                 return twin_make_pattern();
162         }
163         sx = twin_fixed_div(twin_int_to_fixed(raw_background->width),
164                         twin_int_to_fixed(tscreen->width));
165         sy = twin_fixed_div(twin_int_to_fixed(raw_background->height),
166                         twin_int_to_fixed(tscreen->height));
167
168         twin_matrix_scale(&raw_background->transform, sx, sy);
169         srcop.source_kind = TWIN_PIXMAP;
170         srcop.u.pixmap = raw_background;
171         twin_composite(scaled_background, 0, 0, &srcop, 0, 0,
172                 NULL, 0, 0, TWIN_SOURCE,
173                 tscreen->width, tscreen->height);
174
175         twin_pixmap_destroy(raw_background);
176
177         return scaled_background;
178 }
179
180 const char *pbt_icon_chooser(const char *hint)
181 {
182         if (strstr(hint, "net"))
183                 return PB_ARTWORK_PATH "/network-wired.png";
184
185         return NULL;
186 }
187
188 /**
189  * pbt_icon_load - Load an icon pixmap from storage.
190  * @filename: File name of a png icon.
191  *
192  * Returns the default icon if @filename is NULL, or if the load
193  * of @filename fails.
194  * Caches pixmaps based on a hash of the @filename string.
195  */
196
197 twin_pixmap_t *pbt_icon_load(const char *filename)
198 {
199         static const char *default_icon_file = PB_ARTWORK_PATH "/tux.png";
200         struct cache_entry {
201                 struct list_item list;
202                 int hash;
203                 twin_pixmap_t *icon;
204         };
205         STATIC_LIST(icon_cache);
206         struct cache_entry new;
207         struct cache_entry *i;
208
209         if (!filename)
210                 filename = default_icon_file;
211
212 retry:
213         new.hash = pb_elf_hash(filename);
214
215         list_for_each_entry(&icon_cache, i, list) {
216                 if (i->hash == new.hash) {
217                         DBGS("found %p\n", i->icon);
218                         return i->icon;
219                 }
220         }
221
222         new.icon = twin_png_to_pixmap(filename, TWIN_ARGB32);
223
224         if (!new.icon) {
225                 pb_log("%s: loading image '%s' failed\n", __func__, filename);
226
227                 if (filename == default_icon_file)
228                         return NULL;
229
230                 filename = default_icon_file;
231                 goto retry;
232         }
233
234         DBGS("new %p\n", new.icon);
235
236         i = talloc(NULL, struct cache_entry);
237         *i = new;
238         list_add(&icon_cache, &i->list);
239
240         pbt_dump_pixmap(new.icon);
241
242         return new.icon;
243 }
244
245 /**
246  * pbt_border_draw - Draw a border on a pixmap.
247  * @pixmap: The image to operate on.
248  * @border: The border to draw.
249  */
250
251 void pbt_border_draw(twin_pixmap_t *pixmap, const struct pbt_border *border)
252 {
253         twin_path_t *path = twin_path_create();
254         twin_argb32_t fill = border->fill_color ? border->fill_color
255                 : 0xff000000;  /* default to black */
256
257         assert(path);
258
259         //pbt_dump_pixmap(pixmap);
260
261         if (border->left) {
262                 twin_path_rectangle(path, 0, 0,
263                         twin_int_to_fixed(border->left),
264                         twin_int_to_fixed(pixmap->height));
265         }
266
267         if (border->right) {
268                 twin_path_rectangle(path,
269                         twin_int_to_fixed(pixmap->width - border->right),
270                         0,
271                         twin_int_to_fixed(pixmap->width),
272                         twin_int_to_fixed(pixmap->height));
273         }
274
275         if (border->top) {
276                 twin_path_rectangle(path, 0, 0,
277                         twin_int_to_fixed(pixmap->width),
278                         twin_int_to_fixed(border->top));
279         }
280
281         if (border->bottom) {
282                 twin_path_rectangle(path, 0,
283                         twin_int_to_fixed(pixmap->height - border->bottom),
284                         twin_int_to_fixed(pixmap->width),
285                         twin_int_to_fixed(border->bottom));
286         }
287
288         twin_paint_path(pixmap, fill, path);
289         twin_path_empty(path);
290 }
291
292 int pbt_window_contains(const twin_window_t *window, const twin_event_t *event)
293 {
294         pbt_dump_pixmap(window->pixmap);
295
296         if (event->u.pointer.x < window->pixmap->x) {
297                 DBGS("%p: {%d,%d} left miss\n", window, event->u.pointer.x, event->u.pointer.y);
298                 return 0;
299         }
300         if (event->u.pointer.x >= window->pixmap->x + window->pixmap->width) {
301                 DBGS("%p: {%d,%d} right miss\n", window, event->u.pointer.x, event->u.pointer.y);
302                 return 0;
303         }
304         if (event->u.pointer.y < window->pixmap->y) {
305                 DBGS("%p: {%d,%d} high miss\n", window, event->u.pointer.x, event->u.pointer.y);
306                 return 0;
307         }
308         if (event->u.pointer.y >= window->pixmap->y + window->pixmap->height){
309                 DBGS("%p: {%d,%d} low miss\n", window, event->u.pointer.x, event->u.pointer.y);
310                 return 0;
311         }
312
313         DBGS("%p: {%d,%d} hit\n", window, event->u.pointer.x, event->u.pointer.y);
314         return 1;
315 }
316
317
318 static __attribute__((unused)) void pbt_image_copy(twin_pixmap_t *dest, twin_pixmap_t *src)
319 {
320         twin_operand_t op;
321
322         assert(dest->height >= src->height);
323
324         op.source_kind = TWIN_PIXMAP;
325         op.u.pixmap = src;
326
327         twin_composite(dest, 0, 0, &op, 0, 0, NULL,
328                 0, 0, TWIN_SOURCE, src->width, src->height);
329 }
330
331 void pbt_image_draw(twin_pixmap_t *dest, twin_pixmap_t *image)
332 {
333         twin_operand_t src;
334         int offset;
335
336         assert(dest->height >= image->height);
337
338         src.source_kind = TWIN_PIXMAP;
339         src.u.pixmap = image;
340
341         /* Center the image in the window. */
342
343         offset = (dest->height - image->height) / 2;
344
345         twin_composite(dest, offset, offset, &src, 0, 0, NULL,
346                 0, 0, TWIN_SOURCE, image->width, image->height);
347 }
348
349 static int pbt_twin_waiter_cb(struct pbt_twin_ctx *twin_ctx)
350 {
351 #if defined(HAVE_LIBTWIN_TWIN_X11_H)
352         if (twin_ctx->backend == pbt_twin_x11)
353                 twin_x11_process_events(twin_ctx->x11);
354 #endif
355 #if defined(HAVE_LIBTWIN_TWIN_FBDEV_H)
356         if (twin_ctx->backend == pbt_twin_fbdev)
357                 twin_fbdev_process_events(twin_ctx->fbdev);
358 #endif
359         return 0;
360 };
361
362 static void pbt_scr_destructor(struct pbt_scr *scr)
363 {
364         pb_log("%s\n", __func__);
365
366         twin_x11_destroy(scr->twin_ctx.x11);
367         // FIXME: need cursor cleanup???
368         memset(scr, 0, sizeof(*scr));
369 }
370
371 struct pbt_scr *pbt_scr_init(void *talloc_ctx,
372         struct waitset *waitset,
373         enum pbt_twin_backend backend,
374         unsigned int width, unsigned int height,
375         const char *filename_background,
376         twin_bool_t (*scr_event_cb)(twin_screen_t *tscreen,
377                 twin_event_t *event))
378 {
379         struct pbt_scr *scr = talloc_zero(talloc_ctx, struct pbt_scr);
380         int waiter_fd = -1;
381
382         assert(backend && backend < 3);
383
384         if (!scr) {
385                 pb_log("%s: alloc pbt_scr failed.\n", __func__);
386                 goto fail_alloc;
387         }
388
389         talloc_set_destructor(scr, (void *)pbt_scr_destructor);
390
391         twin_feature_init(); // FIXME: need it???
392
393         scr->twin_ctx.backend = backend;
394
395         if (backend == pbt_twin_x11) {
396                 pb_log("%s: using twin x11 backend.\n", __func__);
397                 assert(width > 100);
398                 assert(height > 100);
399
400 #if !defined(HAVE_LIBTWIN_TWIN_X11_H)
401                 assert(0);
402 #else
403                 scr->twin_ctx.x11 = twin_x11_create_ext(XOpenDisplay(0), width,
404                         height, 0);
405
406                 if (!scr->twin_ctx.x11) {
407                         pb_log("%s: twin_x11_create_ext failed.\n", __func__);
408                         perror("failed to create twin x11 context\n");
409                         goto fail_ctx_create;
410                 }
411
412                 pb_log("%s: x11: %p\n", __func__, scr->twin_ctx.x11);
413
414                 assert(scr->twin_ctx.x11->screen);
415                 scr->tscreen = scr->twin_ctx.x11->screen;
416                 waiter_fd = ConnectionNumber(scr->twin_ctx.x11->dpy);
417 #endif
418         } else if (backend == pbt_twin_fbdev) {
419                 pb_log("%s: using twin fbdev backend.\n", __func__);
420 #if !defined(HAVE_LIBTWIN_TWIN_FBDEV_H)
421                 assert(0);
422 #else
423                 scr->twin_ctx.fbdev = twin_fbdev_create_ext(-1, SIGUSR1, 0);
424
425                 if (!scr->twin_ctx.fbdev) {
426                         pb_log("%s: twin_fbdev_create_ext failed.\n", __func__);
427                         perror("failed to create twin fbdev context\n");
428                         goto fail_ctx_create;
429                 }
430
431                 assert(scr->twin_ctx.fbdev->screen);
432                 scr->tscreen = scr->twin_ctx.fbdev->screen;
433                 waiter_fd = scr->twin_ctx.fbdev->vt_fd;
434
435                 twin_fbdev_activate(scr->twin_ctx.fbdev);
436 #endif
437         }
438
439         scr->tscreen->event_filter = scr_event_cb;
440
441         twin_screen_set_background(scr->tscreen,
442                 pbt_background_load(scr->tscreen, filename_background));
443
444         assert(waiter_fd != -1);
445
446         waiter_register(waitset, waiter_fd, WAIT_IN, (void *)pbt_twin_waiter_cb,
447                 &scr->twin_ctx);
448
449         return scr;
450
451 fail_ctx_create:
452 fail_alloc:
453         return NULL;
454 }
455
456 void pbt_window_redraw(twin_window_t *twindow)
457 {
458         twin_window_damage(twindow, 0, 0, twindow->pixmap->width,
459                 twindow->pixmap->height);
460         //twin_window_queue_paint(twindow);
461         twin_window_draw(twindow);
462 }