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>
583 lines
15 KiB
C
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);
|
|
}
|