kmscon/src/wlt_theme.c
David Herrmann 52edafe08c conf: allow multiple grabs and keysyms per grab-option
We allow users to specify grabs on the command-line or in config-options.
We sometimes want multiple grabs to do the same. Therefore, this changes
the grab-parser and handlers to allow this.

Furthermore, this adds support for keysyms that are assembled of multiple
syms. This isn't needed by any keymap, yet, but might be used in the
future.

Last but not least, you can also specify modifiers only now. This might be
used by subsystems to use the default keysyms but let the user specify
what modifiers should be used.
However, be careful when specifying modifiers-only for normal grabs as
this means the grab is executed everytime the modifier is reported with
_any_ key.

Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
2012-10-15 17:30:27 +02:00

614 lines
16 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 "shl_misc.h"
#include "wlt_main.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;
if (flags & WLT_WINDOW_FULLSCREEN)
return;
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;
if (flags & WLT_WINDOW_FULLSCREEN)
return;
/* 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));
if (flags & WLT_WINDOW_FULLSCREEN)
return;
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 bool widget_key(struct wlt_widget *widget, unsigned int mask,
uint32_t sym, uint32_t ascii, uint32_t state,
bool handled, void *data)
{
struct wlt_theme *theme = data;
if (handled || state != WL_KEYBOARD_KEY_STATE_PRESSED)
return false;
if (conf_grab_matches(wlt_conf.grab_fullscreen,
mask, 1, &sym)) {
wlt_window_toggle_fullscreen(theme->wnd);
return true;
}
return false;
}
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);
wlt_widget_set_keyboard_cb(theme->widget, widget_key);
*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);
}