96c1caa8a2b9d8251c6840a6554abb57026f82ca
[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 "wlt_theme.h"
37 #include "wlt_toolkit.h"
38
39 #define LOG_SUBSYSTEM "wlt_theme"
40
41 enum theme_location {
42         LOC_NOWHERE,
43         LOC_SOMEWHERE,
44         LOC_RESIZE_TOP,
45         LOC_RESIZE_BOTTOM,
46         LOC_RESIZE_LEFT,
47         LOC_RESIZE_RIGHT,
48         LOC_RESIZE_TOP_LEFT,
49         LOC_RESIZE_TOP_RIGHT,
50         LOC_RESIZE_BOTTOM_LEFT,
51         LOC_RESIZE_BOTTOM_RIGHT,
52         LOC_CONTROL,
53         LOC_MINIMIZE,
54         LOC_MAXIMIZE,
55         LOC_CLOSE,
56 };
57
58 struct wlt_theme {
59         struct wlt_window *wnd;
60         struct wlt_widget *widget;
61         struct wlt_shm_buffer buffer;
62         struct wlt_rect alloc;
63         unsigned int control_height;
64         unsigned int frame_width;
65         unsigned int resize_margin;
66         unsigned int button_size;
67         unsigned int button_padding;
68         unsigned int button_margin;
69
70         unsigned int pointer_x;
71         unsigned int pointer_y;
72         unsigned int pointer_loc;
73         bool pointer_pressed;
74         unsigned int pointer_grabbed;
75 };
76
77 static void draw_control(struct wlt_theme *theme)
78 {
79         uint8_t *dst;
80         uint32_t *line, backcol;
81         unsigned int i, j;
82         unsigned int b1off, b2off, b3off;
83
84         /* background */
85         if (theme->pointer_loc == LOC_NOWHERE)
86                 backcol = (0x60 << 24) | (0xaa << 16) |
87                           (0xaa << 8) | 0xaa;
88         else
89                 backcol = (0x20 << 24) | (0xee << 16) |
90                           (0xee << 8) | 0xee;
91         dst = theme->buffer.data;
92         for (i = 0; i < theme->control_height; ++i) {
93                 line = (uint32_t*)dst;
94                 for (j = 0; j < theme->buffer.width; ++j) {
95                         if (i == 0 || i == theme->control_height - 1 ||
96                             j == 0 || j == theme->buffer.width - 1)
97                                 line[j] = 0xff << 24;
98                         else
99                                 line[j] = backcol;
100                 }
101                 dst += theme->buffer.stride;
102         }
103
104         /* buttons */
105         b1off = theme->buffer.width - theme->button_margin - theme->button_size;
106         b2off = b1off - theme->button_padding - theme->button_size;
107         b3off = b2off - theme->button_padding - theme->button_size;
108         dst = theme->buffer.data + theme->buffer.stride * theme->button_margin;
109         for (i = 0; i < theme->button_size; ++i) {
110                 line = (uint32_t*)dst;
111                 for (j = 0; j < theme->buffer.width; ++j) {
112                         if (j >= b1off &&
113                             j < b1off + theme->button_size) {
114                                 if (i == 0 ||
115                                     i == theme->button_size - 1 ||
116                                     j == b1off ||
117                                     j == b1off + theme->button_size - 1)
118                                         line[j] = 0xff << 24;
119                                 else if (theme->pointer_loc == LOC_CLOSE &&
120                                          theme->pointer_pressed &&
121                                          theme->pointer_grabbed == LOC_CLOSE)
122                                         line[j] = (0xff << 24) | 0x1f1f1f1f;
123                                 else if (theme->pointer_loc == LOC_CLOSE &&
124                                          !theme->pointer_pressed)
125                                         line[j] = 0xffffffff;
126                                 else
127                                         line[j] = (0xff << 24) | 0x33333333;
128                         } else if (j >= b2off &&
129                                    j < b2off + theme->button_size) {
130                                 if (i == 0 ||
131                                     i == theme->button_size - 1 ||
132                                     j == b2off ||
133                                     j == b2off + theme->button_size - 1)
134                                         line[j] = 0xff << 24;
135                                 else if (theme->pointer_loc == LOC_MAXIMIZE &&
136                                          theme->pointer_pressed &&
137                                          theme->pointer_grabbed == LOC_MAXIMIZE)
138                                         line[j] = (0xff << 24) | 0x1f1f1f1f;
139                                 else if (theme->pointer_loc == LOC_MAXIMIZE &&
140                                          !theme->pointer_pressed)
141                                         line[j] = 0xffffffff;
142                                 else
143                                         line[j] = (0xff << 24) | 0x66666666;
144                         } else if (j >= b3off &&
145                                    j < b3off + theme->button_size) {
146                                 if (i == 0 ||
147                                     i == theme->button_size - 1 ||
148                                     j == b3off ||
149                                     j == b3off + theme->button_size - 1)
150                                         line[j] = 0xff << 24;
151                                 else if (theme->pointer_loc == LOC_MINIMIZE &&
152                                          theme->pointer_pressed &&
153                                          theme->pointer_grabbed == LOC_MINIMIZE)
154                                         line[j] = (0xff << 24) | 0x1f1f1f1f;
155                                 else if (theme->pointer_loc == LOC_MINIMIZE &&
156                                          !theme->pointer_pressed)
157                                         line[j] = 0xffffffff;
158                                 else
159                                         line[j] = (0xff << 24) | 0xaaaaaaaa;
160                         }
161                 }
162                 dst += theme->buffer.stride;
163         }
164 }
165
166 static void draw_frame(struct wlt_theme *theme)
167 {
168         uint8_t *dst;
169         uint32_t *line;
170         unsigned int i, j, height;
171
172         /* top frame */
173         dst = theme->buffer.data + theme->buffer.stride *
174               theme->control_height;
175         for (i = 0; i < theme->frame_width; ++i) {
176                 line = (uint32_t*)dst;
177                 for (j = 0; j < theme->buffer.width; ++j)
178                         line[j] = 0xa0 << 24;
179                 dst += theme->buffer.stride;
180         }
181
182         /* bottom frame */
183         dst = theme->buffer.data + theme->buffer.stride *
184               (theme->buffer.height - theme->frame_width);
185         for (i = 0; i < theme->frame_width; ++i) {
186                 line = (uint32_t*)dst;
187                 for (j = 0; j < theme->buffer.width; ++j)
188                         line[j] = 0xa0 << 24;
189                 dst += theme->buffer.stride;
190         }
191
192         /* left frame */
193         dst = theme->buffer.data + theme->buffer.stride *
194               (theme->control_height + theme->frame_width);
195         height = theme->buffer.height - theme->control_height -
196                  theme->frame_width * 2;
197         for (i = 0; i < height; ++i) {
198                 line = (uint32_t*)dst;
199                 for (j = 0; j < theme->frame_width; ++j)
200                         line[j] = 0xa0 << 24;
201                 dst += theme->buffer.stride;
202         }
203
204         /* right frame */
205         dst = theme->buffer.data + theme->buffer.stride *
206               (theme->control_height + theme->frame_width);
207         height = theme->buffer.height - theme->control_height -
208                  theme->frame_width * 2;
209         for (i = 0; i < height; ++i) {
210                 line = (uint32_t*)dst;
211                 line += theme->buffer.width - theme->frame_width;
212                 for (j = 0; j < theme->frame_width; ++j)
213                         line[j] = 0xa0 << 24;
214                 dst += theme->buffer.stride;
215         }
216 }
217
218 static void widget_draw_fallback(struct wlt_theme *theme)
219 {
220         uint8_t *dst;
221         uint32_t *line;
222         unsigned int i, j;
223
224         dst = theme->buffer.data;
225         for (i = 0; i < theme->buffer.height; ++i) {
226                 line = (uint32_t*)dst;
227                 for (j = 0; j < theme->buffer.width; ++j) {
228                         line[j] = 0xff << 24;
229                 }
230                 dst += theme->buffer.stride;
231         }
232 }
233
234 static void widget_redraw(struct wlt_widget *widget, void *data)
235 {
236         struct wlt_theme *theme = data;
237         unsigned int width, height;
238
239         width = theme->buffer.width;
240         height = theme->buffer.height;
241         if (width < 2 ||
242             width < 2 * theme->frame_width) {
243                 widget_draw_fallback(theme);
244         } else if (height < theme->control_height + 2 * theme->frame_width) {
245                 widget_draw_fallback(theme);
246         } else {
247                 draw_frame(theme);
248                 draw_control(theme);
249         }
250 }
251
252 static void widget_prepare_resize(struct wlt_widget *widget,
253                                   unsigned int *width, unsigned int *height,
254                                   void *data)
255 {
256         struct wlt_theme *theme = data;
257         unsigned int minw, minh;
258
259         minw = theme->frame_width * 2;
260         minh = theme->control_height + theme->frame_width * 2;
261         if (*width < minw)
262                 *width = minw;
263         if (*height < minh)
264                 *height = minh;
265 }
266
267 static void widget_resize(struct wlt_widget *widget, struct wlt_rect *alloc,
268                           void *data)
269 {
270         struct wlt_theme *theme = data;
271         unsigned int nwidth, nheight;
272
273         wlt_window_get_buffer(theme->wnd, alloc, &theme->buffer);
274         memcpy(&theme->alloc, alloc, sizeof(*alloc));
275
276         alloc->x = theme->frame_width;
277         alloc->y = theme->control_height + theme->frame_width;
278         nwidth = alloc->width - 2 * theme->frame_width;
279         nheight = alloc->height;
280         nheight -= (theme->control_height + 2 * theme->frame_width);
281
282         if (nwidth > alloc->width || nheight > alloc->height) {
283                 alloc->width = 0;
284                 alloc->height = 0;
285         } else {
286                 alloc->width = nwidth;
287                 alloc->height = nheight;
288         }
289 }
290
291 static unsigned int get_pointer_location(struct wlt_theme *theme)
292 {
293         unsigned int m = theme->resize_margin;
294         unsigned int b1off, b2off, b3off;
295
296         if (theme->pointer_y < m) {
297                 if (theme->pointer_x < m)
298                         return LOC_RESIZE_TOP_LEFT;
299                 else if (theme->pointer_x >= theme->buffer.width - m)
300                         return LOC_RESIZE_TOP_RIGHT;
301                 else
302                         return LOC_RESIZE_TOP;
303         }
304
305         if (theme->pointer_y >= theme->buffer.height - m) {
306                 if (theme->pointer_x < m)
307                         return LOC_RESIZE_BOTTOM_LEFT;
308                 else if (theme->pointer_x >= theme->buffer.width - m)
309                         return LOC_RESIZE_BOTTOM_RIGHT;
310                 else
311                         return LOC_RESIZE_BOTTOM;
312         }
313
314         if (theme->pointer_x < m)
315                 return LOC_RESIZE_LEFT;
316
317         if (theme->pointer_x >= theme->buffer.width - m)
318                 return LOC_RESIZE_RIGHT;
319
320         if (theme->pointer_y < theme->control_height) {
321                 b1off = theme->buffer.width - theme->button_margin -
322                         theme->button_size;
323                 b2off = b1off - theme->button_padding - theme->button_size;
324                 b3off = b2off - theme->button_padding - theme->button_size;
325
326                 if (theme->pointer_y >= theme->button_margin &&
327                     theme->pointer_y < theme->control_height -
328                                        theme->button_margin) {
329                         if (theme->pointer_x >= b1off &&
330                             theme->pointer_x < b1off + theme->button_size)
331                                 return LOC_CLOSE;
332                         if (theme->pointer_x >= b2off &&
333                             theme->pointer_x < b2off + theme->button_size)
334                                 return LOC_MAXIMIZE;
335                         if (theme->pointer_x >= b3off &&
336                             theme->pointer_x < b3off + theme->button_size)
337                                 return LOC_MINIMIZE;
338                 }
339
340                 return LOC_CONTROL;
341         }
342
343         return LOC_SOMEWHERE;
344 }
345
346 static void set_pointer_location(struct wlt_theme *theme, unsigned int loc)
347 {
348         if (theme->pointer_loc == loc)
349                 return;
350
351         theme->pointer_loc = loc;
352         if (loc == LOC_NOWHERE) {
353                 theme->pointer_x = -1;
354                 theme->pointer_y = -1;
355         }
356
357         wlt_window_schedule_redraw(theme->wnd);
358 }
359
360 static void widget_pointer_motion(struct wlt_widget *widget,
361                                   unsigned int x, unsigned int y, void *data)
362 {
363         struct wlt_theme *theme = data;
364
365         if (!wlt_rect_contains(&theme->alloc, x, y)) {
366                 set_pointer_location(theme, LOC_NOWHERE);
367                 return;
368         } else {
369                 theme->pointer_x = x + theme->alloc.x;
370                 theme->pointer_y = y + theme->alloc.y;
371                 set_pointer_location(theme, get_pointer_location(theme));
372         }
373
374         switch (theme->pointer_loc) {
375         case LOC_RESIZE_LEFT:
376                 wlt_window_set_cursor(theme->wnd, WLT_CURSOR_LEFT);
377                 break;
378         case LOC_RESIZE_RIGHT:
379                 wlt_window_set_cursor(theme->wnd, WLT_CURSOR_RIGHT);
380                 break;
381         case LOC_RESIZE_TOP:
382                 wlt_window_set_cursor(theme->wnd, WLT_CURSOR_TOP);
383                 break;
384         case LOC_RESIZE_BOTTOM:
385                 wlt_window_set_cursor(theme->wnd, WLT_CURSOR_BOTTOM);
386                 break;
387         case LOC_RESIZE_TOP_LEFT:
388                 wlt_window_set_cursor(theme->wnd, WLT_CURSOR_TOP_LEFT);
389                 break;
390         case LOC_RESIZE_TOP_RIGHT:
391                 wlt_window_set_cursor(theme->wnd, WLT_CURSOR_TOP_RIGHT);
392                 break;
393         case LOC_RESIZE_BOTTOM_LEFT:
394                 wlt_window_set_cursor(theme->wnd,
395                                       WLT_CURSOR_BOTTOM_LEFT);
396                 break;
397         case LOC_RESIZE_BOTTOM_RIGHT:
398                 wlt_window_set_cursor(theme->wnd,
399                                       WLT_CURSOR_BOTTOM_RIGHT);
400                 break;
401         default:
402                 wlt_window_set_cursor(theme->wnd, WLT_CURSOR_LEFT_PTR);
403         }
404 }
405
406 static void widget_pointer_enter(struct wlt_widget *widget,
407                                  unsigned int x, unsigned int y, void *data)
408 {
409         struct wlt_theme *theme = data;
410
411         widget_pointer_motion(widget, x, y, theme);
412 }
413
414 static void widget_pointer_leave(struct wlt_widget *widget, void *data)
415 {
416         struct wlt_theme *theme = data;
417
418         if (theme->pointer_pressed) {
419                 theme->pointer_pressed = false;
420                 wlt_window_schedule_redraw(theme->wnd);
421         }
422
423         set_pointer_location(theme, LOC_NOWHERE);
424 }
425
426 static void button_action(struct wlt_theme *theme)
427 {
428         if (theme->pointer_grabbed != theme->pointer_loc)
429                 return;
430
431         switch (theme->pointer_loc) {
432         case LOC_CLOSE:
433                 wlt_window_close(theme->wnd);
434                 break;
435         case LOC_MAXIMIZE:
436                 break;
437         case LOC_MINIMIZE:
438                 break;
439         }
440 }
441
442 static void widget_pointer_button(struct wlt_widget *widget,
443                                   uint32_t button, uint32_t state, void *data)
444 {
445         struct wlt_theme *theme = data;
446
447         if (button != BTN_LEFT)
448                 return;
449
450         if (state != WL_POINTER_BUTTON_STATE_PRESSED) {
451                 if (theme->pointer_pressed) {
452                         button_action(theme);
453                         theme->pointer_pressed = false;
454                         theme->pointer_grabbed = LOC_NOWHERE;
455                         wlt_window_schedule_redraw(theme->wnd);
456                 }
457                 return;
458         }
459
460         if (!theme->pointer_pressed) {
461                 theme->pointer_pressed = true;
462                 theme->pointer_grabbed = theme->pointer_loc;
463                 wlt_window_schedule_redraw(theme->wnd);
464         }
465
466         switch (theme->pointer_loc) {
467         case LOC_RESIZE_LEFT:
468                 wlt_window_resize(theme->wnd, WL_SHELL_SURFACE_RESIZE_LEFT);
469                 break;
470         case LOC_RESIZE_RIGHT:
471                 wlt_window_resize(theme->wnd, WL_SHELL_SURFACE_RESIZE_RIGHT);
472                 break;
473         case LOC_RESIZE_TOP:
474                 wlt_window_resize(theme->wnd,
475                         WL_SHELL_SURFACE_RESIZE_TOP);
476                 break;
477         case LOC_RESIZE_BOTTOM:
478                 wlt_window_resize(theme->wnd,
479                         WL_SHELL_SURFACE_RESIZE_BOTTOM);
480                 break;
481         case LOC_RESIZE_TOP_LEFT:
482                 wlt_window_resize(theme->wnd,
483                         WL_SHELL_SURFACE_RESIZE_TOP_LEFT);
484                 break;
485         case LOC_RESIZE_TOP_RIGHT:
486                 wlt_window_resize(theme->wnd,
487                         WL_SHELL_SURFACE_RESIZE_TOP_RIGHT);
488                 break;
489         case LOC_RESIZE_BOTTOM_LEFT:
490                 wlt_window_resize(theme->wnd,
491                         WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT);
492                 break;
493         case LOC_RESIZE_BOTTOM_RIGHT:
494                 wlt_window_resize(theme->wnd,
495                         WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT);
496                 break;
497         case LOC_CONTROL:
498                 wlt_window_move(theme->wnd);
499                 break;
500         }
501 }
502
503 static void widget_destroy(struct wlt_widget *widget, void *data)
504 {
505         struct wlt_theme *theme = data;
506
507         log_debug("destroy theme");
508
509         free(theme);
510 }
511
512 int wlt_theme_new(struct wlt_theme **out, struct wlt_window *wnd)
513 {
514         struct wlt_theme *theme;
515         int ret;
516
517         if (!out || !wnd)
518                 return -EINVAL;
519
520         log_debug("create new theme");
521
522         theme = malloc(sizeof(*theme));
523         if (!theme)
524                 return -ENOMEM;
525         memset(theme, 0, sizeof(*theme));
526         theme->wnd = wnd;
527         theme->control_height = 25;
528         theme->frame_width = 5;
529         theme->resize_margin = 5;
530         theme->button_size = 15;
531         theme->button_padding = 3;
532         theme->button_margin = 5;
533         theme->pointer_grabbed = LOC_NOWHERE;
534         set_pointer_location(theme, LOC_NOWHERE);
535
536         ret = wlt_window_create_widget(wnd, &theme->widget, theme);
537         if (ret) {
538                 log_error("cannot create widget");
539                 goto err_free;
540         }
541
542         wlt_widget_set_destroy_cb(theme->widget, widget_destroy);
543         wlt_widget_set_redraw_cb(theme->widget, widget_redraw);
544         wlt_widget_set_resize_cb(theme->widget, widget_prepare_resize,
545                                  widget_resize);
546         wlt_widget_set_pointer_cb(theme->widget, widget_pointer_enter,
547                                   widget_pointer_leave, widget_pointer_motion,
548                                   widget_pointer_button);
549         *out = theme;
550         return 0;
551
552 err_free:
553         free(theme);
554         return ret;
555 }
556
557 void wlt_theme_destroy(struct wlt_theme *theme)
558 {
559         if (!theme)
560                 return;
561
562         wlt_widget_destroy(theme->widget);
563 }