kmscon/src/wlt_theme.c
David Herrmann 1b1ff11355 wlt: toolkit: pass flags to resize and redraw callbacks
If a window is maximized, fullscreen or should be drawn without
decorations, then the widgets must be notified about it. Hence, this adds
a flags argument to resize and redraw callbacks.

Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
2012-10-01 14:14:28 +02:00

583 lines
15 KiB
C

/*
* wlt - Theme Helper
*
* Copyright (c) 2012 David Herrmann <dh.herrmann@googlemail.com>
*
* 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 <errno.h>
#include <linux/input.h>
#include <stdlib.h>
#include <string.h>
#include <wayland-client.h>
#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, unsigned int flags,
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 ||
width < 2 * theme->button_margin + 2 * theme->button_padding +
3 * theme->button_size) {
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 flags,
unsigned int width, unsigned int height,
unsigned int *min_width,
unsigned int *min_height,
unsigned int *new_width,
unsigned int *new_height,
void *data)
{
struct wlt_theme *theme = data;
unsigned int minw, minh;
/* set minimal size */
minw = 2 * theme->button_margin + 2 * theme->button_padding +
3 * theme->button_size + *new_width;
minh = theme->button_size + 2 * theme->button_padding +
2 * theme->frame_width + *new_height;
if (*min_width < minw)
*min_width = minw;
if (*min_height < minh)
*min_height = minh;
/* set margin size */
minw = 2 * theme->frame_width;
minh = theme->control_height + 2 * theme->frame_width;
*new_width += minw;
*new_height += minh;
}
static void widget_resize(struct wlt_widget *widget, unsigned int flags,
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:
wlt_window_toggle_maximize(theme->wnd);
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);
}