/* * wlt - Theme Helper * * Copyright (c) 2012 David Herrmann * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* * Wayland Terminal theme/decoration drawing helper */ #include #include #include #include #include #include "log.h" #include "wlt_theme.h" #include "wlt_toolkit.h" #define LOG_SUBSYSTEM "wlt_theme" enum theme_location { LOC_NOWHERE, LOC_SOMEWHERE, LOC_RESIZE_TOP, LOC_RESIZE_BOTTOM, LOC_RESIZE_LEFT, LOC_RESIZE_RIGHT, LOC_RESIZE_TOP_LEFT, LOC_RESIZE_TOP_RIGHT, LOC_RESIZE_BOTTOM_LEFT, LOC_RESIZE_BOTTOM_RIGHT, LOC_CONTROL, LOC_MINIMIZE, LOC_MAXIMIZE, LOC_CLOSE, }; struct wlt_theme { struct wlt_window *wnd; struct wlt_widget *widget; struct wlt_shm_buffer buffer; struct wlt_rect alloc; unsigned int control_height; unsigned int frame_width; unsigned int resize_margin; unsigned int button_size; unsigned int button_padding; unsigned int button_margin; unsigned int pointer_x; unsigned int pointer_y; unsigned int pointer_loc; bool pointer_pressed; unsigned int pointer_grabbed; }; static void draw_control(struct wlt_theme *theme) { uint8_t *dst; uint32_t *line, backcol; unsigned int i, j; unsigned int b1off, b2off, b3off; /* background */ if (theme->pointer_loc == LOC_NOWHERE) backcol = (0x60 << 24) | (0xaa << 16) | (0xaa << 8) | 0xaa; else backcol = (0x20 << 24) | (0xee << 16) | (0xee << 8) | 0xee; dst = theme->buffer.data; for (i = 0; i < theme->control_height; ++i) { line = (uint32_t*)dst; for (j = 0; j < theme->buffer.width; ++j) { if (i == 0 || i == theme->control_height - 1 || j == 0 || j == theme->buffer.width - 1) line[j] = 0xff << 24; else line[j] = backcol; } dst += theme->buffer.stride; } /* buttons */ b1off = theme->buffer.width - theme->button_margin - theme->button_size; b2off = b1off - theme->button_padding - theme->button_size; b3off = b2off - theme->button_padding - theme->button_size; dst = theme->buffer.data + theme->buffer.stride * theme->button_margin; for (i = 0; i < theme->button_size; ++i) { line = (uint32_t*)dst; for (j = 0; j < theme->buffer.width; ++j) { if (j >= b1off && j < b1off + theme->button_size) { if (i == 0 || i == theme->button_size - 1 || j == b1off || j == b1off + theme->button_size - 1) line[j] = 0xff << 24; else if (theme->pointer_loc == LOC_CLOSE && theme->pointer_pressed && theme->pointer_grabbed == LOC_CLOSE) line[j] = (0xff << 24) | 0x1f1f1f1f; else if (theme->pointer_loc == LOC_CLOSE && !theme->pointer_pressed) line[j] = 0xffffffff; else line[j] = (0xff << 24) | 0x33333333; } else if (j >= b2off && j < b2off + theme->button_size) { if (i == 0 || i == theme->button_size - 1 || j == b2off || j == b2off + theme->button_size - 1) line[j] = 0xff << 24; else if (theme->pointer_loc == LOC_MAXIMIZE && theme->pointer_pressed && theme->pointer_grabbed == LOC_MAXIMIZE) line[j] = (0xff << 24) | 0x1f1f1f1f; else if (theme->pointer_loc == LOC_MAXIMIZE && !theme->pointer_pressed) line[j] = 0xffffffff; else line[j] = (0xff << 24) | 0x66666666; } else if (j >= b3off && j < b3off + theme->button_size) { if (i == 0 || i == theme->button_size - 1 || j == b3off || j == b3off + theme->button_size - 1) line[j] = 0xff << 24; else if (theme->pointer_loc == LOC_MINIMIZE && theme->pointer_pressed && theme->pointer_grabbed == LOC_MINIMIZE) line[j] = (0xff << 24) | 0x1f1f1f1f; else if (theme->pointer_loc == LOC_MINIMIZE && !theme->pointer_pressed) line[j] = 0xffffffff; else line[j] = (0xff << 24) | 0xaaaaaaaa; } } dst += theme->buffer.stride; } } static void draw_frame(struct wlt_theme *theme) { uint8_t *dst; uint32_t *line; unsigned int i, j, height; /* top frame */ dst = theme->buffer.data + theme->buffer.stride * theme->control_height; for (i = 0; i < theme->frame_width; ++i) { line = (uint32_t*)dst; for (j = 0; j < theme->buffer.width; ++j) line[j] = 0xa0 << 24; dst += theme->buffer.stride; } /* bottom frame */ dst = theme->buffer.data + theme->buffer.stride * (theme->buffer.height - theme->frame_width); for (i = 0; i < theme->frame_width; ++i) { line = (uint32_t*)dst; for (j = 0; j < theme->buffer.width; ++j) line[j] = 0xa0 << 24; dst += theme->buffer.stride; } /* left frame */ dst = theme->buffer.data + theme->buffer.stride * (theme->control_height + theme->frame_width); height = theme->buffer.height - theme->control_height - theme->frame_width * 2; for (i = 0; i < height; ++i) { line = (uint32_t*)dst; for (j = 0; j < theme->frame_width; ++j) line[j] = 0xa0 << 24; dst += theme->buffer.stride; } /* right frame */ dst = theme->buffer.data + theme->buffer.stride * (theme->control_height + theme->frame_width); height = theme->buffer.height - theme->control_height - theme->frame_width * 2; for (i = 0; i < height; ++i) { line = (uint32_t*)dst; line += theme->buffer.width - theme->frame_width; for (j = 0; j < theme->frame_width; ++j) line[j] = 0xa0 << 24; dst += theme->buffer.stride; } } static void widget_draw_fallback(struct wlt_theme *theme) { uint8_t *dst; uint32_t *line; unsigned int i, j; dst = theme->buffer.data; for (i = 0; i < theme->buffer.height; ++i) { line = (uint32_t*)dst; for (j = 0; j < theme->buffer.width; ++j) { line[j] = 0xff << 24; } dst += theme->buffer.stride; } } static void widget_redraw(struct wlt_widget *widget, void *data) { struct wlt_theme *theme = data; unsigned int width, height; width = theme->buffer.width; height = theme->buffer.height; if (width < 2 || width < 2 * theme->frame_width) { widget_draw_fallback(theme); } else if (height < theme->control_height + 2 * theme->frame_width) { widget_draw_fallback(theme); } else { draw_frame(theme); draw_control(theme); } } static void widget_prepare_resize(struct wlt_widget *widget, unsigned int *width, unsigned int *height, void *data) { struct wlt_theme *theme = data; unsigned int minw, minh; minw = theme->frame_width * 2; minh = theme->control_height + theme->frame_width * 2; if (*width < minw) *width = minw; if (*height < minh) *height = minh; } static void widget_resize(struct wlt_widget *widget, struct wlt_rect *alloc, void *data) { struct wlt_theme *theme = data; unsigned int nwidth, nheight; wlt_window_get_buffer(theme->wnd, alloc, &theme->buffer); memcpy(&theme->alloc, alloc, sizeof(*alloc)); alloc->x = theme->frame_width; alloc->y = theme->control_height + theme->frame_width; nwidth = alloc->width - 2 * theme->frame_width; nheight = alloc->height; nheight -= (theme->control_height + 2 * theme->frame_width); if (nwidth > alloc->width || nheight > alloc->height) { alloc->width = 0; alloc->height = 0; } else { alloc->width = nwidth; alloc->height = nheight; } } static unsigned int get_pointer_location(struct wlt_theme *theme) { unsigned int m = theme->resize_margin; unsigned int b1off, b2off, b3off; if (theme->pointer_y < m) { if (theme->pointer_x < m) return LOC_RESIZE_TOP_LEFT; else if (theme->pointer_x >= theme->buffer.width - m) return LOC_RESIZE_TOP_RIGHT; else return LOC_RESIZE_TOP; } if (theme->pointer_y >= theme->buffer.height - m) { if (theme->pointer_x < m) return LOC_RESIZE_BOTTOM_LEFT; else if (theme->pointer_x >= theme->buffer.width - m) return LOC_RESIZE_BOTTOM_RIGHT; else return LOC_RESIZE_BOTTOM; } if (theme->pointer_x < m) return LOC_RESIZE_LEFT; if (theme->pointer_x >= theme->buffer.width - m) return LOC_RESIZE_RIGHT; if (theme->pointer_y < theme->control_height) { b1off = theme->buffer.width - theme->button_margin - theme->button_size; b2off = b1off - theme->button_padding - theme->button_size; b3off = b2off - theme->button_padding - theme->button_size; if (theme->pointer_y >= theme->button_margin && theme->pointer_y < theme->control_height - theme->button_margin) { if (theme->pointer_x >= b1off && theme->pointer_x < b1off + theme->button_size) return LOC_CLOSE; if (theme->pointer_x >= b2off && theme->pointer_x < b2off + theme->button_size) return LOC_MAXIMIZE; if (theme->pointer_x >= b3off && theme->pointer_x < b3off + theme->button_size) return LOC_MINIMIZE; } return LOC_CONTROL; } return LOC_SOMEWHERE; } static void set_pointer_location(struct wlt_theme *theme, unsigned int loc) { if (theme->pointer_loc == loc) return; theme->pointer_loc = loc; if (loc == LOC_NOWHERE) { theme->pointer_x = -1; theme->pointer_y = -1; } wlt_window_schedule_redraw(theme->wnd); } static void widget_pointer_motion(struct wlt_widget *widget, unsigned int x, unsigned int y, void *data) { struct wlt_theme *theme = data; if (!wlt_rect_contains(&theme->alloc, x, y)) { set_pointer_location(theme, LOC_NOWHERE); return; } else { theme->pointer_x = x + theme->alloc.x; theme->pointer_y = y + theme->alloc.y; set_pointer_location(theme, get_pointer_location(theme)); } switch (theme->pointer_loc) { case LOC_RESIZE_LEFT: wlt_window_set_cursor(theme->wnd, WLT_CURSOR_LEFT); break; case LOC_RESIZE_RIGHT: wlt_window_set_cursor(theme->wnd, WLT_CURSOR_RIGHT); break; case LOC_RESIZE_TOP: wlt_window_set_cursor(theme->wnd, WLT_CURSOR_TOP); break; case LOC_RESIZE_BOTTOM: wlt_window_set_cursor(theme->wnd, WLT_CURSOR_BOTTOM); break; case LOC_RESIZE_TOP_LEFT: wlt_window_set_cursor(theme->wnd, WLT_CURSOR_TOP_LEFT); break; case LOC_RESIZE_TOP_RIGHT: wlt_window_set_cursor(theme->wnd, WLT_CURSOR_TOP_RIGHT); break; case LOC_RESIZE_BOTTOM_LEFT: wlt_window_set_cursor(theme->wnd, WLT_CURSOR_BOTTOM_LEFT); break; case LOC_RESIZE_BOTTOM_RIGHT: wlt_window_set_cursor(theme->wnd, WLT_CURSOR_BOTTOM_RIGHT); break; default: wlt_window_set_cursor(theme->wnd, WLT_CURSOR_LEFT_PTR); } } static void widget_pointer_enter(struct wlt_widget *widget, unsigned int x, unsigned int y, void *data) { struct wlt_theme *theme = data; widget_pointer_motion(widget, x, y, theme); } static void widget_pointer_leave(struct wlt_widget *widget, void *data) { struct wlt_theme *theme = data; if (theme->pointer_pressed) { theme->pointer_pressed = false; wlt_window_schedule_redraw(theme->wnd); } set_pointer_location(theme, LOC_NOWHERE); } static void button_action(struct wlt_theme *theme) { if (theme->pointer_grabbed != theme->pointer_loc) return; switch (theme->pointer_loc) { case LOC_CLOSE: wlt_window_close(theme->wnd); break; case LOC_MAXIMIZE: break; case LOC_MINIMIZE: break; } } static void widget_pointer_button(struct wlt_widget *widget, uint32_t button, uint32_t state, void *data) { struct wlt_theme *theme = data; if (button != BTN_LEFT) return; if (state != WL_POINTER_BUTTON_STATE_PRESSED) { if (theme->pointer_pressed) { button_action(theme); theme->pointer_pressed = false; theme->pointer_grabbed = LOC_NOWHERE; wlt_window_schedule_redraw(theme->wnd); } return; } if (!theme->pointer_pressed) { theme->pointer_pressed = true; theme->pointer_grabbed = theme->pointer_loc; wlt_window_schedule_redraw(theme->wnd); } switch (theme->pointer_loc) { case LOC_RESIZE_LEFT: wlt_window_resize(theme->wnd, WL_SHELL_SURFACE_RESIZE_LEFT); break; case LOC_RESIZE_RIGHT: wlt_window_resize(theme->wnd, WL_SHELL_SURFACE_RESIZE_RIGHT); break; case LOC_RESIZE_TOP: wlt_window_resize(theme->wnd, WL_SHELL_SURFACE_RESIZE_TOP); break; case LOC_RESIZE_BOTTOM: wlt_window_resize(theme->wnd, WL_SHELL_SURFACE_RESIZE_BOTTOM); break; case LOC_RESIZE_TOP_LEFT: wlt_window_resize(theme->wnd, WL_SHELL_SURFACE_RESIZE_TOP_LEFT); break; case LOC_RESIZE_TOP_RIGHT: wlt_window_resize(theme->wnd, WL_SHELL_SURFACE_RESIZE_TOP_RIGHT); break; case LOC_RESIZE_BOTTOM_LEFT: wlt_window_resize(theme->wnd, WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT); break; case LOC_RESIZE_BOTTOM_RIGHT: wlt_window_resize(theme->wnd, WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT); break; case LOC_CONTROL: wlt_window_move(theme->wnd); break; } } static void widget_destroy(struct wlt_widget *widget, void *data) { struct wlt_theme *theme = data; log_debug("destroy theme"); free(theme); } int wlt_theme_new(struct wlt_theme **out, struct wlt_window *wnd) { struct wlt_theme *theme; int ret; if (!out || !wnd) return -EINVAL; log_debug("create new theme"); theme = malloc(sizeof(*theme)); if (!theme) return -ENOMEM; memset(theme, 0, sizeof(*theme)); theme->wnd = wnd; theme->control_height = 25; theme->frame_width = 5; theme->resize_margin = 5; theme->button_size = 15; theme->button_padding = 3; theme->button_margin = 5; theme->pointer_grabbed = LOC_NOWHERE; set_pointer_location(theme, LOC_NOWHERE); ret = wlt_window_create_widget(wnd, &theme->widget, theme); if (ret) { log_error("cannot create widget"); goto err_free; } wlt_widget_set_destroy_cb(theme->widget, widget_destroy); wlt_widget_set_redraw_cb(theme->widget, widget_redraw); wlt_widget_set_resize_cb(theme->widget, widget_prepare_resize, widget_resize); wlt_widget_set_pointer_cb(theme->widget, widget_pointer_enter, widget_pointer_leave, widget_pointer_motion, widget_pointer_button); *out = theme; return 0; err_free: free(theme); return ret; } void wlt_theme_destroy(struct wlt_theme *theme) { if (!theme) return; wlt_widget_destroy(theme->widget); }