wlt: fix shl_hook API changes
[platform/upstream/kmscon.git] / src / wlt_theme.c
1 /*
2  * wlt - Theme Helper
3  *
4  * Copyright (c) 2012 David Herrmann <dh.herrmann@googlemail.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files
8  * (the "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included
15  * in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25
26 /*
27  * Wayland Terminal theme/decoration drawing helper
28  */
29
30 #include <errno.h>
31 #include <linux/input.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <wayland-client.h>
35 #include "log.h"
36 #include "shl_misc.h"
37 #include "wlt_main.h"
38 #include "wlt_theme.h"
39 #include "wlt_toolkit.h"
40
41 #define LOG_SUBSYSTEM "wlt_theme"
42
43 enum theme_location {
44         LOC_NOWHERE,
45         LOC_SOMEWHERE,
46         LOC_RESIZE_TOP,
47         LOC_RESIZE_BOTTOM,
48         LOC_RESIZE_LEFT,
49         LOC_RESIZE_RIGHT,
50         LOC_RESIZE_TOP_LEFT,
51         LOC_RESIZE_TOP_RIGHT,
52         LOC_RESIZE_BOTTOM_LEFT,
53         LOC_RESIZE_BOTTOM_RIGHT,
54         LOC_CONTROL,
55         LOC_MINIMIZE,
56         LOC_MAXIMIZE,
57         LOC_CLOSE,
58 };
59
60 struct wlt_theme {
61         struct wlt_window *wnd;
62         struct wlt_widget *widget;
63         struct wlt_shm_buffer buffer;
64         struct wlt_rect alloc;
65         unsigned int control_height;
66         unsigned int frame_width;
67         unsigned int resize_margin;
68         unsigned int button_size;
69         unsigned int button_padding;
70         unsigned int button_margin;
71
72         unsigned int pointer_x;
73         unsigned int pointer_y;
74         unsigned int pointer_loc;
75         bool pointer_pressed;
76         unsigned int pointer_grabbed;
77 };
78
79 static void draw_control(struct wlt_theme *theme)
80 {
81         uint8_t *dst;
82         uint32_t *line, backcol;
83         unsigned int i, j;
84         unsigned int b1off, b2off, b3off;
85
86         /* background */
87         if (theme->pointer_loc == LOC_NOWHERE)
88                 backcol = (0x60 << 24) | (0xaa << 16) |
89                           (0xaa << 8) | 0xaa;
90         else
91                 backcol = (0x20 << 24) | (0xee << 16) |
92                           (0xee << 8) | 0xee;
93         dst = theme->buffer.data;
94         for (i = 0; i < theme->control_height; ++i) {
95                 line = (uint32_t*)dst;
96                 for (j = 0; j < theme->buffer.width; ++j) {
97                         if (i == 0 || i == theme->control_height - 1 ||
98                             j == 0 || j == theme->buffer.width - 1)
99                                 line[j] = 0xff << 24;
100                         else
101                                 line[j] = backcol;
102                 }
103                 dst += theme->buffer.stride;
104         }
105
106         /* buttons */
107         b1off = theme->buffer.width - theme->button_margin - theme->button_size;
108         b2off = b1off - theme->button_padding - theme->button_size;
109         b3off = b2off - theme->button_padding - theme->button_size;
110         dst = theme->buffer.data + theme->buffer.stride * theme->button_margin;
111         for (i = 0; i < theme->button_size; ++i) {
112                 line = (uint32_t*)dst;
113                 for (j = 0; j < theme->buffer.width; ++j) {
114                         if (j >= b1off &&
115                             j < b1off + theme->button_size) {
116                                 if (i == 0 ||
117                                     i == theme->button_size - 1 ||
118                                     j == b1off ||
119                                     j == b1off + theme->button_size - 1)
120                                         line[j] = 0xff << 24;
121                                 else if (theme->pointer_loc == LOC_CLOSE &&
122                                          theme->pointer_pressed &&
123                                          theme->pointer_grabbed == LOC_CLOSE)
124                                         line[j] = (0xff << 24) | 0x1f1f1f1f;
125                                 else if (theme->pointer_loc == LOC_CLOSE &&
126                                          !theme->pointer_pressed)
127                                         line[j] = 0xffffffff;
128                                 else
129                                         line[j] = (0xff << 24) | 0x33333333;
130                         } else if (j >= b2off &&
131                                    j < b2off + theme->button_size) {
132                                 if (i == 0 ||
133                                     i == theme->button_size - 1 ||
134                                     j == b2off ||
135                                     j == b2off + theme->button_size - 1)
136                                         line[j] = 0xff << 24;
137                                 else if (theme->pointer_loc == LOC_MAXIMIZE &&
138                                          theme->pointer_pressed &&
139                                          theme->pointer_grabbed == LOC_MAXIMIZE)
140                                         line[j] = (0xff << 24) | 0x1f1f1f1f;
141                                 else if (theme->pointer_loc == LOC_MAXIMIZE &&
142                                          !theme->pointer_pressed)
143                                         line[j] = 0xffffffff;
144                                 else
145                                         line[j] = (0xff << 24) | 0x66666666;
146                         } else if (j >= b3off &&
147                                    j < b3off + theme->button_size) {
148                                 if (i == 0 ||
149                                     i == theme->button_size - 1 ||
150                                     j == b3off ||
151                                     j == b3off + theme->button_size - 1)
152                                         line[j] = 0xff << 24;
153                                 else if (theme->pointer_loc == LOC_MINIMIZE &&
154                                          theme->pointer_pressed &&
155                                          theme->pointer_grabbed == LOC_MINIMIZE)
156                                         line[j] = (0xff << 24) | 0x1f1f1f1f;
157                                 else if (theme->pointer_loc == LOC_MINIMIZE &&
158                                          !theme->pointer_pressed)
159                                         line[j] = 0xffffffff;
160                                 else
161                                         line[j] = (0xff << 24) | 0xaaaaaaaa;
162                         }
163                 }
164                 dst += theme->buffer.stride;
165         }
166 }
167
168 static void draw_frame(struct wlt_theme *theme)
169 {
170         uint8_t *dst;
171         uint32_t *line;
172         unsigned int i, j, height;
173
174         /* top frame */
175         dst = theme->buffer.data + theme->buffer.stride *
176               theme->control_height;
177         for (i = 0; i < theme->frame_width; ++i) {
178                 line = (uint32_t*)dst;
179                 for (j = 0; j < theme->buffer.width; ++j)
180                         line[j] = 0xa0 << 24;
181                 dst += theme->buffer.stride;
182         }
183
184         /* bottom frame */
185         dst = theme->buffer.data + theme->buffer.stride *
186               (theme->buffer.height - theme->frame_width);
187         for (i = 0; i < theme->frame_width; ++i) {
188                 line = (uint32_t*)dst;
189                 for (j = 0; j < theme->buffer.width; ++j)
190                         line[j] = 0xa0 << 24;
191                 dst += theme->buffer.stride;
192         }
193
194         /* left frame */
195         dst = theme->buffer.data + theme->buffer.stride *
196               (theme->control_height + theme->frame_width);
197         height = theme->buffer.height - theme->control_height -
198                  theme->frame_width * 2;
199         for (i = 0; i < height; ++i) {
200                 line = (uint32_t*)dst;
201                 for (j = 0; j < theme->frame_width; ++j)
202                         line[j] = 0xa0 << 24;
203                 dst += theme->buffer.stride;
204         }
205
206         /* right frame */
207         dst = theme->buffer.data + theme->buffer.stride *
208               (theme->control_height + theme->frame_width);
209         height = theme->buffer.height - theme->control_height -
210                  theme->frame_width * 2;
211         for (i = 0; i < height; ++i) {
212                 line = (uint32_t*)dst;
213                 line += theme->buffer.width - theme->frame_width;
214                 for (j = 0; j < theme->frame_width; ++j)
215                         line[j] = 0xa0 << 24;
216                 dst += theme->buffer.stride;
217         }
218 }
219
220 static void widget_draw_fallback(struct wlt_theme *theme)
221 {
222         uint8_t *dst;
223         uint32_t *line;
224         unsigned int i, j;
225
226         dst = theme->buffer.data;
227         for (i = 0; i < theme->buffer.height; ++i) {
228                 line = (uint32_t*)dst;
229                 for (j = 0; j < theme->buffer.width; ++j) {
230                         line[j] = 0xff << 24;
231                 }
232                 dst += theme->buffer.stride;
233         }
234 }
235
236 static void widget_redraw(struct wlt_widget *widget, unsigned int flags,
237                           void *data)
238 {
239         struct wlt_theme *theme = data;
240         unsigned int width, height;
241
242         if (flags & WLT_WINDOW_FULLSCREEN)
243                 return;
244
245         width = theme->buffer.width;
246         height = theme->buffer.height;
247         if (width < 2 ||
248             width < 2 * theme->frame_width ||
249             width < 2 * theme->button_margin + 2 * theme->button_padding +
250                     3 * theme->button_size) {
251                 widget_draw_fallback(theme);
252         } else if (height < theme->control_height + 2 * theme->frame_width) {
253                 widget_draw_fallback(theme);
254         } else {
255                 draw_frame(theme);
256                 draw_control(theme);
257         }
258 }
259
260 static void widget_prepare_resize(struct wlt_widget *widget,
261                                   unsigned int flags,
262                                   unsigned int width, unsigned int height,
263                                   unsigned int *min_width,
264                                   unsigned int *min_height,
265                                   unsigned int *new_width,
266                                   unsigned int *new_height,
267                                   void *data)
268 {
269         struct wlt_theme *theme = data;
270         unsigned int minw, minh;
271
272         if (flags & WLT_WINDOW_FULLSCREEN)
273                 return;
274
275         /* set minimal size */
276         minw = 2 * theme->button_margin + 2 * theme->button_padding +
277                3 * theme->button_size + *new_width;
278         minh = theme->button_size + 2 * theme->button_padding +
279                2 * theme->frame_width + *new_height;
280         if (*min_width < minw)
281                 *min_width = minw;
282         if (*min_height < minh)
283                 *min_height = minh;
284
285         /* set margin size */
286         minw = 2 * theme->frame_width;
287         minh = theme->control_height + 2 * theme->frame_width;
288
289         *new_width += minw;
290         *new_height += minh;
291 }
292
293 static void widget_resize(struct wlt_widget *widget, unsigned int flags,
294                           struct wlt_rect *alloc, void *data)
295 {
296         struct wlt_theme *theme = data;
297         unsigned int nwidth, nheight;
298
299         wlt_window_get_buffer(theme->wnd, alloc, &theme->buffer);
300         memcpy(&theme->alloc, alloc, sizeof(*alloc));
301
302         if (flags & WLT_WINDOW_FULLSCREEN)
303                 return;
304
305         alloc->x = theme->frame_width;
306         alloc->y = theme->control_height + theme->frame_width;
307         nwidth = alloc->width - 2 * theme->frame_width;
308         nheight = alloc->height;
309         nheight -= (theme->control_height + 2 * theme->frame_width);
310
311         if (nwidth > alloc->width || nheight > alloc->height) {
312                 alloc->width = 0;
313                 alloc->height = 0;
314         } else {
315                 alloc->width = nwidth;
316                 alloc->height = nheight;
317         }
318 }
319
320 static unsigned int get_pointer_location(struct wlt_theme *theme)
321 {
322         unsigned int m = theme->resize_margin;
323         unsigned int b1off, b2off, b3off;
324
325         if (theme->pointer_y < m) {
326                 if (theme->pointer_x < m)
327                         return LOC_RESIZE_TOP_LEFT;
328                 else if (theme->pointer_x >= theme->buffer.width - m)
329                         return LOC_RESIZE_TOP_RIGHT;
330                 else
331                         return LOC_RESIZE_TOP;
332         }
333
334         if (theme->pointer_y >= theme->buffer.height - m) {
335                 if (theme->pointer_x < m)
336                         return LOC_RESIZE_BOTTOM_LEFT;
337                 else if (theme->pointer_x >= theme->buffer.width - m)
338                         return LOC_RESIZE_BOTTOM_RIGHT;
339                 else
340                         return LOC_RESIZE_BOTTOM;
341         }
342
343         if (theme->pointer_x < m)
344                 return LOC_RESIZE_LEFT;
345
346         if (theme->pointer_x >= theme->buffer.width - m)
347                 return LOC_RESIZE_RIGHT;
348
349         if (theme->pointer_y < theme->control_height) {
350                 b1off = theme->buffer.width - theme->button_margin -
351                         theme->button_size;
352                 b2off = b1off - theme->button_padding - theme->button_size;
353                 b3off = b2off - theme->button_padding - theme->button_size;
354
355                 if (theme->pointer_y >= theme->button_margin &&
356                     theme->pointer_y < theme->control_height -
357                                        theme->button_margin) {
358                         if (theme->pointer_x >= b1off &&
359                             theme->pointer_x < b1off + theme->button_size)
360                                 return LOC_CLOSE;
361                         if (theme->pointer_x >= b2off &&
362                             theme->pointer_x < b2off + theme->button_size)
363                                 return LOC_MAXIMIZE;
364                         if (theme->pointer_x >= b3off &&
365                             theme->pointer_x < b3off + theme->button_size)
366                                 return LOC_MINIMIZE;
367                 }
368
369                 return LOC_CONTROL;
370         }
371
372         return LOC_SOMEWHERE;
373 }
374
375 static void set_pointer_location(struct wlt_theme *theme, unsigned int loc)
376 {
377         if (theme->pointer_loc == loc)
378                 return;
379
380         theme->pointer_loc = loc;
381         if (loc == LOC_NOWHERE) {
382                 theme->pointer_x = -1;
383                 theme->pointer_y = -1;
384         }
385
386         wlt_window_schedule_redraw(theme->wnd);
387 }
388
389 static void widget_pointer_motion(struct wlt_widget *widget,
390                                   unsigned int x, unsigned int y, void *data)
391 {
392         struct wlt_theme *theme = data;
393
394         if (!wlt_rect_contains(&theme->alloc, x, y)) {
395                 set_pointer_location(theme, LOC_NOWHERE);
396                 return;
397         } else {
398                 theme->pointer_x = x - theme->alloc.x;
399                 theme->pointer_y = y - theme->alloc.y;
400                 set_pointer_location(theme, get_pointer_location(theme));
401         }
402
403         switch (theme->pointer_loc) {
404         case LOC_RESIZE_LEFT:
405                 wlt_window_set_cursor(theme->wnd, WLT_CURSOR_LEFT);
406                 break;
407         case LOC_RESIZE_RIGHT:
408                 wlt_window_set_cursor(theme->wnd, WLT_CURSOR_RIGHT);
409                 break;
410         case LOC_RESIZE_TOP:
411                 wlt_window_set_cursor(theme->wnd, WLT_CURSOR_TOP);
412                 break;
413         case LOC_RESIZE_BOTTOM:
414                 wlt_window_set_cursor(theme->wnd, WLT_CURSOR_BOTTOM);
415                 break;
416         case LOC_RESIZE_TOP_LEFT:
417                 wlt_window_set_cursor(theme->wnd, WLT_CURSOR_TOP_LEFT);
418                 break;
419         case LOC_RESIZE_TOP_RIGHT:
420                 wlt_window_set_cursor(theme->wnd, WLT_CURSOR_TOP_RIGHT);
421                 break;
422         case LOC_RESIZE_BOTTOM_LEFT:
423                 wlt_window_set_cursor(theme->wnd,
424                                       WLT_CURSOR_BOTTOM_LEFT);
425                 break;
426         case LOC_RESIZE_BOTTOM_RIGHT:
427                 wlt_window_set_cursor(theme->wnd,
428                                       WLT_CURSOR_BOTTOM_RIGHT);
429                 break;
430         default:
431                 wlt_window_set_cursor(theme->wnd, WLT_CURSOR_LEFT_PTR);
432         }
433 }
434
435 static void widget_pointer_enter(struct wlt_widget *widget,
436                                  unsigned int x, unsigned int y, void *data)
437 {
438         struct wlt_theme *theme = data;
439
440         widget_pointer_motion(widget, x, y, theme);
441 }
442
443 static void widget_pointer_leave(struct wlt_widget *widget, void *data)
444 {
445         struct wlt_theme *theme = data;
446
447         if (theme->pointer_pressed) {
448                 theme->pointer_pressed = false;
449                 wlt_window_schedule_redraw(theme->wnd);
450         }
451
452         set_pointer_location(theme, LOC_NOWHERE);
453 }
454
455 static void button_action(struct wlt_theme *theme)
456 {
457         if (theme->pointer_grabbed != theme->pointer_loc)
458                 return;
459
460         switch (theme->pointer_loc) {
461         case LOC_CLOSE:
462                 wlt_window_close(theme->wnd);
463                 break;
464         case LOC_MAXIMIZE:
465                 wlt_window_toggle_maximize(theme->wnd);
466                 break;
467         case LOC_MINIMIZE:
468                 break;
469         }
470 }
471
472 static void widget_pointer_button(struct wlt_widget *widget,
473                                   uint32_t button, uint32_t state, void *data)
474 {
475         struct wlt_theme *theme = data;
476
477         if (button != BTN_LEFT)
478                 return;
479
480         if (state != WL_POINTER_BUTTON_STATE_PRESSED) {
481                 if (theme->pointer_pressed) {
482                         button_action(theme);
483                         theme->pointer_pressed = false;
484                         theme->pointer_grabbed = LOC_NOWHERE;
485                         wlt_window_schedule_redraw(theme->wnd);
486                 }
487                 return;
488         }
489
490         if (!theme->pointer_pressed) {
491                 theme->pointer_pressed = true;
492                 theme->pointer_grabbed = theme->pointer_loc;
493                 wlt_window_schedule_redraw(theme->wnd);
494         }
495
496         switch (theme->pointer_loc) {
497         case LOC_RESIZE_LEFT:
498                 wlt_window_resize(theme->wnd, WL_SHELL_SURFACE_RESIZE_LEFT);
499                 break;
500         case LOC_RESIZE_RIGHT:
501                 wlt_window_resize(theme->wnd, WL_SHELL_SURFACE_RESIZE_RIGHT);
502                 break;
503         case LOC_RESIZE_TOP:
504                 wlt_window_resize(theme->wnd,
505                         WL_SHELL_SURFACE_RESIZE_TOP);
506                 break;
507         case LOC_RESIZE_BOTTOM:
508                 wlt_window_resize(theme->wnd,
509                         WL_SHELL_SURFACE_RESIZE_BOTTOM);
510                 break;
511         case LOC_RESIZE_TOP_LEFT:
512                 wlt_window_resize(theme->wnd,
513                         WL_SHELL_SURFACE_RESIZE_TOP_LEFT);
514                 break;
515         case LOC_RESIZE_TOP_RIGHT:
516                 wlt_window_resize(theme->wnd,
517                         WL_SHELL_SURFACE_RESIZE_TOP_RIGHT);
518                 break;
519         case LOC_RESIZE_BOTTOM_LEFT:
520                 wlt_window_resize(theme->wnd,
521                         WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT);
522                 break;
523         case LOC_RESIZE_BOTTOM_RIGHT:
524                 wlt_window_resize(theme->wnd,
525                         WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT);
526                 break;
527         case LOC_CONTROL:
528                 wlt_window_move(theme->wnd);
529                 break;
530         }
531 }
532
533 static bool widget_key(struct wlt_widget *widget, unsigned int mask,
534                        uint32_t sym, uint32_t ascii, uint32_t state,
535                        bool handled, void *data)
536 {
537         struct wlt_theme *theme = data;
538
539         if (handled || state != WL_KEYBOARD_KEY_STATE_PRESSED)
540                 return false;
541
542         if (conf_grab_matches(wlt_conf.grab_fullscreen,
543                               mask, 1, &sym)) {
544                 wlt_window_toggle_fullscreen(theme->wnd);
545                 return true;
546         }
547
548         return false;
549 }
550
551
552 static void widget_destroy(struct wlt_widget *widget, void *data)
553 {
554         struct wlt_theme *theme = data;
555
556         log_debug("destroy theme");
557
558         free(theme);
559 }
560
561 int wlt_theme_new(struct wlt_theme **out, struct wlt_window *wnd)
562 {
563         struct wlt_theme *theme;
564         int ret;
565
566         if (!out || !wnd)
567                 return -EINVAL;
568
569         log_debug("create new theme");
570
571         theme = malloc(sizeof(*theme));
572         if (!theme)
573                 return -ENOMEM;
574         memset(theme, 0, sizeof(*theme));
575         theme->wnd = wnd;
576         theme->control_height = 25;
577         theme->frame_width = 5;
578         theme->resize_margin = 5;
579         theme->button_size = 15;
580         theme->button_padding = 3;
581         theme->button_margin = 5;
582         theme->pointer_grabbed = LOC_NOWHERE;
583         set_pointer_location(theme, LOC_NOWHERE);
584
585         ret = wlt_window_create_widget(wnd, &theme->widget, theme);
586         if (ret) {
587                 log_error("cannot create widget");
588                 goto err_free;
589         }
590
591         wlt_widget_set_destroy_cb(theme->widget, widget_destroy);
592         wlt_widget_set_redraw_cb(theme->widget, widget_redraw);
593         wlt_widget_set_resize_cb(theme->widget, widget_prepare_resize,
594                                  widget_resize);
595         wlt_widget_set_pointer_cb(theme->widget, widget_pointer_enter,
596                                   widget_pointer_leave, widget_pointer_motion,
597                                   widget_pointer_button);
598         wlt_widget_set_keyboard_cb(theme->widget, widget_key);
599         *out = theme;
600         return 0;
601
602 err_free:
603         free(theme);
604         return ret;
605 }
606
607 void wlt_theme_destroy(struct wlt_theme *theme)
608 {
609         if (!theme)
610                 return;
611
612         wlt_widget_destroy(theme->widget);
613 }