4 * Copyright (c) 2012 David Herrmann <dh.herrmann@googlemail.com>
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:
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
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.
27 * Wayland Terminal theme/decoration drawing helper
31 #include <linux/input.h>
34 #include <wayland-client.h>
38 #include "wlt_theme.h"
39 #include "wlt_toolkit.h"
41 #define LOG_SUBSYSTEM "wlt_theme"
52 LOC_RESIZE_BOTTOM_LEFT,
53 LOC_RESIZE_BOTTOM_RIGHT,
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;
72 unsigned int pointer_x;
73 unsigned int pointer_y;
74 unsigned int pointer_loc;
76 unsigned int pointer_grabbed;
79 static void draw_control(struct wlt_theme *theme)
82 uint32_t *line, backcol;
84 unsigned int b1off, b2off, b3off;
87 if (theme->pointer_loc == LOC_NOWHERE)
88 backcol = (0x60 << 24) | (0xaa << 16) |
91 backcol = (0x20 << 24) | (0xee << 16) |
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)
103 dst += theme->buffer.stride;
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) {
115 j < b1off + theme->button_size) {
117 i == theme->button_size - 1 ||
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;
129 line[j] = (0xff << 24) | 0x33333333;
130 } else if (j >= b2off &&
131 j < b2off + theme->button_size) {
133 i == theme->button_size - 1 ||
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;
145 line[j] = (0xff << 24) | 0x66666666;
146 } else if (j >= b3off &&
147 j < b3off + theme->button_size) {
149 i == theme->button_size - 1 ||
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;
161 line[j] = (0xff << 24) | 0xaaaaaaaa;
164 dst += theme->buffer.stride;
168 static void draw_frame(struct wlt_theme *theme)
172 unsigned int i, j, height;
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;
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;
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;
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;
220 static void widget_draw_fallback(struct wlt_theme *theme)
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;
232 dst += theme->buffer.stride;
236 static void widget_redraw(struct wlt_widget *widget, unsigned int flags,
239 struct wlt_theme *theme = data;
240 unsigned int width, height;
242 if (flags & WLT_WINDOW_FULLSCREEN)
245 width = theme->buffer.width;
246 height = theme->buffer.height;
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);
260 static void widget_prepare_resize(struct wlt_widget *widget,
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,
269 struct wlt_theme *theme = data;
270 unsigned int minw, minh;
272 if (flags & WLT_WINDOW_FULLSCREEN)
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)
282 if (*min_height < minh)
285 /* set margin size */
286 minw = 2 * theme->frame_width;
287 minh = theme->control_height + 2 * theme->frame_width;
293 static void widget_resize(struct wlt_widget *widget, unsigned int flags,
294 struct wlt_rect *alloc, void *data)
296 struct wlt_theme *theme = data;
297 unsigned int nwidth, nheight;
299 wlt_window_get_buffer(theme->wnd, alloc, &theme->buffer);
300 memcpy(&theme->alloc, alloc, sizeof(*alloc));
302 if (flags & WLT_WINDOW_FULLSCREEN)
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);
311 if (nwidth > alloc->width || nheight > alloc->height) {
315 alloc->width = nwidth;
316 alloc->height = nheight;
320 static unsigned int get_pointer_location(struct wlt_theme *theme)
322 unsigned int m = theme->resize_margin;
323 unsigned int b1off, b2off, b3off;
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;
331 return LOC_RESIZE_TOP;
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;
340 return LOC_RESIZE_BOTTOM;
343 if (theme->pointer_x < m)
344 return LOC_RESIZE_LEFT;
346 if (theme->pointer_x >= theme->buffer.width - m)
347 return LOC_RESIZE_RIGHT;
349 if (theme->pointer_y < theme->control_height) {
350 b1off = theme->buffer.width - theme->button_margin -
352 b2off = b1off - theme->button_padding - theme->button_size;
353 b3off = b2off - theme->button_padding - theme->button_size;
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)
361 if (theme->pointer_x >= b2off &&
362 theme->pointer_x < b2off + theme->button_size)
364 if (theme->pointer_x >= b3off &&
365 theme->pointer_x < b3off + theme->button_size)
372 return LOC_SOMEWHERE;
375 static void set_pointer_location(struct wlt_theme *theme, unsigned int loc)
377 if (theme->pointer_loc == loc)
380 theme->pointer_loc = loc;
381 if (loc == LOC_NOWHERE) {
382 theme->pointer_x = -1;
383 theme->pointer_y = -1;
386 wlt_window_schedule_redraw(theme->wnd);
389 static void widget_pointer_motion(struct wlt_widget *widget,
390 unsigned int x, unsigned int y, void *data)
392 struct wlt_theme *theme = data;
394 if (!wlt_rect_contains(&theme->alloc, x, y)) {
395 set_pointer_location(theme, LOC_NOWHERE);
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));
403 switch (theme->pointer_loc) {
404 case LOC_RESIZE_LEFT:
405 wlt_window_set_cursor(theme->wnd, WLT_CURSOR_LEFT);
407 case LOC_RESIZE_RIGHT:
408 wlt_window_set_cursor(theme->wnd, WLT_CURSOR_RIGHT);
411 wlt_window_set_cursor(theme->wnd, WLT_CURSOR_TOP);
413 case LOC_RESIZE_BOTTOM:
414 wlt_window_set_cursor(theme->wnd, WLT_CURSOR_BOTTOM);
416 case LOC_RESIZE_TOP_LEFT:
417 wlt_window_set_cursor(theme->wnd, WLT_CURSOR_TOP_LEFT);
419 case LOC_RESIZE_TOP_RIGHT:
420 wlt_window_set_cursor(theme->wnd, WLT_CURSOR_TOP_RIGHT);
422 case LOC_RESIZE_BOTTOM_LEFT:
423 wlt_window_set_cursor(theme->wnd,
424 WLT_CURSOR_BOTTOM_LEFT);
426 case LOC_RESIZE_BOTTOM_RIGHT:
427 wlt_window_set_cursor(theme->wnd,
428 WLT_CURSOR_BOTTOM_RIGHT);
431 wlt_window_set_cursor(theme->wnd, WLT_CURSOR_LEFT_PTR);
435 static void widget_pointer_enter(struct wlt_widget *widget,
436 unsigned int x, unsigned int y, void *data)
438 struct wlt_theme *theme = data;
440 widget_pointer_motion(widget, x, y, theme);
443 static void widget_pointer_leave(struct wlt_widget *widget, void *data)
445 struct wlt_theme *theme = data;
447 if (theme->pointer_pressed) {
448 theme->pointer_pressed = false;
449 wlt_window_schedule_redraw(theme->wnd);
452 set_pointer_location(theme, LOC_NOWHERE);
455 static void button_action(struct wlt_theme *theme)
457 if (theme->pointer_grabbed != theme->pointer_loc)
460 switch (theme->pointer_loc) {
462 wlt_window_close(theme->wnd);
465 wlt_window_toggle_maximize(theme->wnd);
472 static void widget_pointer_button(struct wlt_widget *widget,
473 uint32_t button, uint32_t state, void *data)
475 struct wlt_theme *theme = data;
477 if (button != BTN_LEFT)
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);
490 if (!theme->pointer_pressed) {
491 theme->pointer_pressed = true;
492 theme->pointer_grabbed = theme->pointer_loc;
493 wlt_window_schedule_redraw(theme->wnd);
496 switch (theme->pointer_loc) {
497 case LOC_RESIZE_LEFT:
498 wlt_window_resize(theme->wnd, WL_SHELL_SURFACE_RESIZE_LEFT);
500 case LOC_RESIZE_RIGHT:
501 wlt_window_resize(theme->wnd, WL_SHELL_SURFACE_RESIZE_RIGHT);
504 wlt_window_resize(theme->wnd,
505 WL_SHELL_SURFACE_RESIZE_TOP);
507 case LOC_RESIZE_BOTTOM:
508 wlt_window_resize(theme->wnd,
509 WL_SHELL_SURFACE_RESIZE_BOTTOM);
511 case LOC_RESIZE_TOP_LEFT:
512 wlt_window_resize(theme->wnd,
513 WL_SHELL_SURFACE_RESIZE_TOP_LEFT);
515 case LOC_RESIZE_TOP_RIGHT:
516 wlt_window_resize(theme->wnd,
517 WL_SHELL_SURFACE_RESIZE_TOP_RIGHT);
519 case LOC_RESIZE_BOTTOM_LEFT:
520 wlt_window_resize(theme->wnd,
521 WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT);
523 case LOC_RESIZE_BOTTOM_RIGHT:
524 wlt_window_resize(theme->wnd,
525 WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT);
528 wlt_window_move(theme->wnd);
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)
537 struct wlt_theme *theme = data;
539 if (handled || state != WL_KEYBOARD_KEY_STATE_PRESSED)
542 if (conf_grab_matches(wlt_conf.grab_fullscreen,
544 wlt_window_toggle_fullscreen(theme->wnd);
552 static void widget_destroy(struct wlt_widget *widget, void *data)
554 struct wlt_theme *theme = data;
556 log_debug("destroy theme");
561 int wlt_theme_new(struct wlt_theme **out, struct wlt_window *wnd)
563 struct wlt_theme *theme;
569 log_debug("create new theme");
571 theme = malloc(sizeof(*theme));
574 memset(theme, 0, sizeof(*theme));
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);
585 ret = wlt_window_create_widget(wnd, &theme->widget, theme);
587 log_error("cannot create widget");
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,
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);
607 void wlt_theme_destroy(struct wlt_theme *theme)
612 wlt_widget_destroy(theme->widget);