You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2722 lines
76 KiB
C
2722 lines
76 KiB
C
/**
|
|
* @file wayland.c
|
|
*
|
|
*/
|
|
|
|
/*********************
|
|
* INCLUDES
|
|
*********************/
|
|
#include "wayland.h"
|
|
#include "smm.h"
|
|
|
|
#if USE_WAYLAND
|
|
|
|
#include <stdio.h>
|
|
#include <stddef.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
|
|
#include <sys/mman.h>
|
|
|
|
#include <linux/input.h>
|
|
#include <linux/input-event-codes.h>
|
|
|
|
#include <wayland-client.h>
|
|
#include <wayland-cursor.h>
|
|
#include <xkbcommon/xkbcommon.h>
|
|
|
|
#if !(LV_WAYLAND_XDG_SHELL || LV_WAYLAND_WL_SHELL)
|
|
#error "Please select at least one shell integration for Wayland driver"
|
|
#endif
|
|
|
|
#if LV_WAYLAND_XDG_SHELL
|
|
#include "protocols/wayland-xdg-shell-client-protocol.h"
|
|
#endif
|
|
|
|
/*********************
|
|
* DEFINES
|
|
*********************/
|
|
|
|
#define BYTES_PER_PIXEL ((LV_COLOR_DEPTH + 7) / 8)
|
|
#define LVGL_DRAW_BUFFER_DIV (8)
|
|
#define DMG_CACHE_CAPACITY (32)
|
|
#define TAG_LOCAL (0)
|
|
#define TAG_BUFFER_DAMAGE (1)
|
|
|
|
#if LV_WAYLAND_CLIENT_SIDE_DECORATIONS
|
|
#define TITLE_BAR_HEIGHT 24
|
|
#define BORDER_SIZE 2
|
|
#define BUTTON_MARGIN LV_MAX((TITLE_BAR_HEIGHT / 6), BORDER_SIZE)
|
|
#define BUTTON_PADDING LV_MAX((TITLE_BAR_HEIGHT / 8), BORDER_SIZE)
|
|
#define BUTTON_SIZE (TITLE_BAR_HEIGHT - (2 * BUTTON_MARGIN))
|
|
#endif
|
|
|
|
#ifndef LV_WAYLAND_CYCLE_PERIOD
|
|
#define LV_WAYLAND_CYCLE_PERIOD LV_MIN(LV_DEF_REFR_PERIOD,1)
|
|
#endif
|
|
|
|
/**********************
|
|
* TYPEDEFS
|
|
**********************/
|
|
|
|
enum object_type {
|
|
OBJECT_TITLEBAR = 0,
|
|
OBJECT_BUTTON_CLOSE,
|
|
#if LV_WAYLAND_XDG_SHELL
|
|
OBJECT_BUTTON_MAXIMIZE,
|
|
OBJECT_BUTTON_MINIMIZE,
|
|
#endif
|
|
OBJECT_BORDER_TOP,
|
|
OBJECT_BORDER_BOTTOM,
|
|
OBJECT_BORDER_LEFT,
|
|
OBJECT_BORDER_RIGHT,
|
|
OBJECT_WINDOW,
|
|
};
|
|
|
|
#define FIRST_DECORATION (OBJECT_TITLEBAR)
|
|
#define LAST_DECORATION (OBJECT_BORDER_RIGHT)
|
|
#define NUM_DECORATIONS (LAST_DECORATION-FIRST_DECORATION+1)
|
|
|
|
struct window;
|
|
struct input
|
|
{
|
|
struct
|
|
{
|
|
lv_coord_t x;
|
|
lv_coord_t y;
|
|
lv_indev_state_t left_button;
|
|
lv_indev_state_t right_button;
|
|
lv_indev_state_t wheel_button;
|
|
int16_t wheel_diff;
|
|
} pointer;
|
|
|
|
struct
|
|
{
|
|
lv_key_t key;
|
|
lv_indev_state_t state;
|
|
} keyboard;
|
|
|
|
struct
|
|
{
|
|
lv_coord_t x;
|
|
lv_coord_t y;
|
|
lv_indev_state_t state;
|
|
} touch;
|
|
};
|
|
|
|
struct seat
|
|
{
|
|
struct wl_touch *wl_touch;
|
|
struct wl_pointer *wl_pointer;
|
|
struct wl_keyboard *wl_keyboard;
|
|
|
|
struct
|
|
{
|
|
struct xkb_keymap *keymap;
|
|
struct xkb_state *state;
|
|
} xkb;
|
|
};
|
|
|
|
struct graphic_object
|
|
{
|
|
struct window *window;
|
|
|
|
struct wl_surface *surface;
|
|
bool surface_configured;
|
|
smm_buffer_t *pending_buffer;
|
|
smm_group_t *buffer_group;
|
|
struct wl_subsurface *subsurface;
|
|
|
|
enum object_type type;
|
|
int width;
|
|
int height;
|
|
|
|
struct input input;
|
|
};
|
|
|
|
struct application
|
|
{
|
|
struct wl_display *display;
|
|
struct wl_registry *registry;
|
|
struct wl_compositor *compositor;
|
|
struct wl_subcompositor *subcompositor;
|
|
struct wl_shm *shm;
|
|
struct wl_seat *wl_seat;
|
|
|
|
struct wl_cursor_theme *cursor_theme;
|
|
struct wl_surface *cursor_surface;
|
|
|
|
#if LV_WAYLAND_WL_SHELL
|
|
struct wl_shell *wl_shell;
|
|
#endif
|
|
|
|
#if LV_WAYLAND_XDG_SHELL
|
|
struct xdg_wm_base *xdg_wm;
|
|
#endif
|
|
|
|
const char *xdg_runtime_dir;
|
|
|
|
#ifdef LV_WAYLAND_CLIENT_SIDE_DECORATIONS
|
|
bool opt_disable_decorations;
|
|
#endif
|
|
|
|
uint32_t format;
|
|
|
|
struct xkb_context *xkb_context;
|
|
|
|
struct seat seat;
|
|
|
|
struct graphic_object *touch_obj;
|
|
struct graphic_object *pointer_obj;
|
|
struct graphic_object *keyboard_obj;
|
|
|
|
lv_ll_t window_ll;
|
|
lv_timer_t * cycle_timer;
|
|
|
|
bool cursor_flush_pending;
|
|
};
|
|
|
|
struct window
|
|
{
|
|
lv_disp_drv_t lv_disp_drv;
|
|
lv_disp_draw_buf_t lv_disp_draw_buf;
|
|
lv_disp_t *lv_disp;
|
|
|
|
lv_indev_drv_t lv_indev_drv_pointer;
|
|
lv_indev_t * lv_indev_pointer;
|
|
|
|
lv_indev_drv_t lv_indev_drv_pointeraxis;
|
|
lv_indev_t * lv_indev_pointeraxis;
|
|
|
|
lv_indev_drv_t lv_indev_drv_touch;
|
|
lv_indev_t * lv_indev_touch;
|
|
|
|
lv_indev_drv_t lv_indev_drv_keyboard;
|
|
lv_indev_t * lv_indev_keyboard;
|
|
|
|
lv_wayland_display_close_f_t close_cb;
|
|
|
|
struct application *application;
|
|
|
|
#if LV_WAYLAND_WL_SHELL
|
|
struct wl_shell_surface *wl_shell_surface;
|
|
#endif
|
|
|
|
#if LV_WAYLAND_XDG_SHELL
|
|
struct xdg_surface *xdg_surface;
|
|
struct xdg_toplevel *xdg_toplevel;
|
|
uint32_t wm_capabilities;
|
|
#endif
|
|
|
|
struct graphic_object * body;
|
|
struct {
|
|
lv_area_t cache[DMG_CACHE_CAPACITY];
|
|
unsigned char start;
|
|
unsigned char end;
|
|
unsigned size;
|
|
} dmg_cache;
|
|
|
|
#if LV_WAYLAND_CLIENT_SIDE_DECORATIONS
|
|
struct graphic_object * decoration[NUM_DECORATIONS];
|
|
#endif
|
|
|
|
int width;
|
|
int height;
|
|
|
|
bool resize_pending;
|
|
int resize_width;
|
|
int resize_height;
|
|
|
|
bool flush_pending;
|
|
bool shall_close;
|
|
bool closed;
|
|
bool maximized;
|
|
bool fullscreen;
|
|
};
|
|
|
|
/*********************************
|
|
* STATIC VARIABLES and FUNTIONS
|
|
*********************************/
|
|
|
|
static struct application application;
|
|
|
|
static inline bool _is_digit(char ch)
|
|
{
|
|
return (ch >= '0') && (ch <= '9');
|
|
}
|
|
|
|
static unsigned int _atoi(const char ** str)
|
|
{
|
|
unsigned int i = 0U;
|
|
while (_is_digit(**str))
|
|
{
|
|
i = i * 10U + (unsigned int)(*((*str)++) - '0');
|
|
}
|
|
return i;
|
|
}
|
|
|
|
static void shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
|
|
{
|
|
struct application *app = data;
|
|
|
|
switch (format)
|
|
{
|
|
#if (LV_COLOR_DEPTH == 32)
|
|
case WL_SHM_FORMAT_ARGB8888:
|
|
app->format = format;
|
|
break;
|
|
case WL_SHM_FORMAT_XRGB8888:
|
|
if (app->format != WL_SHM_FORMAT_ARGB8888)
|
|
{
|
|
app->format = format;
|
|
}
|
|
break;
|
|
#elif (LV_COLOR_DEPTH == 16)
|
|
case WL_SHM_FORMAT_RGB565:
|
|
app->format = format;
|
|
break;
|
|
#elif (LV_COLOR_DEPTH == 8)
|
|
case WL_SHM_FORMAT_RGB332:
|
|
app->format = format;
|
|
break;
|
|
#elif (LV_COLOR_DEPTH == 1)
|
|
case WL_SHM_FORMAT_RGB332:
|
|
app->format = format;
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static const struct wl_shm_listener shm_listener = {
|
|
shm_format
|
|
};
|
|
|
|
static void pointer_handle_enter(void *data, struct wl_pointer *pointer,
|
|
uint32_t serial, struct wl_surface *surface,
|
|
wl_fixed_t sx, wl_fixed_t sy)
|
|
{
|
|
struct application *app = data;
|
|
const char * cursor = "left_ptr";
|
|
int pos_x = wl_fixed_to_int(sx);
|
|
int pos_y = wl_fixed_to_int(sy);
|
|
|
|
if (!surface)
|
|
{
|
|
app->pointer_obj = NULL;
|
|
return;
|
|
}
|
|
|
|
app->pointer_obj = wl_surface_get_user_data(surface);
|
|
|
|
app->pointer_obj->input.pointer.x = pos_x;
|
|
app->pointer_obj->input.pointer.y = pos_y;
|
|
|
|
#if (LV_WAYLAND_CLIENT_SIDE_DECORATIONS && LV_WAYLAND_XDG_SHELL)
|
|
if (!app->pointer_obj->window->xdg_toplevel || app->opt_disable_decorations)
|
|
{
|
|
return;
|
|
}
|
|
|
|
struct window *window = app->pointer_obj->window;
|
|
|
|
switch (app->pointer_obj->type)
|
|
{
|
|
case OBJECT_BORDER_TOP:
|
|
if (window->maximized)
|
|
{
|
|
// do nothing
|
|
}
|
|
else if (pos_x < (BORDER_SIZE * 5))
|
|
{
|
|
cursor = "top_left_corner";
|
|
}
|
|
else if (pos_x >= (window->width + BORDER_SIZE - (BORDER_SIZE * 5)))
|
|
{
|
|
cursor = "top_right_corner";
|
|
}
|
|
else
|
|
{
|
|
cursor = "top_side";
|
|
}
|
|
break;
|
|
case OBJECT_BORDER_BOTTOM:
|
|
if (window->maximized)
|
|
{
|
|
// do nothing
|
|
}
|
|
else if (pos_x < (BORDER_SIZE * 5))
|
|
{
|
|
cursor = "bottom_left_corner";
|
|
}
|
|
else if (pos_x >= (window->width + BORDER_SIZE - (BORDER_SIZE * 5)))
|
|
{
|
|
cursor = "bottom_right_corner";
|
|
}
|
|
else
|
|
{
|
|
cursor = "bottom_side";
|
|
}
|
|
break;
|
|
case OBJECT_BORDER_LEFT:
|
|
if (window->maximized)
|
|
{
|
|
// do nothing
|
|
}
|
|
else if (pos_y < (BORDER_SIZE * 5))
|
|
{
|
|
cursor = "top_left_corner";
|
|
}
|
|
else if (pos_y >= (window->height + BORDER_SIZE - (BORDER_SIZE * 5)))
|
|
{
|
|
cursor = "bottom_left_corner";
|
|
}
|
|
else
|
|
{
|
|
cursor = "left_side";
|
|
}
|
|
break;
|
|
case OBJECT_BORDER_RIGHT:
|
|
if (window->maximized)
|
|
{
|
|
// do nothing
|
|
}
|
|
else if (pos_y < (BORDER_SIZE * 5))
|
|
{
|
|
cursor = "top_right_corner";
|
|
}
|
|
else if (pos_y >= (window->height + BORDER_SIZE - (BORDER_SIZE * 5)))
|
|
{
|
|
cursor = "bottom_right_corner";
|
|
}
|
|
else
|
|
{
|
|
cursor = "right_side";
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
if (app->cursor_surface)
|
|
{
|
|
struct wl_cursor_image *cursor_image = wl_cursor_theme_get_cursor(app->cursor_theme, cursor)->images[0];
|
|
wl_pointer_set_cursor(pointer, serial, app->cursor_surface, cursor_image->hotspot_x, cursor_image->hotspot_y);
|
|
wl_surface_attach(app->cursor_surface, wl_cursor_image_get_buffer(cursor_image), 0, 0);
|
|
wl_surface_damage(app->cursor_surface, 0, 0, cursor_image->width, cursor_image->height);
|
|
wl_surface_commit(app->cursor_surface);
|
|
app->cursor_flush_pending = true;
|
|
}
|
|
}
|
|
|
|
static void pointer_handle_leave(void *data, struct wl_pointer *pointer,
|
|
uint32_t serial, struct wl_surface *surface)
|
|
{
|
|
struct application *app = data;
|
|
|
|
if (!surface || (app->pointer_obj == wl_surface_get_user_data(surface)))
|
|
{
|
|
app->pointer_obj = NULL;
|
|
}
|
|
}
|
|
|
|
static void pointer_handle_motion(void *data, struct wl_pointer *pointer,
|
|
uint32_t time, wl_fixed_t sx, wl_fixed_t sy)
|
|
{
|
|
struct application *app = data;
|
|
int max_x, max_y;
|
|
|
|
if (!app->pointer_obj)
|
|
{
|
|
return;
|
|
}
|
|
|
|
app->pointer_obj->input.pointer.x = LV_MAX(0, LV_MIN(wl_fixed_to_int(sx), app->pointer_obj->width - 1));
|
|
app->pointer_obj->input.pointer.y = LV_MAX(0, LV_MIN(wl_fixed_to_int(sy), app->pointer_obj->height - 1));
|
|
}
|
|
|
|
static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
|
|
uint32_t serial, uint32_t time, uint32_t button,
|
|
uint32_t state)
|
|
{
|
|
struct application *app = data;
|
|
const lv_indev_state_t lv_state =
|
|
(state == WL_POINTER_BUTTON_STATE_PRESSED) ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL;
|
|
|
|
if (!app->pointer_obj)
|
|
{
|
|
return;
|
|
}
|
|
|
|
#if LV_WAYLAND_CLIENT_SIDE_DECORATIONS
|
|
struct window *window = app->pointer_obj->window;
|
|
int pos_x = app->pointer_obj->input.pointer.x;
|
|
int pos_y = app->pointer_obj->input.pointer.y;
|
|
#endif
|
|
|
|
switch (app->pointer_obj->type)
|
|
{
|
|
case OBJECT_WINDOW:
|
|
switch (button)
|
|
{
|
|
case BTN_LEFT:
|
|
app->pointer_obj->input.pointer.left_button = lv_state;
|
|
break;
|
|
case BTN_RIGHT:
|
|
app->pointer_obj->input.pointer.right_button = lv_state;
|
|
break;
|
|
case BTN_MIDDLE:
|
|
app->pointer_obj->input.pointer.wheel_button = lv_state;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
#if LV_WAYLAND_CLIENT_SIDE_DECORATIONS
|
|
case OBJECT_TITLEBAR:
|
|
if ((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_PRESSED))
|
|
{
|
|
#if LV_WAYLAND_XDG_SHELL
|
|
if (window->xdg_toplevel)
|
|
{
|
|
xdg_toplevel_move(window->xdg_toplevel, app->wl_seat, serial);
|
|
window->flush_pending = true;
|
|
}
|
|
#endif
|
|
#if LV_WAYLAND_WL_SHELL
|
|
if (window->wl_shell_surface)
|
|
{
|
|
wl_shell_surface_move(window->wl_shell_surface, app->wl_seat, serial);
|
|
window->flush_pending = true;
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
case OBJECT_BUTTON_CLOSE:
|
|
if ((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_RELEASED))
|
|
{
|
|
window->shall_close = true;
|
|
}
|
|
break;
|
|
#if LV_WAYLAND_XDG_SHELL
|
|
case OBJECT_BUTTON_MAXIMIZE:
|
|
if ((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_RELEASED))
|
|
{
|
|
if (window->xdg_toplevel)
|
|
{
|
|
if (window->maximized)
|
|
{
|
|
xdg_toplevel_unset_maximized(window->xdg_toplevel);
|
|
}
|
|
else
|
|
{
|
|
xdg_toplevel_set_maximized(window->xdg_toplevel);
|
|
}
|
|
window->maximized ^= true;
|
|
window->flush_pending = true;
|
|
}
|
|
}
|
|
break;
|
|
case OBJECT_BUTTON_MINIMIZE:
|
|
if ((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_RELEASED))
|
|
{
|
|
if (window->xdg_toplevel)
|
|
{
|
|
xdg_toplevel_set_minimized(window->xdg_toplevel);
|
|
window->flush_pending = true;
|
|
}
|
|
}
|
|
break;
|
|
case OBJECT_BORDER_TOP:
|
|
if ((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_PRESSED))
|
|
{
|
|
if (window->xdg_toplevel && !window->maximized)
|
|
{
|
|
uint32_t edge;
|
|
if (pos_x < (BORDER_SIZE * 5))
|
|
{
|
|
edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT;
|
|
}
|
|
else if (pos_x >= (window->width + BORDER_SIZE - (BORDER_SIZE * 5)))
|
|
{
|
|
edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT;
|
|
}
|
|
else
|
|
{
|
|
edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP;
|
|
}
|
|
xdg_toplevel_resize(window->xdg_toplevel,
|
|
window->application->wl_seat, serial, edge);
|
|
window->flush_pending = true;
|
|
}
|
|
}
|
|
break;
|
|
case OBJECT_BORDER_BOTTOM:
|
|
if ((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_PRESSED))
|
|
{
|
|
if (window->xdg_toplevel && !window->maximized)
|
|
{
|
|
uint32_t edge;
|
|
if (pos_x < (BORDER_SIZE * 5))
|
|
{
|
|
edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT;
|
|
}
|
|
else if (pos_x >= (window->width + BORDER_SIZE - (BORDER_SIZE * 5)))
|
|
{
|
|
edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT;
|
|
}
|
|
else
|
|
{
|
|
edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM;
|
|
}
|
|
xdg_toplevel_resize(window->xdg_toplevel,
|
|
window->application->wl_seat, serial, edge);
|
|
window->flush_pending = true;
|
|
}
|
|
}
|
|
break;
|
|
case OBJECT_BORDER_LEFT:
|
|
if ((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_PRESSED))
|
|
{
|
|
if (window->xdg_toplevel && !window->maximized)
|
|
{
|
|
uint32_t edge;
|
|
if (pos_y < (BORDER_SIZE * 5))
|
|
{
|
|
edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT;
|
|
}
|
|
else if (pos_y >= (window->height + BORDER_SIZE - (BORDER_SIZE * 5)))
|
|
{
|
|
edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT;
|
|
}
|
|
else
|
|
{
|
|
edge = XDG_TOPLEVEL_RESIZE_EDGE_LEFT;
|
|
}
|
|
xdg_toplevel_resize(window->xdg_toplevel,
|
|
window->application->wl_seat, serial, edge);
|
|
window->flush_pending = true;
|
|
}
|
|
}
|
|
break;
|
|
case OBJECT_BORDER_RIGHT:
|
|
if ((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_PRESSED))
|
|
{
|
|
if (window->xdg_toplevel && !window->maximized)
|
|
{
|
|
uint32_t edge;
|
|
if (pos_y < (BORDER_SIZE * 5))
|
|
{
|
|
edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT;
|
|
}
|
|
else if (pos_y >= (window->height + BORDER_SIZE - (BORDER_SIZE * 5)))
|
|
{
|
|
edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT;
|
|
}
|
|
else
|
|
{
|
|
edge = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT;
|
|
}
|
|
xdg_toplevel_resize(window->xdg_toplevel,
|
|
window->application->wl_seat, serial, edge);
|
|
window->flush_pending = true;
|
|
}
|
|
}
|
|
break;
|
|
#endif // LV_WAYLAND_XDG_SHELL
|
|
#endif // LV_WAYLAND_CLIENT_SIDE_DECORATIONS
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
|
|
uint32_t time, uint32_t axis, wl_fixed_t value)
|
|
{
|
|
struct application *app = data;
|
|
const int diff = wl_fixed_to_int(value);
|
|
|
|
if (!app->pointer_obj)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (axis == 0)
|
|
{
|
|
if (diff > 0)
|
|
{
|
|
app->pointer_obj->input.pointer.wheel_diff++;
|
|
}
|
|
else if (diff < 0)
|
|
{
|
|
app->pointer_obj->input.pointer.wheel_diff--;
|
|
}
|
|
}
|
|
}
|
|
|
|
static const struct wl_pointer_listener pointer_listener = {
|
|
.enter = pointer_handle_enter,
|
|
.leave = pointer_handle_leave,
|
|
.motion = pointer_handle_motion,
|
|
.button = pointer_handle_button,
|
|
.axis = pointer_handle_axis,
|
|
};
|
|
|
|
static lv_key_t keycode_xkb_to_lv(xkb_keysym_t xkb_key)
|
|
{
|
|
lv_key_t key = 0;
|
|
|
|
if (((xkb_key >= XKB_KEY_space) && (xkb_key <= XKB_KEY_asciitilde)))
|
|
{
|
|
key = xkb_key;
|
|
}
|
|
else if (((xkb_key >= XKB_KEY_KP_0) && (xkb_key <= XKB_KEY_KP_9)))
|
|
{
|
|
key = (xkb_key & 0x003f);
|
|
}
|
|
else
|
|
{
|
|
switch (xkb_key)
|
|
{
|
|
case XKB_KEY_BackSpace:
|
|
key = LV_KEY_BACKSPACE;
|
|
break;
|
|
case XKB_KEY_Return:
|
|
case XKB_KEY_KP_Enter:
|
|
key = LV_KEY_ENTER;
|
|
break;
|
|
case XKB_KEY_Escape:
|
|
key = LV_KEY_ESC;
|
|
break;
|
|
case XKB_KEY_Delete:
|
|
case XKB_KEY_KP_Delete:
|
|
key = LV_KEY_DEL;
|
|
break;
|
|
case XKB_KEY_Home:
|
|
case XKB_KEY_KP_Home:
|
|
key = LV_KEY_HOME;
|
|
break;
|
|
case XKB_KEY_Left:
|
|
case XKB_KEY_KP_Left:
|
|
key = LV_KEY_LEFT;
|
|
break;
|
|
case XKB_KEY_Up:
|
|
case XKB_KEY_KP_Up:
|
|
key = LV_KEY_UP;
|
|
break;
|
|
case XKB_KEY_Right:
|
|
case XKB_KEY_KP_Right:
|
|
key = LV_KEY_RIGHT;
|
|
break;
|
|
case XKB_KEY_Down:
|
|
case XKB_KEY_KP_Down:
|
|
key = LV_KEY_DOWN;
|
|
break;
|
|
case XKB_KEY_Prior:
|
|
case XKB_KEY_KP_Prior:
|
|
key = LV_KEY_PREV;
|
|
break;
|
|
case XKB_KEY_Next:
|
|
case XKB_KEY_KP_Next:
|
|
case XKB_KEY_Tab:
|
|
case XKB_KEY_KP_Tab:
|
|
key = LV_KEY_NEXT;
|
|
break;
|
|
case XKB_KEY_End:
|
|
case XKB_KEY_KP_End:
|
|
key = LV_KEY_END;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return key;
|
|
}
|
|
|
|
static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
|
|
uint32_t format, int fd, uint32_t size)
|
|
{
|
|
struct application *app = data;
|
|
|
|
struct xkb_keymap *keymap;
|
|
struct xkb_state *state;
|
|
char *map_str;
|
|
|
|
if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1)
|
|
{
|
|
close(fd);
|
|
return;
|
|
}
|
|
|
|
map_str = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
|
|
if (map_str == MAP_FAILED)
|
|
{
|
|
close(fd);
|
|
return;
|
|
}
|
|
|
|
/* Set up XKB keymap */
|
|
keymap = xkb_keymap_new_from_string(app->xkb_context, map_str,
|
|
XKB_KEYMAP_FORMAT_TEXT_V1, 0);
|
|
munmap(map_str, size);
|
|
close(fd);
|
|
|
|
if (!keymap)
|
|
{
|
|
LV_LOG_ERROR("failed to compile keymap");
|
|
return;
|
|
}
|
|
|
|
/* Set up XKB state */
|
|
state = xkb_state_new(keymap);
|
|
if (!state)
|
|
{
|
|
LV_LOG_ERROR("failed to create XKB state");
|
|
xkb_keymap_unref(keymap);
|
|
return;
|
|
}
|
|
|
|
xkb_keymap_unref(app->seat.xkb.keymap);
|
|
xkb_state_unref(app->seat.xkb.state);
|
|
app->seat.xkb.keymap = keymap;
|
|
app->seat.xkb.state = state;
|
|
}
|
|
|
|
static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
|
|
uint32_t serial, struct wl_surface *surface,
|
|
struct wl_array *keys)
|
|
{
|
|
struct application *app = data;
|
|
|
|
if (!surface)
|
|
{
|
|
app->keyboard_obj = NULL;
|
|
}
|
|
else
|
|
{
|
|
app->keyboard_obj = wl_surface_get_user_data(surface);
|
|
}
|
|
}
|
|
|
|
static void keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
|
|
uint32_t serial, struct wl_surface *surface)
|
|
{
|
|
struct application *app = data;
|
|
|
|
if (!surface || (app->keyboard_obj == wl_surface_get_user_data(surface)))
|
|
{
|
|
app->keyboard_obj = NULL;
|
|
}
|
|
}
|
|
|
|
static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
|
|
uint32_t serial, uint32_t time, uint32_t key,
|
|
uint32_t state)
|
|
{
|
|
struct application *app = data;
|
|
const uint32_t code = (key + 8);
|
|
const xkb_keysym_t *syms;
|
|
xkb_keysym_t sym = XKB_KEY_NoSymbol;
|
|
|
|
if (!app->keyboard_obj || !app->seat.xkb.state)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (xkb_state_key_get_syms(app->seat.xkb.state, code, &syms) == 1)
|
|
{
|
|
sym = syms[0];
|
|
}
|
|
|
|
const lv_key_t lv_key = keycode_xkb_to_lv(sym);
|
|
const lv_indev_state_t lv_state =
|
|
(state == WL_KEYBOARD_KEY_STATE_PRESSED) ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL;
|
|
|
|
if (lv_key != 0)
|
|
{
|
|
app->keyboard_obj->input.keyboard.key = lv_key;
|
|
app->keyboard_obj->input.keyboard.state = lv_state;
|
|
}
|
|
}
|
|
|
|
static void keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
|
|
uint32_t serial, uint32_t mods_depressed,
|
|
uint32_t mods_latched, uint32_t mods_locked,
|
|
uint32_t group)
|
|
{
|
|
struct application *app = data;
|
|
|
|
/* If we're not using a keymap, then we don't handle PC-style modifiers */
|
|
if (!app->seat.xkb.keymap)
|
|
{
|
|
return;
|
|
}
|
|
|
|
xkb_state_update_mask(app->seat.xkb.state,
|
|
mods_depressed, mods_latched, mods_locked, 0, 0, group);
|
|
}
|
|
|
|
static const struct wl_keyboard_listener keyboard_listener = {
|
|
.keymap = keyboard_handle_keymap,
|
|
.enter = keyboard_handle_enter,
|
|
.leave = keyboard_handle_leave,
|
|
.key = keyboard_handle_key,
|
|
.modifiers = keyboard_handle_modifiers,
|
|
};
|
|
|
|
static void touch_handle_down(void *data, struct wl_touch *wl_touch,
|
|
uint32_t serial, uint32_t time, struct wl_surface *surface,
|
|
int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
|
|
{
|
|
struct application *app = data;
|
|
|
|
if (!surface)
|
|
{
|
|
app->touch_obj = NULL;
|
|
return;
|
|
}
|
|
|
|
app->touch_obj = wl_surface_get_user_data(surface);
|
|
|
|
app->touch_obj->input.touch.x = wl_fixed_to_int(x_w);
|
|
app->touch_obj->input.touch.y = wl_fixed_to_int(y_w);
|
|
app->touch_obj->input.touch.state = LV_INDEV_STATE_PR;
|
|
|
|
#if LV_WAYLAND_CLIENT_SIDE_DECORATIONS
|
|
struct window *window = app->touch_obj->window;
|
|
switch (app->touch_obj->type)
|
|
{
|
|
case OBJECT_TITLEBAR:
|
|
#if LV_WAYLAND_XDG_SHELL
|
|
if (window->xdg_toplevel)
|
|
{
|
|
xdg_toplevel_move(window->xdg_toplevel, app->wl_seat, serial);
|
|
window->flush_pending = true;
|
|
}
|
|
#endif
|
|
#if LV_WAYLAND_WL_SHELL
|
|
if (window->wl_shell_surface)
|
|
{
|
|
wl_shell_surface_move(window->wl_shell_surface, app->wl_seat, serial);
|
|
window->flush_pending = true;
|
|
}
|
|
#endif
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void touch_handle_up(void *data, struct wl_touch *wl_touch,
|
|
uint32_t serial, uint32_t time, int32_t id)
|
|
{
|
|
struct application *app = data;
|
|
|
|
if (!app->touch_obj)
|
|
{
|
|
return;
|
|
}
|
|
|
|
app->touch_obj->input.touch.state = LV_INDEV_STATE_REL;
|
|
|
|
#if LV_WAYLAND_CLIENT_SIDE_DECORATIONS
|
|
struct window *window = app->touch_obj->window;
|
|
switch (app->touch_obj->type)
|
|
{
|
|
case OBJECT_BUTTON_CLOSE:
|
|
window->shall_close = true;
|
|
break;
|
|
#if LV_WAYLAND_XDG_SHELL
|
|
case OBJECT_BUTTON_MAXIMIZE:
|
|
if (window->xdg_toplevel)
|
|
{
|
|
if (window->maximized)
|
|
{
|
|
xdg_toplevel_unset_maximized(window->xdg_toplevel);
|
|
}
|
|
else
|
|
{
|
|
xdg_toplevel_set_maximized(window->xdg_toplevel);
|
|
}
|
|
window->maximized ^= true;
|
|
}
|
|
break;
|
|
case OBJECT_BUTTON_MINIMIZE:
|
|
if (window->xdg_toplevel)
|
|
{
|
|
xdg_toplevel_set_minimized(window->xdg_toplevel);
|
|
window->flush_pending = true;
|
|
}
|
|
#endif // LV_WAYLAND_XDG_SHELL
|
|
default:
|
|
break;
|
|
}
|
|
#endif // LV_WAYLAND_CLIENT_SIDE_DECORATIONS
|
|
|
|
app->touch_obj = NULL;
|
|
}
|
|
|
|
static void touch_handle_motion(void *data, struct wl_touch *wl_touch,
|
|
uint32_t time, int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
|
|
{
|
|
struct application *app = data;
|
|
|
|
if (!app->touch_obj)
|
|
{
|
|
return;
|
|
}
|
|
|
|
app->touch_obj->input.touch.x = wl_fixed_to_int(x_w);
|
|
app->touch_obj->input.touch.y = wl_fixed_to_int(y_w);
|
|
}
|
|
|
|
static void touch_handle_frame(void *data, struct wl_touch *wl_touch)
|
|
{
|
|
}
|
|
|
|
static void touch_handle_cancel(void *data, struct wl_touch *wl_touch)
|
|
{
|
|
}
|
|
|
|
static const struct wl_touch_listener touch_listener = {
|
|
.down = touch_handle_down,
|
|
.up = touch_handle_up,
|
|
.motion = touch_handle_motion,
|
|
.frame = touch_handle_frame,
|
|
.cancel = touch_handle_cancel,
|
|
};
|
|
|
|
static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum wl_seat_capability caps)
|
|
{
|
|
struct application *app = data;
|
|
struct seat *seat = &app->seat;
|
|
|
|
if ((caps & WL_SEAT_CAPABILITY_POINTER) && !seat->wl_pointer)
|
|
{
|
|
seat->wl_pointer = wl_seat_get_pointer(wl_seat);
|
|
wl_pointer_add_listener(seat->wl_pointer, &pointer_listener, app);
|
|
app->cursor_surface = wl_compositor_create_surface(app->compositor);
|
|
if (!app->cursor_surface)
|
|
{
|
|
LV_LOG_WARN("failed to create cursor surface");
|
|
}
|
|
}
|
|
else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && seat->wl_pointer)
|
|
{
|
|
wl_pointer_destroy(seat->wl_pointer);
|
|
if (app->cursor_surface)
|
|
{
|
|
wl_surface_destroy(app->cursor_surface);
|
|
}
|
|
seat->wl_pointer = NULL;
|
|
}
|
|
|
|
if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !seat->wl_keyboard)
|
|
{
|
|
seat->wl_keyboard = wl_seat_get_keyboard(wl_seat);
|
|
wl_keyboard_add_listener(seat->wl_keyboard, &keyboard_listener, app);
|
|
}
|
|
else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && seat->wl_keyboard)
|
|
{
|
|
wl_keyboard_destroy(seat->wl_keyboard);
|
|
seat->wl_keyboard = NULL;
|
|
}
|
|
|
|
if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !seat->wl_touch)
|
|
{
|
|
seat->wl_touch = wl_seat_get_touch(wl_seat);
|
|
wl_touch_add_listener(seat->wl_touch, &touch_listener, app);
|
|
}
|
|
else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && seat->wl_touch)
|
|
{
|
|
wl_touch_destroy(seat->wl_touch);
|
|
seat->wl_touch = NULL;
|
|
}
|
|
}
|
|
|
|
static const struct wl_seat_listener seat_listener = {
|
|
.capabilities = seat_handle_capabilities,
|
|
};
|
|
|
|
#if LV_WAYLAND_WL_SHELL
|
|
static void wl_shell_handle_ping(void *data, struct wl_shell_surface *shell_surface, uint32_t serial)
|
|
{
|
|
return wl_shell_surface_pong(shell_surface, serial);
|
|
}
|
|
|
|
static void wl_shell_handle_configure(void *data, struct wl_shell_surface *shell_surface,
|
|
uint32_t edges, int32_t width, int32_t height)
|
|
{
|
|
struct window *window = (struct window *)data;
|
|
|
|
if ((width <= 0) || (height <= 0))
|
|
{
|
|
return;
|
|
}
|
|
else if ((width != window->width) || (height != window->height))
|
|
{
|
|
window->resize_width = width;
|
|
window->resize_height = height;
|
|
window->resize_pending = true;
|
|
}
|
|
}
|
|
|
|
static const struct wl_shell_surface_listener shell_surface_listener = {
|
|
.ping = wl_shell_handle_ping,
|
|
.configure = wl_shell_handle_configure,
|
|
};
|
|
#endif
|
|
|
|
#if LV_WAYLAND_XDG_SHELL
|
|
static void xdg_surface_handle_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial)
|
|
{
|
|
struct window *window = (struct window *)data;
|
|
struct wl_buffer *wl_buf;
|
|
|
|
xdg_surface_ack_configure(xdg_surface, serial);
|
|
|
|
if ((!window->body->surface_configured) &&
|
|
(window->body->pending_buffer != NULL)) {
|
|
// LVGL flush occured before surface was configured, so attach pending buffer here
|
|
wl_buf = SMM_BUFFER_PROPERTIES(window->body->pending_buffer)->tag[TAG_LOCAL];
|
|
window->body->pending_buffer = NULL;
|
|
|
|
wl_surface_attach(window->body->surface, wl_buf, 0, 0);
|
|
wl_surface_commit(window->body->surface);
|
|
window->flush_pending = true;
|
|
}
|
|
|
|
window->body->surface_configured = true;
|
|
}
|
|
|
|
static const struct xdg_surface_listener xdg_surface_listener = {
|
|
.configure = xdg_surface_handle_configure,
|
|
};
|
|
|
|
static void xdg_toplevel_handle_configure(void *data, struct xdg_toplevel *xdg_toplevel,
|
|
int32_t width, int32_t height, struct wl_array *states)
|
|
{
|
|
struct window *window = (struct window *)data;
|
|
|
|
#if LV_WAYLAND_CLIENT_SIDE_DECORATIONS
|
|
if (!window->application->opt_disable_decorations && !window->fullscreen)
|
|
{
|
|
width -= (2 * BORDER_SIZE);
|
|
height -= (TITLE_BAR_HEIGHT + (2 * BORDER_SIZE));
|
|
}
|
|
#endif
|
|
|
|
if ((width <= 0) || (height <= 0))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ((width != window->width) || (height != window->height))
|
|
{
|
|
window->resize_width = width;
|
|
window->resize_height = height;
|
|
window->resize_pending = true;
|
|
}
|
|
}
|
|
|
|
static void xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_toplevel)
|
|
{
|
|
struct window *window = (struct window *)data;
|
|
window->shall_close = true;
|
|
}
|
|
|
|
static void xdg_toplevel_handle_configure_bounds(void *data, struct xdg_toplevel *xdg_toplevel,
|
|
int32_t width, int32_t height)
|
|
{
|
|
struct window *window = (struct window *)data;
|
|
/* Optional: Could set window width/height upper bounds, however, currently
|
|
* we'll honor the set width/height.
|
|
*/
|
|
}
|
|
|
|
static void xdg_toplevel_handle_wm_capabilities(void *data, struct xdg_toplevel *xdg_toplevel,
|
|
struct wl_array *capabilities)
|
|
{
|
|
uint32_t *cap;
|
|
struct window *window = (struct window *)data;
|
|
|
|
wl_array_for_each(cap, capabilities) {
|
|
window->wm_capabilities |= (1 << (*cap));
|
|
/* TODO: Disable appropriate graphics/capabilities as appropriate */
|
|
}
|
|
}
|
|
|
|
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
|
|
.configure = xdg_toplevel_handle_configure,
|
|
.close = xdg_toplevel_handle_close,
|
|
.configure_bounds = xdg_toplevel_handle_configure_bounds,
|
|
.wm_capabilities = xdg_toplevel_handle_wm_capabilities
|
|
};
|
|
|
|
static void xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial)
|
|
{
|
|
return xdg_wm_base_pong(xdg_wm_base, serial);
|
|
}
|
|
|
|
static const struct xdg_wm_base_listener xdg_wm_base_listener = {
|
|
.ping = xdg_wm_base_ping
|
|
};
|
|
#endif
|
|
|
|
static void handle_global(void *data, struct wl_registry *registry,
|
|
uint32_t name, const char *interface, uint32_t version)
|
|
{
|
|
struct application *app = data;
|
|
|
|
if (strcmp(interface, wl_compositor_interface.name) == 0)
|
|
{
|
|
app->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1);
|
|
}
|
|
else if (strcmp(interface, wl_subcompositor_interface.name) == 0)
|
|
{
|
|
app->subcompositor = wl_registry_bind(registry, name, &wl_subcompositor_interface, 1);
|
|
}
|
|
else if (strcmp(interface, wl_shm_interface.name) == 0)
|
|
{
|
|
app->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
|
|
wl_shm_add_listener(app->shm, &shm_listener, app);
|
|
app->cursor_theme = wl_cursor_theme_load(NULL, 32, app->shm);
|
|
}
|
|
else if (strcmp(interface, wl_seat_interface.name) == 0)
|
|
{
|
|
app->wl_seat = wl_registry_bind(app->registry, name, &wl_seat_interface, 1);
|
|
wl_seat_add_listener(app->wl_seat, &seat_listener, app);
|
|
}
|
|
#if LV_WAYLAND_WL_SHELL
|
|
else if (strcmp(interface, wl_shell_interface.name) == 0)
|
|
{
|
|
app->wl_shell = wl_registry_bind(registry, name, &wl_shell_interface, 1);
|
|
}
|
|
#endif
|
|
#if LV_WAYLAND_XDG_SHELL
|
|
else if (strcmp(interface, xdg_wm_base_interface.name) == 0)
|
|
{
|
|
app->xdg_wm = wl_registry_bind(app->registry, name, &xdg_wm_base_interface, version);
|
|
xdg_wm_base_add_listener(app->xdg_wm, &xdg_wm_base_listener, app);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
|
|
{
|
|
|
|
}
|
|
|
|
static const struct wl_registry_listener registry_listener = {
|
|
.global = handle_global,
|
|
.global_remove = handle_global_remove
|
|
};
|
|
|
|
static void handle_wl_buffer_release(void *data, struct wl_buffer *wl_buffer)
|
|
{
|
|
smm_release((smm_buffer_t *)data);
|
|
}
|
|
|
|
static const struct wl_buffer_listener wl_buffer_listener = {
|
|
.release = handle_wl_buffer_release,
|
|
};
|
|
|
|
static void cache_clear(struct window *window)
|
|
{
|
|
window->dmg_cache.start = window->dmg_cache.end;
|
|
window->dmg_cache.size = 0;
|
|
}
|
|
|
|
static void cache_purge(struct window *window, smm_buffer_t *buf)
|
|
{
|
|
lv_area_t *next_dmg;
|
|
smm_buffer_t *next_buf = smm_next(buf);
|
|
|
|
/* Remove all damage areas up until start of next buffers damage */
|
|
if (next_buf == NULL)
|
|
{
|
|
cache_clear(window);
|
|
}
|
|
else
|
|
{
|
|
next_dmg = SMM_BUFFER_PROPERTIES(next_buf)->tag[TAG_BUFFER_DAMAGE];
|
|
while ((window->dmg_cache.cache + window->dmg_cache.start) != next_dmg)
|
|
{
|
|
window->dmg_cache.start++;
|
|
window->dmg_cache.start %= DMG_CACHE_CAPACITY;
|
|
window->dmg_cache.size--;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void cache_add_area(struct window *window, smm_buffer_t *buf, const lv_area_t *area)
|
|
{
|
|
if (SMM_BUFFER_PROPERTIES(buf)->tag[TAG_BUFFER_DAMAGE] == NULL)
|
|
{
|
|
/* Buffer damage beyond cache capacity */
|
|
goto done;
|
|
}
|
|
|
|
if ((window->dmg_cache.start == window->dmg_cache.end) &&
|
|
(window->dmg_cache.size))
|
|
{
|
|
/* This buffer has more damage then the cache's capacity, so
|
|
* clear cache and leave buffer damage unrecorded
|
|
*/
|
|
cache_clear(window);
|
|
SMM_TAG(buf, TAG_BUFFER_DAMAGE, NULL);
|
|
goto done;
|
|
}
|
|
|
|
/* Add damage area to cache */
|
|
memcpy(window->dmg_cache.cache + window->dmg_cache.end,
|
|
area,
|
|
sizeof(lv_area_t));
|
|
window->dmg_cache.end++;
|
|
window->dmg_cache.end %= DMG_CACHE_CAPACITY;
|
|
window->dmg_cache.size++;
|
|
|
|
done:
|
|
return;
|
|
}
|
|
|
|
static void cache_apply_areas(struct window *window, void *dest, void *src, smm_buffer_t *src_buf)
|
|
{
|
|
unsigned long offset;
|
|
unsigned char start;
|
|
lv_coord_t y;
|
|
lv_area_t *dmg;
|
|
lv_area_t *next_dmg;
|
|
smm_buffer_t *next_buf = smm_next(src_buf);
|
|
const struct smm_buffer_properties *props = SMM_BUFFER_PROPERTIES(src_buf);
|
|
struct graphic_object *obj = SMM_GROUP_PROPERTIES(props->group)->tag[TAG_LOCAL];
|
|
|
|
if (next_buf == NULL)
|
|
{
|
|
next_dmg = (window->dmg_cache.cache + window->dmg_cache.end);
|
|
}
|
|
else
|
|
{
|
|
next_dmg = SMM_BUFFER_PROPERTIES(next_buf)->tag[TAG_BUFFER_DAMAGE];
|
|
}
|
|
|
|
/* Apply all buffer damage areas */
|
|
start = ((lv_area_t *)SMM_BUFFER_PROPERTIES(src_buf)->tag[TAG_BUFFER_DAMAGE] - window->dmg_cache.cache);
|
|
while ((window->dmg_cache.cache + start) != next_dmg)
|
|
{
|
|
/* Copy an area from source to destination (line-by-line) */
|
|
dmg = (window->dmg_cache.cache + start);
|
|
for (y = dmg->y1; y <= dmg->y2; y++)
|
|
{
|
|
offset = (dmg->x1 + (y * (obj->width * BYTES_PER_PIXEL)));
|
|
memcpy(((char *)dest) + offset,
|
|
((char *)src) + offset,
|
|
((dmg->x2 - dmg->x1 + 1) * BYTES_PER_PIXEL));
|
|
}
|
|
|
|
start++;
|
|
start %= DMG_CACHE_CAPACITY;
|
|
}
|
|
}
|
|
|
|
static bool sme_new_pool(void *ctx, smm_pool_t *pool)
|
|
{
|
|
struct wl_shm_pool *wl_pool;
|
|
struct application *app = ctx;
|
|
const struct smm_pool_properties *props = SMM_POOL_PROPERTIES(pool);
|
|
|
|
wl_pool = wl_shm_create_pool(app->shm,
|
|
props->fd,
|
|
props->size);
|
|
|
|
SMM_TAG(pool, TAG_LOCAL, wl_pool);
|
|
return (wl_pool == NULL);
|
|
}
|
|
|
|
static void sme_expand_pool(void *ctx, smm_pool_t *pool)
|
|
{
|
|
const struct smm_pool_properties *props = SMM_POOL_PROPERTIES(pool);
|
|
|
|
wl_shm_pool_resize(props->tag[TAG_LOCAL], props->size);
|
|
}
|
|
|
|
static void sme_free_pool(void *ctx, smm_pool_t *pool)
|
|
{
|
|
struct wl_shm_pool *wl_pool = SMM_POOL_PROPERTIES(pool)->tag[TAG_LOCAL];
|
|
wl_shm_pool_destroy(wl_pool);
|
|
}
|
|
|
|
static bool sme_new_buffer(void *ctx, smm_buffer_t *buf)
|
|
{
|
|
struct wl_buffer *wl_buf;
|
|
bool fail_alloc = true;
|
|
const struct smm_buffer_properties *props = SMM_BUFFER_PROPERTIES(buf);
|
|
struct wl_shm_pool *wl_pool = SMM_POOL_PROPERTIES(props->pool)->tag[TAG_LOCAL];
|
|
struct application *app = ctx;
|
|
struct graphic_object *obj = SMM_GROUP_PROPERTIES(props->group)->tag[TAG_LOCAL];
|
|
|
|
wl_buf = wl_shm_pool_create_buffer(wl_pool,
|
|
props->offset,
|
|
obj->width,
|
|
obj->height,
|
|
obj->width * BYTES_PER_PIXEL,
|
|
app->format);
|
|
|
|
if (wl_buf != NULL) {
|
|
wl_buffer_add_listener(wl_buf, &wl_buffer_listener, buf);
|
|
SMM_TAG(buf, TAG_LOCAL, wl_buf);
|
|
SMM_TAG(buf, TAG_BUFFER_DAMAGE, NULL);
|
|
fail_alloc = false;
|
|
}
|
|
|
|
return fail_alloc;
|
|
}
|
|
|
|
static bool sme_init_buffer(void *ctx, smm_buffer_t *buf)
|
|
{
|
|
smm_buffer_t *src;
|
|
void *src_base;
|
|
bool fail_init = true;
|
|
bool dmg_missing = false;
|
|
void *buf_base = smm_map(buf);
|
|
const struct smm_buffer_properties *props = SMM_BUFFER_PROPERTIES(buf);
|
|
struct graphic_object *obj = SMM_GROUP_PROPERTIES(props->group)->tag[TAG_LOCAL];
|
|
|
|
if (buf_base == NULL)
|
|
{
|
|
LV_LOG_ERROR("cannot map in buffer to initialize");
|
|
goto done;
|
|
}
|
|
|
|
/* Determine if all subsequent buffers damage is recorded */
|
|
for (src = smm_next(buf); src != NULL; src = smm_next(src))
|
|
{
|
|
if (SMM_BUFFER_PROPERTIES(src)->tag[TAG_BUFFER_DAMAGE] == NULL)
|
|
{
|
|
dmg_missing = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((smm_next(buf) == NULL) || dmg_missing)
|
|
{
|
|
/* Missing subsequent buffer damage, initialize by copying the most
|
|
* recently acquired buffers data
|
|
*/
|
|
src = smm_latest(props->group);
|
|
if ((src != NULL) &&
|
|
(src != buf))
|
|
{
|
|
/* Map and copy latest buffer data */
|
|
src_base = smm_map(src);
|
|
if (src_base == NULL)
|
|
{
|
|
LV_LOG_ERROR("cannot map most recent buffer to copy");
|
|
goto done;
|
|
}
|
|
|
|
memcpy(buf_base,
|
|
src_base,
|
|
(obj->width * BYTES_PER_PIXEL) * obj->height);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* All subsequent buffers damage is recorded, initialize by applying
|
|
* their damage to this buffer
|
|
*/
|
|
for (src = smm_next(buf); src != NULL; src = smm_next(src))
|
|
{
|
|
src_base = smm_map(src);
|
|
if (src_base == NULL)
|
|
{
|
|
LV_LOG_ERROR("cannot map source buffer to copy from");
|
|
goto done;
|
|
}
|
|
|
|
cache_apply_areas(obj->window, buf_base, src_base, src);
|
|
}
|
|
|
|
/* Purge out-of-date cached damage (up to and including next buffer) */
|
|
src = smm_next(buf);
|
|
if (src == NULL)
|
|
{
|
|
cache_purge(obj->window, src);
|
|
}
|
|
}
|
|
|
|
fail_init = false;
|
|
done:
|
|
return fail_init;
|
|
}
|
|
|
|
static void sme_free_buffer(void *ctx, smm_buffer_t *buf)
|
|
{
|
|
struct wl_buffer *wl_buf = SMM_BUFFER_PROPERTIES(buf)->tag[TAG_LOCAL];
|
|
wl_buffer_destroy(wl_buf);
|
|
}
|
|
|
|
static struct graphic_object * create_graphic_obj(struct application *app, struct window *window,
|
|
enum object_type type,
|
|
struct graphic_object *parent)
|
|
{
|
|
struct graphic_object *obj;
|
|
|
|
obj = lv_malloc(sizeof(*obj));
|
|
LV_ASSERT_MALLOC(obj);
|
|
if (!obj)
|
|
{
|
|
goto err_out;
|
|
}
|
|
|
|
lv_memset(obj, 0x00, sizeof(struct graphic_object));
|
|
|
|
obj->surface = wl_compositor_create_surface(app->compositor);
|
|
if (!obj->surface)
|
|
{
|
|
LV_LOG_ERROR("cannot create surface for graphic object");
|
|
goto err_free;
|
|
}
|
|
|
|
obj->buffer_group = smm_create();
|
|
if (obj->buffer_group == NULL)
|
|
{
|
|
LV_LOG_ERROR("cannot create buffer group for graphic object");
|
|
goto err_destroy_surface;
|
|
}
|
|
|
|
obj->window = window;
|
|
obj->type = type;
|
|
obj->surface_configured = true;
|
|
obj->pending_buffer = NULL;
|
|
wl_surface_set_user_data(obj->surface, obj);
|
|
SMM_TAG(obj->buffer_group, TAG_LOCAL, obj);
|
|
|
|
return obj;
|
|
|
|
err_destroy_surface:
|
|
wl_surface_destroy(obj->surface);
|
|
|
|
err_free:
|
|
lv_free(obj);
|
|
|
|
err_out:
|
|
return NULL;
|
|
}
|
|
|
|
static void destroy_graphic_obj(struct graphic_object * obj)
|
|
{
|
|
if (obj->subsurface)
|
|
{
|
|
wl_subsurface_destroy(obj->subsurface);
|
|
}
|
|
|
|
wl_surface_destroy(obj->surface);
|
|
smm_destroy(obj->buffer_group);
|
|
lv_free(obj);
|
|
}
|
|
|
|
#if LV_WAYLAND_CLIENT_SIDE_DECORATIONS
|
|
static bool attach_decoration(struct window *window, struct graphic_object * decoration,
|
|
smm_buffer_t *decoration_buffer, struct graphic_object * parent)
|
|
{
|
|
struct wl_buffer *wl_buf = SMM_BUFFER_PROPERTIES(decoration_buffer)->tag[TAG_LOCAL];
|
|
|
|
int pos_x, pos_y;
|
|
int x, y;
|
|
|
|
switch (decoration->type)
|
|
{
|
|
case OBJECT_TITLEBAR:
|
|
pos_x = 0;
|
|
pos_y = -TITLE_BAR_HEIGHT;
|
|
break;
|
|
case OBJECT_BUTTON_CLOSE:
|
|
pos_x = parent->width - 1 * (BUTTON_MARGIN + BUTTON_SIZE);
|
|
pos_y = -1 * (BUTTON_MARGIN + BUTTON_SIZE + (BORDER_SIZE / 2));
|
|
break;
|
|
#if LV_WAYLAND_XDG_SHELL
|
|
case OBJECT_BUTTON_MAXIMIZE:
|
|
pos_x = parent->width - 2 * (BUTTON_MARGIN + BUTTON_SIZE);
|
|
pos_y = -1 * (BUTTON_MARGIN + BUTTON_SIZE + (BORDER_SIZE / 2));
|
|
break;
|
|
case OBJECT_BUTTON_MINIMIZE:
|
|
pos_x = parent->width - 3 * (BUTTON_MARGIN + BUTTON_SIZE);
|
|
pos_y = -1 * (BUTTON_MARGIN + BUTTON_SIZE + (BORDER_SIZE / 2));
|
|
break;
|
|
#endif
|
|
case OBJECT_BORDER_TOP:
|
|
pos_x = -BORDER_SIZE;
|
|
pos_y = -(BORDER_SIZE + TITLE_BAR_HEIGHT);
|
|
break;
|
|
case OBJECT_BORDER_BOTTOM:
|
|
pos_x = -BORDER_SIZE;
|
|
pos_y = parent->height;
|
|
break;
|
|
case OBJECT_BORDER_LEFT:
|
|
pos_x = -BORDER_SIZE;
|
|
pos_y = -TITLE_BAR_HEIGHT;
|
|
break;
|
|
case OBJECT_BORDER_RIGHT:
|
|
pos_x = parent->width;
|
|
pos_y = -TITLE_BAR_HEIGHT;
|
|
break;
|
|
default:
|
|
LV_ASSERT_MSG(0, "Invalid object type");
|
|
return false;
|
|
}
|
|
|
|
decoration->subsurface = wl_subcompositor_get_subsurface(window->application->subcompositor,
|
|
decoration->surface,
|
|
parent->surface);
|
|
if (!decoration->subsurface)
|
|
{
|
|
LV_LOG_ERROR("cannot get subsurface for decoration");
|
|
goto err_destroy_surface;
|
|
}
|
|
|
|
wl_subsurface_set_desync(decoration->subsurface);
|
|
wl_subsurface_set_position(decoration->subsurface, pos_x, pos_y);
|
|
|
|
wl_surface_attach(decoration->surface, wl_buf, 0, 0);
|
|
wl_surface_commit(decoration->surface);
|
|
|
|
return true;
|
|
|
|
err_destroy_surface:
|
|
wl_surface_destroy(decoration->surface);
|
|
decoration->surface = NULL;
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool create_decoration(struct window *window,
|
|
struct graphic_object * decoration,
|
|
int window_width, int window_height)
|
|
{
|
|
smm_buffer_t *buf;
|
|
void *buf_base;
|
|
int x, y;
|
|
|
|
switch (decoration->type)
|
|
{
|
|
case OBJECT_TITLEBAR:
|
|
decoration->width = window_width;
|
|
decoration->height = TITLE_BAR_HEIGHT;
|
|
break;
|
|
case OBJECT_BUTTON_CLOSE:
|
|
decoration->width = BUTTON_SIZE;
|
|
decoration->height = BUTTON_SIZE;
|
|
break;
|
|
#if LV_WAYLAND_XDG_SHELL
|
|
case OBJECT_BUTTON_MAXIMIZE:
|
|
decoration->width = BUTTON_SIZE;
|
|
decoration->height = BUTTON_SIZE;
|
|
break;
|
|
case OBJECT_BUTTON_MINIMIZE:
|
|
decoration->width = BUTTON_SIZE;
|
|
decoration->height = BUTTON_SIZE;
|
|
break;
|
|
#endif
|
|
case OBJECT_BORDER_TOP:
|
|
decoration->width = window_width + 2 * (BORDER_SIZE);
|
|
decoration->height = BORDER_SIZE;
|
|
break;
|
|
case OBJECT_BORDER_BOTTOM:
|
|
decoration->width = window_width + 2 * (BORDER_SIZE);
|
|
decoration->height = BORDER_SIZE;
|
|
break;
|
|
case OBJECT_BORDER_LEFT:
|
|
decoration->width = BORDER_SIZE;
|
|
decoration->height = window_height + TITLE_BAR_HEIGHT;
|
|
break;
|
|
case OBJECT_BORDER_RIGHT:
|
|
decoration->width = BORDER_SIZE;
|
|
decoration->height = window_height + TITLE_BAR_HEIGHT;
|
|
break;
|
|
default:
|
|
LV_ASSERT_MSG(0, "Invalid object type");
|
|
return false;
|
|
}
|
|
|
|
smm_resize(decoration->buffer_group,
|
|
(decoration->width * BYTES_PER_PIXEL) * decoration->height);
|
|
|
|
buf = smm_acquire(decoration->buffer_group);
|
|
if (buf == NULL)
|
|
{
|
|
LV_LOG_ERROR("cannot allocate buffer for decoration");
|
|
return false;
|
|
}
|
|
|
|
buf_base = smm_map(buf);
|
|
if (buf_base == NULL)
|
|
{
|
|
LV_LOG_ERROR("cannot map in allocated decoration buffer");
|
|
smm_release(buf);
|
|
return false;
|
|
}
|
|
|
|
switch (decoration->type)
|
|
{
|
|
case OBJECT_TITLEBAR:
|
|
lv_color_fill((lv_color_t *)buf_base,
|
|
lv_color_make(0x66, 0x66, 0x66), (decoration->width * decoration->height));
|
|
break;
|
|
case OBJECT_BUTTON_CLOSE:
|
|
lv_color_fill((lv_color_t *)buf_base,
|
|
lv_color_make(0xCC, 0xCC, 0xCC), (decoration->width * decoration->height));
|
|
for (y = 0; y < decoration->height; y++)
|
|
{
|
|
for (x = 0; x < decoration->width; x++)
|
|
{
|
|
lv_color_t *pixel = ((lv_color_t *)buf_base + (y * decoration->width) + x);
|
|
if ((x >= BUTTON_PADDING) && (x < decoration->width - BUTTON_PADDING))
|
|
{
|
|
if ((x == y) || (x == decoration->width - 1 - y))
|
|
{
|
|
*pixel = lv_color_make(0x33, 0x33, 0x33);
|
|
}
|
|
else if ((x == y - 1) || (x == decoration->width - y))
|
|
{
|
|
*pixel = lv_color_make(0x66, 0x66, 0x66);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
#if LV_WAYLAND_XDG_SHELL
|
|
case OBJECT_BUTTON_MAXIMIZE:
|
|
lv_color_fill((lv_color_t *)buf_base,
|
|
lv_color_make(0xCC, 0xCC, 0xCC), (decoration->width * decoration->height));
|
|
for (y = 0; y < decoration->height; y++)
|
|
{
|
|
for (x = 0; x < decoration->width; x++)
|
|
{
|
|
lv_color_t *pixel = ((lv_color_t *)buf_base + (y * decoration->width) + x);
|
|
if (((x == BUTTON_PADDING) && (y >= BUTTON_PADDING) && (y < decoration->height - BUTTON_PADDING)) ||
|
|
((x == (decoration->width - BUTTON_PADDING)) && (y >= BUTTON_PADDING) && (y <= decoration->height - BUTTON_PADDING)) ||
|
|
((y == BUTTON_PADDING) && (x >= BUTTON_PADDING) && (x < decoration->width - BUTTON_PADDING)) ||
|
|
((y == (BUTTON_PADDING + 1)) && (x >= BUTTON_PADDING) && (x < decoration->width - BUTTON_PADDING)) ||
|
|
((y == (decoration->height - BUTTON_PADDING)) && (x >= BUTTON_PADDING) && (x < decoration->width - BUTTON_PADDING)))
|
|
{
|
|
*pixel = lv_color_make(0x33, 0x33, 0x33);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case OBJECT_BUTTON_MINIMIZE:
|
|
lv_color_fill((lv_color_t *)buf_base,
|
|
lv_color_make(0xCC, 0xCC, 0xCC), (decoration->width * decoration->height));
|
|
for (y = 0; y < decoration->height; y++)
|
|
{
|
|
for (x = 0; x < decoration->width; x++)
|
|
{
|
|
lv_color_t *pixel = ((lv_color_t *)buf_base + (y * decoration->width) + x);
|
|
if ((x >= BUTTON_PADDING) && (x < decoration->width - BUTTON_PADDING) &&
|
|
(y > decoration->height - (2 * BUTTON_PADDING)) && (y < decoration->height - BUTTON_PADDING))
|
|
{
|
|
*pixel = lv_color_make(0x33, 0x33, 0x33);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
#endif
|
|
case OBJECT_BORDER_TOP:
|
|
/* fallthrough */
|
|
case OBJECT_BORDER_BOTTOM:
|
|
/* fallthrough */
|
|
case OBJECT_BORDER_LEFT:
|
|
/* fallthrough */
|
|
case OBJECT_BORDER_RIGHT:
|
|
lv_color_fill((lv_color_t *)buf_base,
|
|
lv_color_make(0x66, 0x66, 0x66), (decoration->width * decoration->height));
|
|
break;
|
|
default:
|
|
LV_ASSERT_MSG(0, "Invalid object type");
|
|
return false;
|
|
}
|
|
|
|
return attach_decoration(window, decoration, buf, window->body);
|
|
}
|
|
|
|
static void detach_decoration(struct window *window,
|
|
struct graphic_object * decoration)
|
|
{
|
|
if (decoration->subsurface)
|
|
{
|
|
wl_subsurface_destroy(decoration->subsurface);
|
|
decoration->subsurface = NULL;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static bool resize_window(struct window *window, int width, int height)
|
|
{
|
|
lv_color_t * buf1 = NULL;
|
|
|
|
LV_LOG_TRACE("resize window %dx%d", width, height);
|
|
|
|
#if LV_WAYLAND_CLIENT_SIDE_DECORATIONS
|
|
int b;
|
|
for (b = 0; b < NUM_DECORATIONS; b++)
|
|
{
|
|
if (window->decoration[b] != NULL)
|
|
{
|
|
detach_decoration(window, window->decoration[b]);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Update size for newly allocated buffers */
|
|
smm_resize(window->body->buffer_group, (width * BYTES_PER_PIXEL) * height);
|
|
|
|
window->width = width;
|
|
window->height = height;
|
|
|
|
window->body->width = width;
|
|
window->body->height = height;
|
|
|
|
#if LV_WAYLAND_CLIENT_SIDE_DECORATIONS
|
|
if (!window->application->opt_disable_decorations && !window->fullscreen)
|
|
{
|
|
for (b = 0; b < NUM_DECORATIONS; b++)
|
|
{
|
|
if (!create_decoration(window, window->decoration[b],
|
|
window->body->width, window->body->height))
|
|
{
|
|
LV_LOG_ERROR("failed to create decoration %d", b);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (window->lv_disp != NULL)
|
|
{
|
|
/* Resize draw buffer */
|
|
buf1 = lv_malloc(((width * height) / LVGL_DRAW_BUFFER_DIV) * sizeof(lv_color_t));
|
|
if (!buf1)
|
|
{
|
|
LV_LOG_ERROR("failed to resize draw buffer");
|
|
return false;
|
|
}
|
|
|
|
lv_free(window->lv_disp_draw_buf.buf1);
|
|
lv_disp_draw_buf_init(&window->lv_disp_draw_buf, buf1, NULL, (width * height) / LVGL_DRAW_BUFFER_DIV);
|
|
|
|
/* Propagate resize to LVGL */
|
|
window->lv_disp_drv.hor_res = width;
|
|
window->lv_disp_drv.ver_res = height;
|
|
lv_disp_drv_update(window->lv_disp, &window->lv_disp_drv);
|
|
|
|
window->body->input.pointer.x = LV_MIN(window->body->input.pointer.x, (width - 1));
|
|
window->body->input.pointer.y = LV_MIN(window->body->input.pointer.y, (height - 1));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static struct window *create_window(struct application *app, int width, int height, const char *title)
|
|
{
|
|
struct window *window;
|
|
|
|
window = _lv_ll_ins_tail(&app->window_ll);
|
|
LV_ASSERT_MALLOC(window);
|
|
if (!window)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
lv_memset(window, 0x00, sizeof(struct window));
|
|
|
|
window->application = app;
|
|
|
|
// Create wayland buffer and surface
|
|
window->body = create_graphic_obj(app, window, OBJECT_WINDOW, NULL);
|
|
if (!window->body)
|
|
{
|
|
LV_LOG_ERROR("cannot create window body");
|
|
goto err_free_window;
|
|
}
|
|
|
|
// Create shell surface
|
|
if (0)
|
|
{
|
|
// Needed for #if madness below
|
|
}
|
|
#if LV_WAYLAND_XDG_SHELL
|
|
else if (app->xdg_wm)
|
|
{
|
|
window->xdg_surface = xdg_wm_base_get_xdg_surface(app->xdg_wm, window->body->surface);
|
|
if (!window->xdg_surface)
|
|
{
|
|
LV_LOG_ERROR("cannot create XDG surface");
|
|
goto err_destroy_surface;
|
|
}
|
|
|
|
xdg_surface_add_listener(window->xdg_surface, &xdg_surface_listener, window);
|
|
|
|
window->xdg_toplevel = xdg_surface_get_toplevel(window->xdg_surface);
|
|
if (!window->xdg_toplevel)
|
|
{
|
|
LV_LOG_ERROR("cannot get XDG toplevel surface");
|
|
goto err_destroy_shell_surface;
|
|
}
|
|
|
|
xdg_toplevel_add_listener(window->xdg_toplevel, &xdg_toplevel_listener, window);
|
|
xdg_toplevel_set_title(window->xdg_toplevel, title);
|
|
xdg_toplevel_set_app_id(window->xdg_toplevel, title);
|
|
|
|
// XDG surfaces need to be configured before a buffer can be attached.
|
|
// An (XDG) surface commit (without an attached buffer) triggers this
|
|
// configure event
|
|
window->body->surface_configured = false;
|
|
wl_surface_commit(window->body->surface);
|
|
}
|
|
#endif
|
|
#if LV_WAYLAND_WL_SHELL
|
|
else if (app->wl_shell)
|
|
{
|
|
window->wl_shell_surface = wl_shell_get_shell_surface(app->wl_shell, window->body->surface);
|
|
if (!window->wl_shell_surface)
|
|
{
|
|
LV_LOG_ERROR("cannot create WL shell surface");
|
|
goto err_destroy_surface;
|
|
}
|
|
|
|
wl_shell_surface_add_listener(window->wl_shell_surface, &shell_surface_listener, window);
|
|
wl_shell_surface_set_toplevel(window->wl_shell_surface);
|
|
wl_shell_surface_set_title(window->wl_shell_surface, title);
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
LV_LOG_ERROR("No shell available");
|
|
goto err_destroy_surface;
|
|
}
|
|
|
|
#if LV_WAYLAND_CLIENT_SIDE_DECORATIONS
|
|
if (!app->opt_disable_decorations)
|
|
{
|
|
int d;
|
|
for (d = 0; d < NUM_DECORATIONS; d++)
|
|
{
|
|
window->decoration[d] = create_graphic_obj(app, window, (FIRST_DECORATION+d), window->body);
|
|
if (!window->decoration[d])
|
|
{
|
|
LV_LOG_ERROR("Failed to create decoration %d", d);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (!resize_window(window, width, height))
|
|
{
|
|
LV_LOG_ERROR("Failed to resize window");
|
|
goto err_destroy_shell_surface2;
|
|
}
|
|
|
|
return window;
|
|
|
|
err_destroy_shell_surface2:
|
|
#if LV_WAYLAND_XDG_SHELL
|
|
if (window->xdg_toplevel)
|
|
{
|
|
xdg_toplevel_destroy(window->xdg_toplevel);
|
|
}
|
|
#endif
|
|
|
|
err_destroy_shell_surface:
|
|
#if LV_WAYLAND_WL_SHELL
|
|
if (window->wl_shell_surface)
|
|
{
|
|
wl_shell_surface_destroy(window->wl_shell_surface);
|
|
}
|
|
#endif
|
|
#if LV_WAYLAND_XDG_SHELL
|
|
if (window->xdg_surface)
|
|
{
|
|
xdg_surface_destroy(window->xdg_surface);
|
|
}
|
|
#endif
|
|
|
|
err_destroy_surface:
|
|
wl_surface_destroy(window->body->surface);
|
|
|
|
err_free_window:
|
|
_lv_ll_remove(&app->window_ll, window);
|
|
lv_free(window);
|
|
return NULL;
|
|
}
|
|
|
|
static void destroy_window(struct window *window)
|
|
{
|
|
if (!window)
|
|
{
|
|
return;
|
|
}
|
|
|
|
#if LV_WAYLAND_WL_SHELL
|
|
if (window->wl_shell_surface)
|
|
{
|
|
wl_shell_surface_destroy(window->wl_shell_surface);
|
|
}
|
|
#endif
|
|
#if LV_WAYLAND_XDG_SHELL
|
|
if (window->xdg_toplevel)
|
|
{
|
|
xdg_toplevel_destroy(window->xdg_toplevel);
|
|
xdg_surface_destroy(window->xdg_surface);
|
|
}
|
|
#endif
|
|
|
|
#if LV_WAYLAND_CLIENT_SIDE_DECORATIONS
|
|
int b;
|
|
for (b = 0; b < NUM_DECORATIONS; b++)
|
|
{
|
|
if (window->decoration[b])
|
|
{
|
|
destroy_graphic_obj(window->decoration[b]);
|
|
window->decoration[b] = NULL;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
destroy_graphic_obj(window->body);
|
|
}
|
|
|
|
static void _lv_wayland_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
|
|
{
|
|
unsigned long offset;
|
|
int32_t x;
|
|
int32_t y;
|
|
void *buf_base;
|
|
struct wl_buffer *wl_buf;
|
|
lv_coord_t src_width = (area->x2 - area->x1 + 1);
|
|
lv_coord_t src_height = (area->y2 - area->y1 + 1);
|
|
struct window *window = disp_drv->user_data;
|
|
smm_buffer_t *buf = window->body->pending_buffer;
|
|
|
|
const lv_coord_t hres = (disp_drv->rotated == 0) ? (disp_drv->hor_res) : (disp_drv->ver_res);
|
|
const lv_coord_t vres = (disp_drv->rotated == 0) ? (disp_drv->ver_res) : (disp_drv->hor_res);
|
|
|
|
/* If window has been / is being closed, or is not visible, skip flush */
|
|
if (window->closed || window->shall_close)
|
|
{
|
|
goto skip;
|
|
}
|
|
/* Skip if the area is out the screen */
|
|
else if ((area->x2 < 0) || (area->y2 < 0) || (area->x1 > hres - 1) || (area->y1 > vres - 1))
|
|
{
|
|
goto skip;
|
|
}
|
|
else if (window->resize_pending)
|
|
{
|
|
LV_LOG_TRACE("skip flush since resize is pending");
|
|
goto skip;
|
|
}
|
|
|
|
/* Acquire and map a buffer to attach/commit to surface */
|
|
if (buf == NULL)
|
|
{
|
|
buf = smm_acquire(window->body->buffer_group);
|
|
if (buf == NULL)
|
|
{
|
|
LV_LOG_ERROR("cannot acquire a window body buffer");
|
|
goto skip;
|
|
}
|
|
|
|
window->body->pending_buffer = buf;
|
|
SMM_TAG(buf,
|
|
TAG_BUFFER_DAMAGE,
|
|
window->dmg_cache.cache + window->dmg_cache.end);
|
|
}
|
|
|
|
buf_base = smm_map(buf);
|
|
if (buf_base == NULL)
|
|
{
|
|
LV_LOG_ERROR("cannot map in window body buffer");
|
|
goto skip;
|
|
}
|
|
|
|
/* Modify specified area in buffer */
|
|
for (y = area->y1; y <= area->y2; y++)
|
|
{
|
|
offset = ((area->x1 + (y * disp_drv->hor_res)) * BYTES_PER_PIXEL);
|
|
#if (LV_COLOR_DEPTH == 1)
|
|
for (x = 0; x < src_width; x++)
|
|
{
|
|
uint8_t * const dest = (uint8_t *)buf_base + offset + x;
|
|
*dest = ((0x07 * color_p->ch.red) << 5) |
|
|
((0x07 * color_p->ch.green) << 2) |
|
|
((0x03 * color_p->ch.blue) << 0);
|
|
color_p++;
|
|
}
|
|
#else
|
|
memcpy(((char *)buf_base) + offset,
|
|
color_p,
|
|
src_width * BYTES_PER_PIXEL);
|
|
color_p += src_width;
|
|
#endif
|
|
}
|
|
|
|
/* Mark surface damage */
|
|
wl_surface_damage(window->body->surface,
|
|
area->x1,
|
|
area->y1,
|
|
src_width,
|
|
src_height);
|
|
|
|
/* Cache buffer damage for future buffer initializations */
|
|
cache_add_area(window, buf, area);
|
|
|
|
if (lv_disp_flush_is_last(disp_drv))
|
|
{
|
|
if (window->body->surface_configured) {
|
|
/* Finally, attach buffer and commit to surface */
|
|
wl_buf = SMM_BUFFER_PROPERTIES(buf)->tag[TAG_LOCAL];
|
|
wl_surface_attach(window->body->surface, wl_buf, 0, 0);
|
|
wl_surface_commit(window->body->surface);
|
|
window->body->pending_buffer = NULL;
|
|
}
|
|
|
|
window->flush_pending = true;
|
|
}
|
|
|
|
goto done;
|
|
skip:
|
|
if (buf != NULL) {
|
|
/* Cleanup any intermediate state (in the event that this flush being
|
|
* skipped is in the middle of a flush sequence)
|
|
*/
|
|
cache_clear(window);
|
|
SMM_TAG(buf, TAG_BUFFER_DAMAGE, NULL);
|
|
smm_release(buf);
|
|
window->body->pending_buffer = NULL;
|
|
}
|
|
done:
|
|
lv_disp_flush_ready(disp_drv);
|
|
}
|
|
|
|
static void _lv_wayland_handle_input(void)
|
|
{
|
|
while (wl_display_prepare_read(application.display) != 0)
|
|
{
|
|
wl_display_dispatch_pending(application.display);
|
|
}
|
|
|
|
wl_display_read_events(application.display);
|
|
wl_display_dispatch_pending(application.display);
|
|
}
|
|
|
|
static void _lv_wayland_handle_output(void)
|
|
{
|
|
struct window *window;
|
|
bool shall_flush = application.cursor_flush_pending;
|
|
|
|
_LV_LL_READ(&application.window_ll, window)
|
|
{
|
|
if ((window->shall_close) && (window->close_cb != NULL))
|
|
{
|
|
window->shall_close = window->close_cb(window->lv_disp);
|
|
}
|
|
|
|
if (window->closed)
|
|
{
|
|
continue;
|
|
}
|
|
else if (window->shall_close)
|
|
{
|
|
window->closed = true;
|
|
window->shall_close = false;
|
|
shall_flush = true;
|
|
|
|
window->body->input.touch.x = 0;
|
|
window->body->input.touch.y = 0;
|
|
window->body->input.touch.state = LV_INDEV_STATE_RELEASED;
|
|
if (window->application->touch_obj == window->body)
|
|
{
|
|
window->application->touch_obj = NULL;
|
|
}
|
|
|
|
window->body->input.pointer.x = 0;
|
|
window->body->input.pointer.y = 0;
|
|
window->body->input.pointer.left_button = LV_INDEV_STATE_RELEASED;
|
|
window->body->input.pointer.right_button = LV_INDEV_STATE_RELEASED;
|
|
window->body->input.pointer.wheel_button = LV_INDEV_STATE_RELEASED;
|
|
window->body->input.pointer.wheel_diff = 0;
|
|
if (window->application->pointer_obj == window->body)
|
|
{
|
|
window->application->pointer_obj = NULL;
|
|
}
|
|
|
|
window->body->input.keyboard.key = 0;
|
|
window->body->input.keyboard.state = LV_INDEV_STATE_RELEASED;
|
|
if (window->application->keyboard_obj == window->body)
|
|
{
|
|
window->application->keyboard_obj = NULL;
|
|
}
|
|
destroy_window(window);
|
|
}
|
|
else if (window->resize_pending)
|
|
{
|
|
if (resize_window(window, window->resize_width, window->resize_height))
|
|
{
|
|
window->resize_width = window->width;
|
|
window->resize_height = window->height;
|
|
window->resize_pending = false;
|
|
shall_flush = true;
|
|
}
|
|
}
|
|
|
|
shall_flush |= window->flush_pending;
|
|
}
|
|
|
|
if (shall_flush)
|
|
{
|
|
if (wl_display_flush(application.display) == -1)
|
|
{
|
|
if (errno != EAGAIN)
|
|
{
|
|
LV_LOG_ERROR("failed to flush wayland display");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* All data flushed */
|
|
application.cursor_flush_pending = false;
|
|
_LV_LL_READ(&application.window_ll, window)
|
|
{
|
|
window->flush_pending = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void _lv_wayland_cycle(lv_timer_t * tmr)
|
|
{
|
|
LV_UNUSED(tmr);
|
|
_lv_wayland_handle_input();
|
|
_lv_wayland_handle_output();
|
|
}
|
|
|
|
static void _lv_wayland_pointer_read(lv_indev_drv_t *drv, lv_indev_data_t *data)
|
|
{
|
|
struct window *window = drv->disp->driver->user_data;
|
|
if (!window || window->closed)
|
|
{
|
|
return;
|
|
}
|
|
|
|
data->point.x = window->body->input.pointer.x;
|
|
data->point.y = window->body->input.pointer.y;
|
|
data->state = window->body->input.pointer.left_button;
|
|
}
|
|
|
|
static void _lv_wayland_pointeraxis_read(lv_indev_drv_t *drv, lv_indev_data_t *data)
|
|
{
|
|
struct window *window = drv->disp->driver->user_data;
|
|
if (!window || window->closed)
|
|
{
|
|
return;
|
|
}
|
|
|
|
data->state = window->body->input.pointer.wheel_button;
|
|
data->enc_diff = window->body->input.pointer.wheel_diff;
|
|
|
|
window->body->input.pointer.wheel_diff = 0;
|
|
}
|
|
|
|
static void _lv_wayland_keyboard_read(lv_indev_drv_t *drv, lv_indev_data_t *data)
|
|
{
|
|
struct window *window = drv->disp->driver->user_data;
|
|
if (!window || window->closed)
|
|
{
|
|
return;
|
|
}
|
|
|
|
data->key = window->body->input.keyboard.key;
|
|
data->state = window->body->input.keyboard.state;
|
|
}
|
|
|
|
static void _lv_wayland_touch_read(lv_indev_drv_t *drv, lv_indev_data_t *data)
|
|
{
|
|
struct window *window = drv->disp->driver->user_data;
|
|
if (!window || window->closed)
|
|
{
|
|
return;
|
|
}
|
|
|
|
data->point.x = window->body->input.touch.x;
|
|
data->point.y = window->body->input.touch.y;
|
|
data->state = window->body->input.touch.state;
|
|
}
|
|
|
|
/**********************
|
|
* GLOBAL FUNCTIONS
|
|
**********************/
|
|
|
|
/**
|
|
* Initialize Wayland driver
|
|
*/
|
|
void lv_wayland_init(void)
|
|
{
|
|
struct smm_events evs = {
|
|
NULL,
|
|
sme_new_pool,
|
|
sme_expand_pool,
|
|
sme_free_pool,
|
|
sme_new_buffer,
|
|
sme_init_buffer,
|
|
sme_free_buffer
|
|
};
|
|
|
|
application.xdg_runtime_dir = getenv("XDG_RUNTIME_DIR");
|
|
LV_ASSERT_MSG(application.xdg_runtime_dir, "cannot get XDG_RUNTIME_DIR");
|
|
|
|
// Create XKB context
|
|
application.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
|
LV_ASSERT_MSG(application.xkb_context, "failed to create XKB context");
|
|
if (application.xkb_context == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Connect to Wayland display
|
|
application.display = wl_display_connect(NULL);
|
|
LV_ASSERT_MSG(application.display, "failed to connect to Wayland server");
|
|
if (application.display == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* Add registry listener and wait for registry reception */
|
|
application.format = 0xFFFFFFFF;
|
|
application.registry = wl_display_get_registry(application.display);
|
|
wl_registry_add_listener(application.registry, ®istry_listener, &application);
|
|
wl_display_dispatch(application.display);
|
|
wl_display_roundtrip(application.display);
|
|
|
|
LV_ASSERT_MSG(application.compositor, "Wayland compositor not available");
|
|
if (application.compositor == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LV_ASSERT_MSG(application.shm, "Wayland SHM not available");
|
|
if (application.shm == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LV_ASSERT_MSG((application.format != 0xFFFFFFFF), "WL_SHM_FORMAT not available");
|
|
if (application.format == 0xFFFFFFFF)
|
|
{
|
|
return;
|
|
}
|
|
|
|
smm_init(&evs);
|
|
smm_setctx(&application);
|
|
|
|
#ifdef LV_WAYLAND_CLIENT_SIDE_DECORATIONS
|
|
const char * env_disable_decorations = getenv("LV_WAYLAND_DISABLE_WINDOWDECORATION");
|
|
application.opt_disable_decorations = ((env_disable_decorations != NULL) &&
|
|
(env_disable_decorations[0] != '0'));
|
|
#endif
|
|
|
|
_lv_ll_init(&application.window_ll, sizeof(struct window));
|
|
|
|
#ifndef LV_WAYLAND_TIMER_HANDLER
|
|
application.cycle_timer = lv_timer_create(_lv_wayland_cycle, LV_WAYLAND_CYCLE_PERIOD, NULL);
|
|
LV_ASSERT_MSG(application.cycle_timer, "failed to create cycle timer");
|
|
if (!application.cycle_timer)
|
|
{
|
|
return;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* De-initialize Wayland driver
|
|
*/
|
|
void lv_wayland_deinit(void)
|
|
{
|
|
struct window *window = NULL;
|
|
|
|
_LV_LL_READ(&application.window_ll, window)
|
|
{
|
|
if (!window->closed)
|
|
{
|
|
destroy_window(window);
|
|
}
|
|
}
|
|
|
|
smm_deinit();
|
|
|
|
if (application.shm)
|
|
{
|
|
wl_shm_destroy(application.shm);
|
|
}
|
|
|
|
#if LV_WAYLAND_XDG_SHELL
|
|
if (application.xdg_wm)
|
|
{
|
|
xdg_wm_base_destroy(application.xdg_wm);
|
|
}
|
|
#endif
|
|
|
|
#if LV_WAYLAND_WL_SHELL
|
|
if (application.wl_shell)
|
|
{
|
|
wl_shell_destroy(application.wl_shell);
|
|
}
|
|
#endif
|
|
|
|
if (application.wl_seat)
|
|
{
|
|
wl_seat_destroy(application.wl_seat);
|
|
}
|
|
|
|
if (application.subcompositor)
|
|
{
|
|
wl_subcompositor_destroy(application.subcompositor);
|
|
}
|
|
|
|
if (application.compositor)
|
|
{
|
|
wl_compositor_destroy(application.compositor);
|
|
}
|
|
|
|
wl_registry_destroy(application.registry);
|
|
wl_display_flush(application.display);
|
|
wl_display_disconnect(application.display);
|
|
|
|
_lv_ll_clear(&application.window_ll);
|
|
}
|
|
|
|
/**
|
|
* Get Wayland display file descriptor
|
|
* @return Wayland display file descriptor
|
|
*/
|
|
int lv_wayland_get_fd(void)
|
|
{
|
|
return wl_display_get_fd(application.display);
|
|
}
|
|
|
|
/**
|
|
* Create wayland window
|
|
* @param hor_res initial horizontal window size in pixels
|
|
* @param ver_res initial vertical window size in pixels
|
|
* @param title window title
|
|
* @param close_cb function to be called when the window gets closed by the user (optional)
|
|
* @return new display backed by a Wayland window, or NULL on error
|
|
*/
|
|
lv_disp_t * lv_wayland_create_window(lv_coord_t hor_res, lv_coord_t ver_res, char *title,
|
|
lv_wayland_display_close_f_t close_cb)
|
|
{
|
|
lv_color_t * buf1 = NULL;
|
|
struct window *window;
|
|
|
|
window = create_window(&application, hor_res, ver_res, title);
|
|
if (!window)
|
|
{
|
|
LV_LOG_ERROR("failed to create wayland window");
|
|
return NULL;
|
|
}
|
|
|
|
window->close_cb = close_cb;
|
|
|
|
/* Initialize draw buffer */
|
|
buf1 = lv_malloc(((hor_res * ver_res) / LVGL_DRAW_BUFFER_DIV) * sizeof(lv_color_t));
|
|
if (!buf1)
|
|
{
|
|
LV_LOG_ERROR("failed to allocate draw buffer");
|
|
destroy_window(window);
|
|
return NULL;
|
|
}
|
|
|
|
lv_disp_draw_buf_init(&window->lv_disp_draw_buf, buf1, NULL, (hor_res * ver_res) / LVGL_DRAW_BUFFER_DIV);
|
|
|
|
/* Initialize display driver */
|
|
lv_disp_drv_init(&window->lv_disp_drv);
|
|
window->lv_disp_drv.draw_buf = &window->lv_disp_draw_buf;
|
|
window->lv_disp_drv.hor_res = hor_res;
|
|
window->lv_disp_drv.ver_res = ver_res;
|
|
window->lv_disp_drv.flush_cb = _lv_wayland_flush;
|
|
window->lv_disp_drv.user_data = window;
|
|
|
|
/* Register display */
|
|
window->lv_disp = lv_disp_drv_register(&window->lv_disp_drv);
|
|
|
|
/* Register input */
|
|
lv_indev_drv_init(&window->lv_indev_drv_pointer);
|
|
window->lv_indev_drv_pointer.type = LV_INDEV_TYPE_POINTER;
|
|
window->lv_indev_drv_pointer.read_cb = _lv_wayland_pointer_read;
|
|
window->lv_indev_drv_pointer.disp = window->lv_disp;
|
|
window->lv_indev_pointer = lv_indev_drv_register(&window->lv_indev_drv_pointer);
|
|
if (!window->lv_indev_pointer)
|
|
{
|
|
LV_LOG_ERROR("failed to register pointer indev");
|
|
}
|
|
|
|
lv_indev_drv_init(&window->lv_indev_drv_pointeraxis);
|
|
window->lv_indev_drv_pointeraxis.type = LV_INDEV_TYPE_ENCODER;
|
|
window->lv_indev_drv_pointeraxis.read_cb = _lv_wayland_pointeraxis_read;
|
|
window->lv_indev_drv_pointeraxis.disp = window->lv_disp;
|
|
window->lv_indev_pointeraxis = lv_indev_drv_register(&window->lv_indev_drv_pointeraxis);
|
|
if (!window->lv_indev_pointeraxis)
|
|
{
|
|
LV_LOG_ERROR("failed to register pointeraxis indev");
|
|
}
|
|
|
|
lv_indev_drv_init(&window->lv_indev_drv_touch);
|
|
window->lv_indev_drv_touch.type = LV_INDEV_TYPE_POINTER;
|
|
window->lv_indev_drv_touch.read_cb = _lv_wayland_touch_read;
|
|
window->lv_indev_drv_touch.disp = window->lv_disp;
|
|
window->lv_indev_touch = lv_indev_drv_register(&window->lv_indev_drv_touch);
|
|
if (!window->lv_indev_touch)
|
|
{
|
|
LV_LOG_ERROR("failed to register touch indev");
|
|
}
|
|
|
|
lv_indev_drv_init(&window->lv_indev_drv_keyboard);
|
|
window->lv_indev_drv_keyboard.type = LV_INDEV_TYPE_KEYPAD;
|
|
window->lv_indev_drv_keyboard.read_cb = _lv_wayland_keyboard_read;
|
|
window->lv_indev_drv_keyboard.disp = window->lv_disp;
|
|
window->lv_indev_keyboard = lv_indev_drv_register(&window->lv_indev_drv_keyboard);
|
|
if (!window->lv_indev_keyboard)
|
|
{
|
|
LV_LOG_ERROR("failed to register keyboard indev");
|
|
}
|
|
|
|
return window->lv_disp;
|
|
}
|
|
|
|
/**
|
|
* Close wayland window
|
|
* @param disp LVGL display using window to be closed
|
|
*/
|
|
void lv_wayland_close_window(lv_disp_t * disp)
|
|
{
|
|
struct window *window = disp->driver->user_data;
|
|
if (!window || window->closed)
|
|
{
|
|
return;
|
|
}
|
|
window->shall_close = true;
|
|
window->close_cb = NULL;
|
|
}
|
|
|
|
/**
|
|
* Check if a Wayland window is open on the specified display. Otherwise (if
|
|
* argument is NULL), check if any Wayland window is open.
|
|
* @return true if window open, false otherwise
|
|
*/
|
|
bool lv_wayland_window_is_open(lv_disp_t * disp)
|
|
{
|
|
struct window *window;
|
|
bool open = false;
|
|
|
|
if (disp == NULL)
|
|
{
|
|
_LV_LL_READ(&application.window_ll, window)
|
|
{
|
|
if (!window->closed)
|
|
{
|
|
open = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
window = disp->driver->user_data;
|
|
open = (!window->closed);
|
|
}
|
|
|
|
return open;
|
|
}
|
|
|
|
/**
|
|
* Set/unset window fullscreen mode
|
|
* @param disp LVGL display using window to be set/unset fullscreen
|
|
* @param fullscreen requested status (true = fullscreen)
|
|
*/
|
|
void lv_wayland_window_set_fullscreen(lv_disp_t * disp, bool fullscreen)
|
|
{
|
|
struct window *window = disp->driver->user_data;
|
|
if (!window || window->closed)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (window->fullscreen != fullscreen)
|
|
{
|
|
if (0)
|
|
{
|
|
// Needed for #if madness below
|
|
}
|
|
#if LV_WAYLAND_XDG_SHELL
|
|
else if (window->xdg_toplevel)
|
|
{
|
|
if (fullscreen)
|
|
{
|
|
xdg_toplevel_set_fullscreen(window->xdg_toplevel, NULL);
|
|
}
|
|
else
|
|
{
|
|
xdg_toplevel_unset_fullscreen(window->xdg_toplevel);
|
|
}
|
|
window->fullscreen = fullscreen;
|
|
window->flush_pending = true;
|
|
}
|
|
#endif
|
|
#if LV_WAYLAND_WL_SHELL
|
|
else if (window->wl_shell_surface)
|
|
{
|
|
if (fullscreen)
|
|
{
|
|
wl_shell_surface_set_fullscreen(window->wl_shell_surface,
|
|
WL_SHELL_SURFACE_FULLSCREEN_METHOD_SCALE,
|
|
0, NULL);
|
|
}
|
|
else
|
|
{
|
|
wl_shell_surface_set_toplevel(window->wl_shell_surface);
|
|
}
|
|
window->fullscreen = fullscreen;
|
|
window->flush_pending = true;
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
LV_LOG_WARN("Wayland fullscreen mode not supported");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get pointer input device for given LVGL display
|
|
* @param disp LVGL display
|
|
* @return input device connected to pointer events, or NULL on error
|
|
*/
|
|
lv_indev_t * lv_wayland_get_pointer(lv_disp_t * disp)
|
|
{
|
|
struct window *window = disp->driver->user_data;
|
|
if (!window)
|
|
{
|
|
return NULL;
|
|
}
|
|
return window->lv_indev_pointer;
|
|
}
|
|
|
|
/**
|
|
* Get pointer axis input device for given LVGL display
|
|
* @param disp LVGL display
|
|
* @return input device connected to pointer axis events, or NULL on error
|
|
*/
|
|
lv_indev_t * lv_wayland_get_pointeraxis(lv_disp_t * disp)
|
|
{
|
|
struct window *window = disp->driver->user_data;
|
|
if (!window)
|
|
{
|
|
return NULL;
|
|
}
|
|
return window->lv_indev_pointeraxis;
|
|
}
|
|
|
|
/**
|
|
* Get keyboard input device for given LVGL display
|
|
* @param disp LVGL display
|
|
* @return input device connected to keyboard, or NULL on error
|
|
*/
|
|
lv_indev_t * lv_wayland_get_keyboard(lv_disp_t * disp)
|
|
{
|
|
struct window *window = disp->driver->user_data;
|
|
if (!window)
|
|
{
|
|
return NULL;
|
|
}
|
|
return window->lv_indev_keyboard;
|
|
}
|
|
|
|
/**
|
|
* Get touchscreen input device for given LVGL display
|
|
* @param disp LVGL display
|
|
* @return input device connected to touchscreen, or NULL on error
|
|
*/
|
|
lv_indev_t * lv_wayland_get_touchscreen(lv_disp_t * disp)
|
|
{
|
|
struct window *window = disp->driver->user_data;
|
|
if (!window)
|
|
{
|
|
return NULL;
|
|
}
|
|
return window->lv_indev_touch;
|
|
}
|
|
|
|
#ifdef LV_WAYLAND_TIMER_HANDLER
|
|
/**
|
|
* Wayland specific timer handler (use in place of LVGL lv_timer_handler)
|
|
* @return time until next timer expiry in milliseconds
|
|
*/
|
|
uint32_t lv_wayland_timer_handler(void)
|
|
{
|
|
int i;
|
|
struct window *window;
|
|
lv_timer_t *input_timer[4];
|
|
uint32_t time_till_next;
|
|
|
|
/* Wayland input handling */
|
|
_lv_wayland_handle_input();
|
|
|
|
/* Ready input timers (to probe for any input recieved) */
|
|
_LV_LL_READ(&application.window_ll, window)
|
|
{
|
|
input_timer[0] = window->lv_indev_pointer->driver->read_timer;
|
|
input_timer[1] = window->lv_indev_pointeraxis->driver->read_timer;
|
|
input_timer[2] = window->lv_indev_keyboard->driver->read_timer;
|
|
input_timer[3] = window->lv_indev_touch->driver->read_timer;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
if (input_timer[i])
|
|
{
|
|
lv_timer_ready(input_timer[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* LVGL handling */
|
|
time_till_next = lv_timer_handler();
|
|
|
|
/* Wayland output handling */
|
|
_lv_wayland_handle_output();
|
|
|
|
|
|
/* Set 'errno' if a Wayland flush is outstanding (i.e. data still needs to
|
|
* be sent to the compositor, but the compositor pipe/connection is unable
|
|
* to take more data at this time).
|
|
*/
|
|
_LV_LL_READ(&application.window_ll, window)
|
|
{
|
|
if (window->flush_pending)
|
|
{
|
|
errno = EAGAIN;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return time_till_next;
|
|
}
|
|
#endif
|
|
#endif // USE_WAYLAND
|