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