diff options
| author | ManoloFLTK <41016272+ManoloFLTK@users.noreply.github.com> | 2022-03-04 15:40:29 +0100 |
|---|---|---|
| committer | ManoloFLTK <41016272+ManoloFLTK@users.noreply.github.com> | 2022-03-04 15:41:00 +0100 |
| commit | 3718effc431f5622a23c55b254153efdfe4e72c4 (patch) | |
| tree | d8a805870c6a3785022e2f52f0c3715410e29a37 /libdecor/demo | |
| parent | a773fdc44bfb818f1830e9e48ba765881e68c942 (diff) | |
Add the Wayland platform to FLTK 1.4
Diffstat (limited to 'libdecor/demo')
| -rw-r--r-- | libdecor/demo/demo.c | 1304 | ||||
| -rw-r--r-- | libdecor/demo/egl.c | 355 |
2 files changed, 1659 insertions, 0 deletions
diff --git a/libdecor/demo/demo.c b/libdecor/demo/demo.c new file mode 100644 index 000000000..041e35d6c --- /dev/null +++ b/libdecor/demo/demo.c @@ -0,0 +1,1304 @@ +/* + * Copyright © 2011 Benjamin Franzke + * Copyright © 2010 Intel Corporation + * Copyright © 2018 Jonas Ådahl + * + * 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 (including the + * next paragraph) 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. + */ + +#include "config.h" + +#include <errno.h> +#include <fcntl.h> +#include <linux/input.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <unistd.h> +#include <wayland-cursor.h> +#include <xkbcommon/xkbcommon.h> + +#include "libdecor.h" +#include "utils.h" +#include "cursor-settings.h" +#include "os-compatibility.h" + +#include "xdg-shell-client-protocol.h" + +struct window; + +static const size_t chk = 16; +static const int DEFAULT_WIDTH = 30*chk; +static const int DEFAULT_HEIGHT = 20*chk; + +static const int POPUP_WIDTH = 100; +static const int POPUP_HEIGHT = 300; + +static const char *proxy_tag = "libdecor-demo"; + +static const char *titles[] = { + "Hello!", + "Hallå!", + "Привет!", + "Γειά σου!", + "שלום!", + "你好!", + "สวัสดี!", + "こんにちは!", + "👻❤️🤖➕🍰", +}; + +static const size_t N_TITLES = ARRAY_SIZE(titles); + + +static bool +own_proxy(struct wl_proxy *proxy) +{ + return (wl_proxy_get_tag(proxy) == &proxy_tag); +} + +static bool +own_output(struct wl_output *output) +{ + return own_proxy((struct wl_proxy *) output); +} + +struct buffer { + struct wl_buffer *wl_buffer; + void *data; + size_t data_size; +}; + +struct popup { + struct wl_surface *wl_surface; + struct xdg_surface *xdg_surface; + struct xdg_popup *xdg_popup; + struct xdg_surface *parent; + struct seat *seat; + struct window *window; +}; + +struct window { + struct wl_surface *wl_surface; + struct buffer *buffer; + struct libdecor_frame *frame; + int content_width; + int content_height; + int configured_width; + int configured_height; + int floating_width; + int floating_height; + enum libdecor_window_state window_state; + struct wl_list outputs; + int scale; + struct popup *popup; + size_t title_index; +}; + +struct seat { + struct wl_seat *wl_seat; + struct wl_keyboard *wl_keyboard; + struct wl_pointer *wl_pointer; + struct wl_list link; + struct wl_list pointer_outputs; + struct wl_cursor_theme *cursor_theme; + struct wl_cursor *left_ptr_cursor; + struct wl_surface *cursor_surface; + struct wl_surface *pointer_focus; + int pointer_scale; + uint32_t serial; + wl_fixed_t pointer_sx; + wl_fixed_t pointer_sy; + char *name; + + struct xkb_context *xkb_context; + struct xkb_state *xkb_state; +}; + +struct output { + uint32_t id; + struct wl_output *wl_output; + int scale; + struct wl_list link; +}; + +struct window_output { + struct output* output; + struct wl_list link; +}; + +struct pointer_output { + struct output* output; + struct wl_list link; +}; + +static struct wl_compositor *wl_compositor; +static struct wl_shm *wl_shm; +static struct xdg_wm_base *xdg_wm_base; +static struct wl_list seats; +static struct wl_list outputs; + +static bool has_xrgb = false; + +static struct window *window; + +static void +redraw(struct window *window); + +static void +resize(struct window *window, int width, int height) +{ + struct libdecor_state *state; + + if (!libdecor_frame_is_floating(window->frame)) { + printf("... ignoring in non-floating mode\n"); + return; + } + + /* commit changes to decorations */ + state = libdecor_state_new( width, height); + libdecor_frame_commit(window->frame, state, NULL); + libdecor_state_free(state); + /* force redraw of content and commit */ + window->configured_width = width; + window->configured_height = height; + /* store floating dimensions */ + window->floating_width = width; + window->floating_height = height; + redraw(window); +} + +static struct buffer * +create_shm_buffer(int width, + int height, + uint32_t format); + +static void +update_scale(struct window *window) +{ + int scale = 1; + struct window_output *window_output; + + wl_list_for_each(window_output, &window->outputs, link) { + scale = MAX(scale, window_output->output->scale); + } + if (scale != window->scale) { + window->scale = scale; + redraw(window); + } +} + +static void +shm_format(void *data, + struct wl_shm *wl_shm, + uint32_t format) +{ + if (format == WL_SHM_FORMAT_XRGB8888) + has_xrgb = true; +} + +static struct wl_shm_listener shm_listener = { + shm_format +}; + +static void +try_update_cursor(struct seat *seat); + +static void +cursor_surface_enter(void *data, + struct wl_surface *wl_surface, + struct wl_output *wl_output) +{ + struct seat *seat = data; + struct pointer_output *pointer_output; + + if (!own_output(wl_output)) + return; + + pointer_output = zalloc(sizeof *pointer_output); + pointer_output->output = wl_output_get_user_data(wl_output); + wl_list_insert(&seat->pointer_outputs, &pointer_output->link); + try_update_cursor(seat); +} + +static void +cursor_surface_leave(void *data, + struct wl_surface *wl_surface, + struct wl_output *wl_output) +{ + struct seat *seat = data; + struct pointer_output *pointer_output, *tmp; + + wl_list_for_each_safe(pointer_output, tmp, &seat->pointer_outputs, link) { + if (pointer_output->output->wl_output == wl_output) { + wl_list_remove(&pointer_output->link); + free(pointer_output); + } + } +} + +static struct wl_surface_listener cursor_surface_listener = { + cursor_surface_enter, + cursor_surface_leave, +}; + +static void +init_cursors(struct seat *seat) +{ + char *name; + int size; + struct wl_cursor_theme *theme; + + if (!libdecor_get_cursor_settings(&name, &size)) { + name = NULL; + size = 24; + } + size *= seat->pointer_scale; + + theme = wl_cursor_theme_load(name, size, wl_shm); + free(name); + if (theme != NULL) { + if (seat->cursor_theme) + wl_cursor_theme_destroy(seat->cursor_theme); + seat->cursor_theme = theme; + } + if (seat->cursor_theme) + seat->left_ptr_cursor + = wl_cursor_theme_get_cursor(seat->cursor_theme, "left_ptr"); + if (!seat->cursor_surface) { + seat->cursor_surface = wl_compositor_create_surface( + wl_compositor); + wl_surface_add_listener(seat->cursor_surface, + &cursor_surface_listener, seat); + } +} + +static void +set_cursor(struct seat *seat) +{ + struct wl_cursor *wl_cursor; + struct wl_cursor_image *image; + struct wl_buffer *buffer; + const int scale = seat->pointer_scale; + + if (!seat->cursor_theme) + return; + + wl_cursor = seat->left_ptr_cursor; + + image = wl_cursor->images[0]; + buffer = wl_cursor_image_get_buffer(image); + wl_pointer_set_cursor(seat->wl_pointer, seat->serial, + seat->cursor_surface, + image->hotspot_x / scale, + image->hotspot_y / scale); + wl_surface_attach(seat->cursor_surface, buffer, 0, 0); + wl_surface_set_buffer_scale(seat->cursor_surface, scale); + wl_surface_damage_buffer(seat->cursor_surface, 0, 0, + image->width, image->height); + wl_surface_commit(seat->cursor_surface); +} + +static void +try_update_cursor(struct seat *seat) +{ + struct pointer_output *pointer_output; + int scale = 1; + + wl_list_for_each(pointer_output, &seat->pointer_outputs, link) { + scale = MAX(scale, pointer_output->output->scale); + } + + if (scale != seat->pointer_scale) { + seat->pointer_scale = scale; + init_cursors(seat); + set_cursor(seat); + } +} + +static void +pointer_enter(void *data, + struct wl_pointer *wl_pointer, + uint32_t serial, + struct wl_surface *surface, + wl_fixed_t surface_x, + wl_fixed_t surface_y) +{ + struct seat *seat = data; + + seat->pointer_focus = surface; + seat->serial = serial; + + if (surface != window->wl_surface) + return; + + set_cursor(seat); + + seat->pointer_sx = surface_x; + seat->pointer_sy = surface_y; +} + +static void +pointer_leave(void *data, + struct wl_pointer *wl_pointer, + uint32_t serial, + struct wl_surface *surface) +{ + struct seat *seat = data; + if (seat->pointer_focus == surface) + seat->pointer_focus = NULL; +} + +static void +pointer_motion(void *data, + struct wl_pointer *wl_pointer, + uint32_t time, + wl_fixed_t surface_x, + wl_fixed_t surface_y) +{ + struct seat *seat = data; + + seat->pointer_sx = surface_x; + seat->pointer_sy = surface_y; +} + +static struct xdg_positioner * +create_positioner(struct seat *seat) +{ + struct xdg_positioner *positioner; + enum xdg_positioner_constraint_adjustment constraint_adjustment; + int x, y; + + positioner = xdg_wm_base_create_positioner(xdg_wm_base); + xdg_positioner_set_size(positioner, POPUP_WIDTH, POPUP_HEIGHT); + + libdecor_frame_translate_coordinate(window->frame, + wl_fixed_to_int(seat->pointer_sx), + wl_fixed_to_int(seat->pointer_sy), + &x, &y); + + xdg_positioner_set_anchor_rect(positioner, x, y, 1, 1); + + constraint_adjustment = (XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y | + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X); + xdg_positioner_set_constraint_adjustment (positioner, + constraint_adjustment); + + xdg_positioner_set_anchor (positioner, + XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT); + xdg_positioner_set_gravity (positioner, + XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT); + + return positioner; +} + +static void +xdg_popup_configure(void *data, + struct xdg_popup *xdg_popup, + int32_t x, + int32_t y, + int32_t width, + int32_t height) +{ +} + +static void +popup_destroy(struct popup *popup) +{ + libdecor_frame_popup_ungrab(popup->window->frame, + popup->seat->name); + xdg_popup_destroy(popup->xdg_popup); + xdg_surface_destroy(popup->xdg_surface); + wl_surface_destroy(popup->wl_surface); + popup->window->popup = NULL; + free(popup); +} + +static void +xdg_popup_done(void *data, + struct xdg_popup *xdg_popup) +{ + struct popup *popup = data; + + popup_destroy(popup); +} + +static const struct xdg_popup_listener xdg_popup_listener = { + xdg_popup_configure, + xdg_popup_done, +}; + +static void +xdg_surface_configure(void *data, + struct xdg_surface *xdg_surface, + uint32_t serial) +{ + struct popup *popup = data; + uint32_t *pixels; + struct buffer *buffer; + int y; + + buffer = create_shm_buffer(POPUP_WIDTH, POPUP_HEIGHT, + WL_SHM_FORMAT_XRGB8888); + pixels = buffer->data; + for (y = 0; y < POPUP_HEIGHT; y++) { + int x; + + for (x = 0; x < POPUP_WIDTH; x++) + pixels[y * POPUP_WIDTH + x] = 0xff4455ff; + } + + wl_surface_attach(popup->wl_surface, buffer->wl_buffer, 0, 0); + wl_surface_set_buffer_scale(window->wl_surface, window->scale); + wl_surface_damage(window->wl_surface, 0, 0, + POPUP_WIDTH, POPUP_HEIGHT); + xdg_surface_ack_configure(popup->xdg_surface, serial); + wl_surface_commit(popup->wl_surface); +} + +static const struct xdg_surface_listener xdg_surface_listener = { + xdg_surface_configure, +}; + +static void +open_popup(struct seat *seat) +{ + struct popup *popup; + struct xdg_positioner *positioner; + + popup = zalloc(sizeof *popup); + + popup->wl_surface = wl_compositor_create_surface(wl_compositor); + popup->xdg_surface = xdg_wm_base_get_xdg_surface (xdg_wm_base, + popup->wl_surface); + popup->parent = libdecor_frame_get_xdg_surface(window->frame); + popup->window = window; + popup->seat = seat; + positioner = create_positioner(seat); + popup->xdg_popup = xdg_surface_get_popup(popup->xdg_surface, + popup->parent, + positioner); + xdg_positioner_destroy(positioner); + + xdg_surface_add_listener (popup->xdg_surface, + &xdg_surface_listener, + popup); + xdg_popup_add_listener (popup->xdg_popup, + &xdg_popup_listener, + popup); + + window->popup = popup; + + xdg_popup_grab(popup->xdg_popup, seat->wl_seat, seat->serial); + wl_surface_commit(popup->wl_surface); + + libdecor_frame_popup_grab(window->frame, seat->name); +} + +static void +close_popup(struct window *window) +{ + struct popup *popup = window->popup; + + popup_destroy(popup); +} + +static void +pointer_button(void *data, + struct wl_pointer *wl_pointer, + uint32_t serial, + uint32_t time, + uint32_t button, + uint32_t state) +{ + struct seat *seat = data; + + if (seat->pointer_focus != window->wl_surface) + return; + + seat->serial = serial; + + if (window->popup && + state == WL_POINTER_BUTTON_STATE_PRESSED) + close_popup(window); + + if (button == BTN_LEFT && + state == WL_POINTER_BUTTON_STATE_PRESSED) { + libdecor_frame_move(window->frame, seat->wl_seat, serial); + } else if (button == BTN_MIDDLE && + state == WL_POINTER_BUTTON_STATE_PRESSED) { + libdecor_frame_show_window_menu(window->frame, + seat->wl_seat, + serial, + wl_fixed_to_int(seat->pointer_sx), + wl_fixed_to_int(seat->pointer_sy)); + } else if (button == BTN_RIGHT && + state == WL_POINTER_BUTTON_STATE_PRESSED) { + if (!window->popup) + open_popup(seat); + } +} + +static void +pointer_axis(void *data, + struct wl_pointer *wl_pointer, + uint32_t time, + uint32_t axis, + wl_fixed_t value) +{ +} + +static struct wl_pointer_listener pointer_listener = { + pointer_enter, + pointer_leave, + pointer_motion, + pointer_button, + pointer_axis +}; + +static void +keyboard_keymap(void *data, + struct wl_keyboard *wl_keyboard, + uint32_t format, + int32_t fd, + uint32_t size) +{ + struct seat *seat = data; + + if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { + close(fd); + return; + } + + char *map_str = (char *)(mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0)); + if (map_str == MAP_FAILED) { + close(fd); + fprintf(stderr, "keymap mmap failed: %s", strerror(errno)); + return; + } + + struct xkb_keymap *keymap = xkb_keymap_new_from_string( + seat->xkb_context, map_str, + XKB_KEYMAP_FORMAT_TEXT_V1, + XKB_KEYMAP_COMPILE_NO_FLAGS); + munmap(map_str, size); + close(fd); + + if (!keymap) + return; + + seat->xkb_state = xkb_state_new(keymap); + + xkb_keymap_unref(keymap); +} + +static void +keyboard_enter(void *data, + struct wl_keyboard *wl_keyboard, + uint32_t serial, + struct wl_surface *surface, + struct wl_array *keys) +{ +} + +static void +keyboard_leave(void *data, + struct wl_keyboard *wl_keyboard, + uint32_t serial, + struct wl_surface *surface) +{ +} + +static void +keyboard_key(void *data, + struct wl_keyboard *wl_keyboard, + uint32_t serial, + uint32_t time, + uint32_t key, + uint32_t state) +{ + struct seat *seat = data; + + if (state & WL_KEYBOARD_KEY_STATE_PRESSED) { + const xkb_keysym_t *syms; + + if (xkb_state_key_get_syms(seat->xkb_state, key + 8, &syms) != 1) + return; + + switch (syms[0]) { + case XKB_KEY_Escape: + printf("close\n"); + libdecor_frame_close(window->frame); + break; + case XKB_KEY_1: /* toggle resizability */ + if (libdecor_frame_has_capability( + window->frame, LIBDECOR_ACTION_RESIZE)) { + printf("set fixed-size\n"); + libdecor_frame_unset_capabilities(window->frame, + LIBDECOR_ACTION_RESIZE); + } + else { + printf("set resizeable\n"); + libdecor_frame_set_capabilities(window->frame, + LIBDECOR_ACTION_RESIZE); + } + break; + case XKB_KEY_2: /* maximize */ + printf("maximize\n"); + libdecor_frame_set_maximized(window->frame); + break; + case XKB_KEY_3: /* un-maximize / restore */ + printf("un-maximize\n"); + libdecor_frame_unset_maximized(window->frame); + break; + case XKB_KEY_4: /* fullscreen */ + printf("fullscreen\n"); + libdecor_frame_set_fullscreen(window->frame, NULL); + break; + case XKB_KEY_5: /* un-fullscreen / restore */ + printf("un-fullscreen\n"); + libdecor_frame_unset_fullscreen(window->frame); + break; + case XKB_KEY_minus: + case XKB_KEY_plus: + { + const int dd = (syms[0] == XKB_KEY_minus ? -1 : +1) * chk/2; + printf("resize to: %i x %i\n", + window->configured_width + dd, + window->configured_height + dd); + resize(window, + window->configured_width + dd, + window->configured_height + dd); + } + break; + case XKB_KEY_v: /* VGA: 640x480 */ + printf("set VGA resolution: 640x480\n"); + resize(window, 640, 480); + break; + case XKB_KEY_s: /* SVGA: 800x600 */ + printf("set SVGA resolution: 800x600\n"); + resize(window, 800, 600); + break; + case XKB_KEY_x: /* XVGA: 1024x768 */ + printf("set XVGA resolution: 1024x768\n"); + resize(window, 1024, 768); + break; + case XKB_KEY_t: + libdecor_frame_set_title(window->frame, titles[window->title_index]); + window->title_index = (window->title_index + 1) % N_TITLES; + break; + case XKB_KEY_h: /* toggle decorations */ + libdecor_frame_set_visibility( + window->frame, + !libdecor_frame_is_visible(window->frame)); + printf("decorations %s\n", + libdecor_frame_is_visible(window->frame) ? + "visible" : "hidden"); + break; + } + } +} + +static void +keyboard_modifiers(void *data, + struct wl_keyboard *wl_keyboard, + uint32_t serial, + uint32_t mods_depressed, + uint32_t mods_latched, + uint32_t mods_locked, + uint32_t group) +{ + struct seat *seat = data; + + xkb_state_update_mask(seat->xkb_state, + mods_depressed, mods_latched, mods_locked, + 0, 0, group); +} + +static void +keyboard_repeat_info(void *data, + struct wl_keyboard *wl_keyboard, + int32_t rate, + int32_t delay) +{ +} + +static struct wl_keyboard_listener keyboard_listener = { + keyboard_keymap, + keyboard_enter, + keyboard_leave, + keyboard_key, + keyboard_modifiers, + keyboard_repeat_info, +}; + +static void +seat_capabilities(void *data, + struct wl_seat *wl_seat, + uint32_t capabilities) +{ + struct seat *seat = data; + if (capabilities & 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, + seat); + seat->pointer_scale = 1; + init_cursors(seat); + } else if (!(capabilities & WL_SEAT_CAPABILITY_POINTER) && + seat->wl_pointer) { + wl_pointer_release(seat->wl_pointer); + seat->wl_pointer = NULL; + } + + if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD && + !seat->wl_keyboard) { + seat->wl_keyboard = wl_seat_get_keyboard(wl_seat); + seat->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + wl_keyboard_add_listener(seat->wl_keyboard, &keyboard_listener, + seat); + } else if (!(capabilities & WL_SEAT_CAPABILITY_KEYBOARD) && + seat->wl_keyboard) { + xkb_context_unref(seat->xkb_context); + wl_keyboard_release(seat->wl_keyboard); + seat->wl_keyboard = NULL; + } +} + +static void +seat_name(void *data, + struct wl_seat *wl_seat, + const char *name) +{ + struct seat *seat = data; + + seat->name = strdup(name); +} + +static struct wl_seat_listener seat_listener = { + seat_capabilities, + seat_name +}; + +static void +output_geometry(void *data, + struct wl_output *wl_output, + int32_t x, + int32_t y, + int32_t physical_width, + int32_t physical_height, + int32_t subpixel, + const char *make, + const char *model, + int32_t transform) +{ +} + +static void +output_mode(void *data, + struct wl_output *wl_output, + uint32_t flags, + int32_t width, + int32_t height, + int32_t refresh) +{ +} + +static void +output_done(void *data, + struct wl_output *wl_output) +{ + struct output *output = data; + struct seat *seat; + + if (window) { + if (output->scale != window->scale) + update_scale(window); + } + + wl_list_for_each(seat, &seats, link) { + try_update_cursor(seat); + } +} + +static void +output_scale(void *data, + struct wl_output *wl_output, + int32_t factor) +{ + struct output *output = data; + + output->scale = factor; +} + +static struct wl_output_listener output_listener = { + output_geometry, + output_mode, + output_done, + output_scale +}; + +static void +xdg_wm_base_ping(void *user_data, + struct xdg_wm_base *xdg_wm_base, + uint32_t serial) +{ + xdg_wm_base_pong(xdg_wm_base, serial); +} + +static const struct xdg_wm_base_listener xdg_wm_base_listener = { + xdg_wm_base_ping, +}; + +static void +registry_handle_global(void *user_data, + struct wl_registry *wl_registry, + uint32_t id, + const char *interface, + uint32_t version) +{ + struct seat *seat; + struct output *output; + + if (strcmp(interface, "wl_compositor") == 0) { + if (version < 4) { + fprintf(stderr, "wl_compositor version >= 4 required"); + exit(EXIT_FAILURE); + } + wl_compositor = + wl_registry_bind(wl_registry, + id, &wl_compositor_interface, 4); + } else if (strcmp(interface, "wl_shm") == 0) { + wl_shm = wl_registry_bind(wl_registry, + id, &wl_shm_interface, 1); + wl_shm_add_listener(wl_shm, &shm_listener, NULL); + } else if (strcmp(interface, "wl_seat") == 0) { + if (version < 3) { + fprintf(stderr, "%s version 3 required but only version " + "%i is available\n", interface, version); + exit(EXIT_FAILURE); + } + seat = zalloc(sizeof *seat); + wl_list_init(&seat->pointer_outputs); + seat->wl_seat = wl_registry_bind(wl_registry, + id, &wl_seat_interface, 3); + wl_seat_add_listener(seat->wl_seat, &seat_listener, seat); + } else if (strcmp(interface, "wl_output") == 0) { + if (version < 2) { + fprintf(stderr, "%s version 3 required but only version " + "%i is available\n", interface, version); + exit(EXIT_FAILURE); + } + output = zalloc(sizeof *output); + output->id = id; + output->scale = 1; + output->wl_output = wl_registry_bind(wl_registry, + id, &wl_output_interface, + 2); + wl_proxy_set_tag((struct wl_proxy *) output->wl_output, + &proxy_tag); + wl_output_add_listener(output->wl_output, &output_listener, + output); + wl_list_insert(&outputs, &output->link); + } else if (strcmp(interface, "xdg_wm_base") == 0) { + xdg_wm_base = wl_registry_bind(wl_registry, id, + &xdg_wm_base_interface, + 1); + xdg_wm_base_add_listener(xdg_wm_base, + &xdg_wm_base_listener, + NULL); + } +} + +static void +registry_handle_global_remove(void *data, struct wl_registry *registry, + uint32_t name) +{ + struct output *output; + struct window_output *window_output; + + wl_list_for_each(output, &outputs, link) { + if (output->id == name) { + wl_list_for_each(window_output, &window->outputs, + link) { + if (window_output->output == output) { + wl_list_remove(&window_output->link); + free(window_output); + } + } + wl_list_remove(&output->link); + wl_output_destroy(output->wl_output); + free(output); + break; + } + } +} + +static const struct wl_registry_listener registry_listener = { + registry_handle_global, + registry_handle_global_remove +}; + +static void +handle_error(struct libdecor *context, + enum libdecor_error error, + const char *message) +{ + fprintf(stderr, "Caught error (%d): %s\n", error, message); + exit(EXIT_FAILURE); +} + +static struct libdecor_interface libdecor_iface = { + .error = handle_error, +}; + +static void +buffer_release(void *user_data, + struct wl_buffer *wl_buffer) +{ + struct buffer *buffer = user_data; + + wl_buffer_destroy(buffer->wl_buffer); + munmap(buffer->data, buffer->data_size); + free(buffer); +} + +static const struct wl_buffer_listener buffer_listener = { + buffer_release +}; + +static struct buffer * +create_shm_buffer(int width, + int height, + uint32_t format) +{ + struct wl_shm_pool *pool; + int fd, size, stride; + void *data; + struct buffer *buffer; + + stride = width * 4; + size = stride * height; + + fd = os_create_anonymous_file(size); + if (fd < 0) { + fprintf(stderr, "creating a buffer file for %d B failed: %s\n", + size, strerror(errno)); + return NULL; + } + + data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (data == MAP_FAILED) { + fprintf(stderr, "mmap failed: %s\n", strerror(errno)); + close(fd); + return NULL; + } + + buffer = zalloc(sizeof *buffer); + + pool = wl_shm_create_pool(wl_shm, fd, size); + buffer->wl_buffer = wl_shm_pool_create_buffer(pool, 0, + width, height, + stride, format); + wl_buffer_add_listener(buffer->wl_buffer, &buffer_listener, buffer); + wl_shm_pool_destroy(pool); + close(fd); + + buffer->data = data; + buffer->data_size = size; + + return buffer; +} + +static void +paint_buffer(struct buffer *buffer, + int width, + int height, + int scale, + enum libdecor_window_state window_state) +{ + uint32_t *pixels = buffer->data; + uint32_t bg, fg, color; + int y, x, sx, sy; + size_t off; + int stride = width * scale; + + if (window_state & LIBDECOR_WINDOW_STATE_ACTIVE) { + fg = 0xffbcbcbc; + bg = 0xff8e8e8e; + } else { + fg = 0xff8e8e8e; + bg = 0xff484848; + } + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + color = (x & chk) ^ (y & chk) ? fg : bg; + for (sx = 0; sx < scale; sx++) { + for (sy = 0; sy < scale; sy++) { + off = x * scale + sx + + (y * scale + sy) * stride; + pixels[off] = color; + } + } + } + } +} + +static void +redraw(struct window *window) +{ + struct buffer *buffer; + + buffer = create_shm_buffer(window->configured_width * window->scale, + window->configured_height * window->scale, + WL_SHM_FORMAT_XRGB8888); + paint_buffer(buffer, window->configured_width, + window->configured_height, window->scale, + window->window_state); + + wl_surface_attach(window->wl_surface, buffer->wl_buffer, 0, 0); + wl_surface_set_buffer_scale(window->wl_surface, window->scale); + wl_surface_damage_buffer(window->wl_surface, 0, 0, + window->configured_width * window->scale, + window->configured_height * window->scale); + wl_surface_commit(window->wl_surface); +} + +static void +handle_configure(struct libdecor_frame *frame, + struct libdecor_configuration *configuration, + void *user_data) +{ + struct window *window = user_data; + int width, height; + enum libdecor_window_state window_state; + struct libdecor_state *state; + + if (!libdecor_configuration_get_content_size(configuration, frame, + &width, &height)) { + width = window->content_width; + height = window->content_height; + } + + width = (width == 0) ? window->floating_width : width; + height = (height == 0) ? window->floating_height : height; + + window->configured_width = width; + window->configured_height = height; + + if (!libdecor_configuration_get_window_state(configuration, + &window_state)) + window_state = LIBDECOR_WINDOW_STATE_NONE; + + window->window_state = window_state; + + state = libdecor_state_new(width, height); + libdecor_frame_commit(frame, state, configuration); + libdecor_state_free(state); + + /* store floating dimensions */ + if (libdecor_frame_is_floating(window->frame)) { + window->floating_width = width; + window->floating_height = height; + } + + redraw(window); +} + +static void +handle_close(struct libdecor_frame *frame, + void *user_data) +{ + exit(EXIT_SUCCESS); +} + +static void +handle_commit(struct libdecor_frame *frame, + void *user_data) +{ + wl_surface_commit(window->wl_surface); +} + +static void +handle_dismiss_popup(struct libdecor_frame *frame, + const char *seat_name, + void *user_data) +{ + popup_destroy(window->popup); +} + +static struct libdecor_frame_interface libdecor_frame_iface = { + handle_configure, + handle_close, + handle_commit, + handle_dismiss_popup, +}; + +static void +surface_enter(void *data, + struct wl_surface *wl_surface, + struct wl_output *wl_output) +{ + struct window *window = data; + struct output *output; + struct window_output *window_output; + + if (!own_output(wl_output)) + return; + + output = wl_output_get_user_data(wl_output); + + if (output == NULL) + return; + + window_output = zalloc(sizeof *window_output); + window_output->output = output; + wl_list_insert(&window->outputs, &window_output->link); + update_scale(window); +} + +static void +surface_leave(void *data, + struct wl_surface *wl_surface, + struct wl_output *wl_output) +{ + struct window *window = data; + struct window_output *window_output; + + wl_list_for_each(window_output, &window->outputs, link) { + if (window_output->output->wl_output == wl_output) { + wl_list_remove(&window_output->link); + free(window_output); + update_scale(window); + break; + } + } +} + +static struct wl_surface_listener surface_listener = { + surface_enter, + surface_leave, +}; + +static void +free_outputs() +{ + struct output *output; + struct window_output *window_output, *window_output_tmp; + + wl_list_for_each(output, &outputs, link) { + wl_list_for_each_safe(window_output, window_output_tmp, &window->outputs, link) { + if (window_output->output == output) { + wl_list_remove(&window_output->link); + free(window_output); + } + } + wl_list_remove(&output->link); + wl_output_destroy(output->wl_output); + free(output); + break; + } +} + +int +main(int argc, + char **argv) +{ + struct timeval start; + struct timeval now; + struct wl_display *wl_display; + struct wl_registry *wl_registry; + struct libdecor *context; + struct output *output; + int timeout = -1; + bool timed_quit = false; + + if (argc > 1 && strcmp(argv[1], "--timed-quit") == 0) { + timed_quit = true; + timeout = 500; /* ms */ + } + + /* write all output to stdout immediately */ + setbuf(stdout, NULL); + + wl_display = wl_display_connect(NULL); + if (!wl_display) { + fprintf(stderr, "No Wayland connection\n"); + return EXIT_FAILURE; + } + + wl_list_init(&seats); + wl_list_init(&outputs); + + wl_registry = wl_display_get_registry(wl_display); + wl_registry_add_listener(wl_registry, + ®istry_listener, + NULL); + wl_display_roundtrip(wl_display); + wl_display_roundtrip(wl_display); + if (!has_xrgb) { + fprintf(stderr, "No XRGB shm format\n"); + return EXIT_FAILURE; + } + + window = zalloc(sizeof *window); + window->scale = 1; + window->title_index = 0; + wl_list_for_each(output, &outputs, link) { + window->scale = MAX(window->scale, output->scale); + } + wl_list_init(&window->outputs); + window->wl_surface = wl_compositor_create_surface(wl_compositor); + wl_surface_add_listener(window->wl_surface, &surface_listener, window); + + /* initialise content dimensions */ + window->floating_width = DEFAULT_WIDTH; + window->floating_height = DEFAULT_HEIGHT; + + context = libdecor_new(wl_display, &libdecor_iface); + window->frame = libdecor_decorate(context, window->wl_surface, + &libdecor_frame_iface, window); + libdecor_frame_set_app_id(window->frame, "libdecor-demo"); + libdecor_frame_set_title(window->frame, "libdecor demo"); + libdecor_frame_map(window->frame); + + gettimeofday(&start, NULL); + libdecor_frame_set_min_content_size(window->frame, 15 * chk, 10 * chk); + + while (libdecor_dispatch(context, timeout) >= 0) { + if (timed_quit) { + gettimeofday(&now, NULL); + if (now.tv_sec >= start.tv_sec + 2) { + fprintf(stderr, "Exiting due to --timed-quit\n"); + libdecor_frame_close(window->frame); + } + } + } + + free_outputs(); + + free(window); + + return EXIT_SUCCESS; +} diff --git a/libdecor/demo/egl.c b/libdecor/demo/egl.c new file mode 100644 index 000000000..3d7331645 --- /dev/null +++ b/libdecor/demo/egl.c @@ -0,0 +1,355 @@ +/* + * Copyright © 2011 Benjamin Franzke + * Copyright © 2010 Intel Corporation + * Copyright © 2018 Jonas Ådahl + * + * 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 (including the + * next paragraph) 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. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <math.h> +#include <EGL/egl.h> +#include <wayland-client.h> +#include <wayland-egl.h> +#include <libdecor.h> +#include <GL/gl.h> +#include <utils.h> + +static const size_t default_size = 200; + +struct client { + struct wl_display *display; + struct wl_compositor *compositor; + EGLDisplay egl_display; + EGLContext egl_context; +}; + +struct window { + struct client *client; + struct wl_surface *surface; + struct libdecor_frame *frame; + struct wl_egl_window *egl_window; + EGLSurface egl_surface; + int content_width; + int content_height; + int floating_width; + int floating_height; + bool open; + bool configured; +}; + +static void +frame_configure(struct libdecor_frame *frame, + struct libdecor_configuration *configuration, + void *user_data) +{ + struct window *window = user_data; + struct libdecor_state *state; + int width, height; + + if (!libdecor_configuration_get_content_size(configuration, frame, + &width, &height)) { + width = window->floating_width; + height = window->floating_height; + } + + window->content_width = width; + window->content_height = height; + + wl_egl_window_resize(window->egl_window, + window->content_width, window->content_height, + 0, 0); + + state = libdecor_state_new(width, height); + libdecor_frame_commit(frame, state, configuration); + libdecor_state_free(state); + + /* store floating dimensions */ + if (libdecor_frame_is_floating(window->frame)) { + window->floating_width = width; + window->floating_height = height; + } + + window->configured = true; +} + +static void +frame_close(struct libdecor_frame *frame, + void *user_data) +{ + struct window *window = user_data; + + window->open = false; +} + +static void +frame_commit(struct libdecor_frame *frame, + void *user_data) +{ + struct window *window = user_data; + + eglSwapBuffers(window->client->display, window->egl_surface); +} + +static struct libdecor_frame_interface frame_interface = { + frame_configure, + frame_close, + frame_commit, +}; + +static void +libdecor_error(struct libdecor *context, + enum libdecor_error error, + const char *message) +{ + fprintf(stderr, "Caught error (%d): %s\n", error, message); + exit(EXIT_FAILURE); +} + +static struct libdecor_interface libdecor_interface = { + libdecor_error, +}; + +static void +registry_global(void *data, + struct wl_registry *wl_registry, + uint32_t name, + const char *interface, + uint32_t version) +{ + struct client *client = data; + + if (strcmp(interface, wl_compositor_interface.name) == 0) { + client->compositor = wl_registry_bind(wl_registry, name, + &wl_compositor_interface, 1); + } +} + +static void +registry_global_remove(void *data, + struct wl_registry *wl_registry, + uint32_t name) +{ +} + +static const struct wl_registry_listener registry_listener = { + registry_global, + registry_global_remove +}; + +static bool +setup(struct window *window) +{ + static const EGLint config_attribs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + EGL_NONE + }; + + EGLint major, minor; + EGLint n; + EGLConfig config; + + window->client->egl_display = + eglGetDisplay((EGLNativeDisplayType)window->client->display); + + if (eglInitialize(window->client->egl_display, &major, &minor) == EGL_FALSE) { + fprintf(stderr, "Cannot initialise EGL!\n"); + return false; + } + + if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) { + fprintf(stderr, "Cannot bind EGL API!\n"); + return false; + } + + if (eglChooseConfig(window->client->egl_display, + config_attribs, + &config, 1, &n) == EGL_FALSE) { + fprintf(stderr, "No matching EGL configurations!\n"); + return false; + } + + window->client->egl_context = eglCreateContext(window->client->egl_display, + config, EGL_NO_CONTEXT, NULL); + + if (window->client->egl_context == EGL_NO_CONTEXT) { + fprintf(stderr, "No EGL context!\n"); + return false; + } + + window->surface = wl_compositor_create_surface(window->client->compositor); + + window->egl_window = wl_egl_window_create(window->surface, + default_size, default_size); + + window->egl_surface = eglCreateWindowSurface( + window->client->egl_display, config, + (EGLNativeWindowType)window->egl_window, + NULL); + + eglMakeCurrent(window->client->egl_display, window->egl_surface, + window->egl_surface, window->client->egl_context); + + return true; +} + +static void +cleanup(struct window *window) +{ + if (window->client->egl_display) { + eglMakeCurrent(window->client->egl_display, EGL_NO_SURFACE, + EGL_NO_SURFACE, EGL_NO_CONTEXT); + } + if (window->egl_surface) { + eglDestroySurface(window->client->egl_display, window->egl_surface); + } + if (window->egl_window) { + wl_egl_window_destroy(window->egl_window); + } + if (window->surface) { + wl_surface_destroy(window->surface); + } + if (window->client->egl_context) { + eglDestroyContext(window->client->egl_display, window->client->egl_context); + } + if (window->client->egl_display) { + eglTerminate(window->client->egl_display); + } +} + +static float +hue_to_channel(const float *const hue, const int n) +{ + /* convert hue to rgb channels with saturation and value equal to 1 + * https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB_alternative + */ + const float k = fmod(n + ((*hue) * 3 / M_PI), 6); + return 1 - MAX(0, MIN(MIN(k, 4 - k), 1)); +} + +static void +hue_to_rgb(const float *const hue, float (*rgb)[3]) +{ + (*rgb)[0] = hue_to_channel(hue, 5); + (*rgb)[1] = hue_to_channel(hue, 3); + (*rgb)[2] = hue_to_channel(hue, 1); +} + +static void +draw(struct window *window) +{ + struct timespec tv; + double time; + + /* change of colour hue (HSV space) in rad/sec */ + static const float hue_change = (2 * M_PI) / 10; + float hue; + float rgb[3] = {0,0,0}; + + clock_gettime(CLOCK_REALTIME, &tv); + time = tv.tv_sec + tv.tv_nsec * 1e-9; + + hue = fmod(time * hue_change, 2 * M_PI); + + hue_to_rgb(&hue, &rgb); + + glClearColor(rgb[0], rgb[1], rgb[2], 1); + glClear(GL_COLOR_BUFFER_BIT); + + eglSwapBuffers(window->client->egl_display, window->egl_surface); +} + +int +main(int argc, char *argv[]) +{ + struct wl_registry *wl_registry; + struct libdecor *context = NULL; + struct window *window; + struct client *client; + int ret = EXIT_SUCCESS; + + client = calloc(1, sizeof(struct client)); + + client->display = wl_display_connect(NULL); + if (!client->display) { + fprintf(stderr, "No Wayland connection\n"); + free(client); + return EXIT_FAILURE; + } + + wl_registry = wl_display_get_registry(client->display); + wl_registry_add_listener(wl_registry, ®istry_listener, client); + wl_display_roundtrip(client->display); + + window = calloc(1, sizeof(struct window)); + window->client = client; + window->open = true; + window->configured = false; + window->floating_width = window->floating_height = default_size; + + if (!setup(window)) { + goto out; + } + + context = libdecor_new(client->display, &libdecor_interface); + window->frame = libdecor_decorate(context, window->surface, + &frame_interface, window); + libdecor_frame_set_app_id(window->frame, "egl-demo"); + libdecor_frame_set_title(window->frame, "EGL demo"); + libdecor_frame_map(window->frame); + + wl_display_roundtrip(client->display); + wl_display_roundtrip(client->display); + + /* wait for the first configure event */ + while (!window->configured) { + if (libdecor_dispatch(context, 0) < 0) { + ret = EXIT_FAILURE; + goto out; + } + } + + while (window->open) { + if (libdecor_dispatch(context, 0) < 0) { + ret = EXIT_FAILURE; + goto out; + } + draw(window); + } + +out: + if (context) { + libdecor_unref(context); + } + cleanup(window); + free(window); + free(client); + + return ret; +} |
