mirror of
https://github.com/lbonn/rofi
synced 2024-11-23 12:23:02 +00:00
37b8e9fb99
This was very buggy, we were counting the events twice... * always use pointer frame, it's available since v5 * drop axis handler * use axis_discrete or axis_120 (v >= 8) instead * fail on some protocol minimum version checks * simplify output protocol version check Fixes #120
1791 lines
56 KiB
C
1791 lines
56 KiB
C
/**
|
|
* MIT/X11 License
|
|
* Modified (c) 2017 Quentin “Sardem FF7” Glidic
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
* a copy of this software and associated documentation files (the
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
* the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#define G_LOG_DOMAIN "Wayland"
|
|
#pragma GCC diagnostic ignored "-Wunused-variable"
|
|
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
|
|
|
#include <config.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <linux/input-event-codes.h>
|
|
#include <locale.h>
|
|
#include <math.h>
|
|
#include <stdint.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#include <cairo.h>
|
|
#include <glib.h>
|
|
#include <glib/gstdio.h>
|
|
#include <wayland-client.h>
|
|
#include <wayland-cursor.h>
|
|
#include <xkbcommon/xkbcommon-compose.h>
|
|
#include <xkbcommon/xkbcommon.h>
|
|
|
|
#include <libgwater-wayland.h>
|
|
|
|
#include <nkutils-bindings.h>
|
|
|
|
#include <rofi.h>
|
|
|
|
#include "keyb.h"
|
|
#include "rofi-types.h"
|
|
#include "settings.h"
|
|
#include "view.h"
|
|
|
|
#include "display-internal.h"
|
|
#include "display.h"
|
|
#include "wayland-internal.h"
|
|
|
|
#ifdef HAVE_WAYLAND_CURSOR_SHAPE
|
|
#include "cursor-shape-v1-protocol.h"
|
|
#endif
|
|
#include "primary-selection-unstable-v1-protocol.h"
|
|
#include "wlr-layer-shell-unstable-v1-protocol.h"
|
|
#include "keyboard-shortcuts-inhibit-unstable-v1-protocol.h"
|
|
|
|
#define wayland_output_get_dpi(output, scale, dimension) \
|
|
((output)->current.physical_##dimension > 0 && (scale) > 0 \
|
|
? round((double)(output)->current.dimension * 25.4 / (scale) / \
|
|
(output)->current.physical_##dimension) \
|
|
: 0)
|
|
|
|
typedef struct _display_buffer_pool wayland_buffer_pool;
|
|
typedef struct {
|
|
wayland_stuff *context;
|
|
uint32_t global_name;
|
|
struct wl_output *output;
|
|
gchar *name;
|
|
struct {
|
|
int32_t x;
|
|
int32_t y;
|
|
int32_t width;
|
|
int32_t height;
|
|
int32_t physical_width; /* mm */
|
|
int32_t physical_height; /* mm */
|
|
int32_t scale;
|
|
int32_t transform;
|
|
} current, pending;
|
|
} wayland_output;
|
|
|
|
typedef struct {
|
|
wayland_buffer_pool *pool;
|
|
struct wl_buffer *buffer;
|
|
uint8_t *data;
|
|
gboolean released;
|
|
} wayland_buffer;
|
|
|
|
struct _display_buffer_pool {
|
|
wayland_stuff *context;
|
|
uint8_t *data;
|
|
size_t size;
|
|
int32_t width;
|
|
int32_t height;
|
|
gboolean to_free;
|
|
wayland_buffer *buffers;
|
|
};
|
|
|
|
static gboolean wayland_display_late_setup(void);
|
|
|
|
static wayland_stuff wayland_;
|
|
wayland_stuff *wayland = &wayland_;
|
|
static const cairo_user_data_key_t wayland_cairo_surface_user_data;
|
|
|
|
static void wayland_buffer_cleanup(wayland_buffer_pool *self) {
|
|
if (!self->to_free) {
|
|
return;
|
|
}
|
|
|
|
size_t i, count = 0;
|
|
for (i = 0; i < wayland->buffer_count; ++i) {
|
|
if ((self->buffers[i].released) && (self->buffers[i].buffer != NULL)) {
|
|
wl_buffer_destroy(self->buffers[i].buffer);
|
|
self->buffers[i].buffer = NULL;
|
|
}
|
|
if (self->buffers[i].buffer == NULL) {
|
|
++count;
|
|
}
|
|
}
|
|
|
|
if (count < wayland->buffer_count) {
|
|
return;
|
|
}
|
|
|
|
munmap(self->data, self->size);
|
|
g_free(self);
|
|
}
|
|
|
|
static void wayland_buffer_release(void *data, struct wl_buffer *buffer) {
|
|
wayland_buffer_pool *self = data;
|
|
|
|
size_t i;
|
|
for (i = 0; i < wayland->buffer_count; ++i) {
|
|
if (self->buffers[i].buffer == buffer) {
|
|
self->buffers[i].released = TRUE;
|
|
}
|
|
}
|
|
|
|
wayland_buffer_cleanup(self);
|
|
}
|
|
|
|
static const struct wl_buffer_listener wayland_buffer_listener = {
|
|
wayland_buffer_release};
|
|
|
|
wayland_buffer_pool *display_buffer_pool_new(gint width, gint height) {
|
|
struct wl_shm_pool *wl_pool;
|
|
struct wl_buffer *buffer;
|
|
int fd;
|
|
uint8_t *data;
|
|
width *= wayland->scale;
|
|
height *= wayland->scale;
|
|
int32_t stride;
|
|
size_t size;
|
|
size_t pool_size;
|
|
|
|
stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
|
|
size = stride * height;
|
|
pool_size = size * wayland->buffer_count;
|
|
|
|
gchar filename[PATH_MAX];
|
|
g_snprintf(filename, PATH_MAX, "%s/rofi-wayland-surface",
|
|
g_get_user_runtime_dir());
|
|
fd = g_open(filename, O_CREAT | O_RDWR, 0);
|
|
g_unlink(filename);
|
|
if (fd < 0) {
|
|
g_warning("creating a buffer file for %zu B failed: %s", pool_size,
|
|
g_strerror(errno));
|
|
return NULL;
|
|
}
|
|
if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) {
|
|
g_close(fd, NULL);
|
|
return NULL;
|
|
}
|
|
if (ftruncate(fd, pool_size) < 0) {
|
|
g_close(fd, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
data = mmap(NULL, pool_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
|
if (data == MAP_FAILED) {
|
|
g_warning("mmap of size %zu failed: %s", pool_size, g_strerror(errno));
|
|
close(fd);
|
|
return NULL;
|
|
}
|
|
|
|
wayland_buffer_pool *pool;
|
|
pool = g_new0(wayland_buffer_pool, 1);
|
|
|
|
pool->width = width;
|
|
pool->height = height;
|
|
|
|
pool->buffers = g_new0(wayland_buffer, wayland->buffer_count);
|
|
|
|
wl_pool = wl_shm_create_pool(wayland->shm, fd, pool_size);
|
|
size_t i;
|
|
for (i = 0; i < wayland->buffer_count; ++i) {
|
|
pool->buffers[i].pool = pool;
|
|
pool->buffers[i].buffer = wl_shm_pool_create_buffer(
|
|
wl_pool, size * i, width, height, stride, WL_SHM_FORMAT_ARGB8888);
|
|
pool->buffers[i].data = data + size * i;
|
|
pool->buffers[i].released = TRUE;
|
|
wl_buffer_add_listener(pool->buffers[i].buffer, &wayland_buffer_listener,
|
|
pool);
|
|
}
|
|
wl_shm_pool_destroy(wl_pool);
|
|
close(fd);
|
|
|
|
return pool;
|
|
}
|
|
|
|
void display_buffer_pool_free(wayland_buffer_pool *self) {
|
|
if (self == NULL) {
|
|
return;
|
|
}
|
|
self->to_free = TRUE;
|
|
wayland_buffer_cleanup(self);
|
|
}
|
|
|
|
static void wayland_surface_protocol_enter(void *data,
|
|
struct wl_surface *wl_surface,
|
|
struct wl_output *wl_output) {
|
|
wayland_output *output;
|
|
|
|
output = g_hash_table_lookup(wayland->outputs, wl_output);
|
|
if (output == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (config.dpi == 0 || config.dpi == 1) {
|
|
// DPI auto-detect requested.
|
|
config.dpi = wayland_output_get_dpi(output, output->current.scale, height);
|
|
g_debug("Auto-detected DPI: %d", config.dpi);
|
|
}
|
|
|
|
wl_surface_set_buffer_scale(wl_surface, output->current.scale);
|
|
|
|
if (wayland->scale != output->current.scale) {
|
|
wayland->scale = output->current.scale;
|
|
|
|
// create new buffers with the correct scaled size
|
|
rofi_view_pool_refresh();
|
|
|
|
RofiViewState *state = rofi_view_get_active();
|
|
if (state != NULL) {
|
|
rofi_view_set_size(state, -1, -1);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void wayland_surface_protocol_leave(void *data,
|
|
struct wl_surface *wl_surface,
|
|
struct wl_output *wl_output) {}
|
|
|
|
static const struct wl_surface_listener wayland_surface_interface = {
|
|
.enter = wayland_surface_protocol_enter,
|
|
.leave = wayland_surface_protocol_leave,
|
|
};
|
|
|
|
static void wayland_frame_callback(void *data, struct wl_callback *callback,
|
|
uint32_t time);
|
|
|
|
static const struct wl_callback_listener wayland_frame_wl_callback_listener = {
|
|
.done = wayland_frame_callback,
|
|
};
|
|
|
|
cairo_surface_t *
|
|
display_buffer_pool_get_next_buffer(wayland_buffer_pool *pool) {
|
|
wayland_buffer *buffer = NULL;
|
|
size_t i;
|
|
for (i = 0; (buffer == NULL) && (i < wayland->buffer_count); ++i) {
|
|
buffer = pool->buffers + i;
|
|
if (!buffer->released) {
|
|
buffer = NULL;
|
|
}
|
|
}
|
|
if (buffer == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
cairo_surface_t *surface;
|
|
|
|
surface = cairo_image_surface_create_for_data(
|
|
buffer->data, CAIRO_FORMAT_ARGB32, pool->width, pool->height,
|
|
cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, pool->width));
|
|
cairo_surface_set_user_data(surface, &wayland_cairo_surface_user_data, buffer,
|
|
NULL);
|
|
return surface;
|
|
}
|
|
|
|
void display_surface_commit(cairo_surface_t *surface) {
|
|
if (surface == NULL || wayland->surface == NULL) {
|
|
return;
|
|
}
|
|
|
|
wayland_buffer *buffer =
|
|
cairo_surface_get_user_data(surface, &wayland_cairo_surface_user_data);
|
|
wayland_buffer_pool *pool = buffer->pool;
|
|
|
|
cairo_surface_destroy(surface);
|
|
|
|
wl_surface_damage(wayland->surface, 0, 0, pool->width, pool->height);
|
|
wl_surface_attach(wayland->surface, buffer->buffer, 0, 0);
|
|
// FIXME: hidpi
|
|
wl_surface_set_buffer_scale(wayland->surface, wayland->scale);
|
|
buffer->released = FALSE;
|
|
|
|
wl_surface_commit(wayland->surface);
|
|
}
|
|
|
|
static void wayland_frame_callback(void *data, struct wl_callback *callback,
|
|
uint32_t timestamp) {
|
|
if (wayland->frame_cb != NULL) {
|
|
wl_callback_destroy(wayland->frame_cb);
|
|
wayland->frame_cb = NULL;
|
|
rofi_view_frame_callback();
|
|
}
|
|
if (wayland->surface != NULL) {
|
|
wayland->frame_cb = wl_surface_frame(wayland->surface);
|
|
wl_callback_add_listener(wayland->frame_cb,
|
|
&wayland_frame_wl_callback_listener, wayland);
|
|
}
|
|
}
|
|
|
|
static void wayland_keyboard_keymap(void *data, struct wl_keyboard *keyboard,
|
|
enum wl_keyboard_keymap_format format,
|
|
int32_t fd, uint32_t size) {
|
|
wayland_seat *self = data;
|
|
|
|
if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
|
|
close(fd);
|
|
return;
|
|
}
|
|
|
|
char *str = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
|
|
if (str == MAP_FAILED) {
|
|
close(fd);
|
|
return;
|
|
}
|
|
|
|
struct xkb_keymap *keymap = xkb_keymap_new_from_string(
|
|
nk_bindings_seat_get_context(wayland->bindings_seat), str,
|
|
XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
|
if (keymap == NULL) {
|
|
fprintf(stderr, "Failed to get Keymap for current keyboard device.\n");
|
|
return;
|
|
}
|
|
struct xkb_state *state = xkb_state_new(keymap);
|
|
if (state == NULL) {
|
|
fprintf(stderr,
|
|
"Failed to get state object for current keyboard device.\n");
|
|
return;
|
|
}
|
|
|
|
nk_bindings_seat_update_keymap(wayland->bindings_seat, keymap, state);
|
|
}
|
|
|
|
static void wayland_keyboard_enter(void *data, struct wl_keyboard *keyboard,
|
|
uint32_t serial, struct wl_surface *surface,
|
|
struct wl_array *keys) {
|
|
wayland_seat *self = data;
|
|
|
|
wayland->last_seat = self;
|
|
self->serial = serial;
|
|
|
|
uint32_t *key, *kend;
|
|
for (key = keys->data, kend = key + keys->size / sizeof(*key); key < kend;
|
|
++key) {
|
|
nk_bindings_seat_handle_key(wayland->bindings_seat, NULL, *key + 8,
|
|
NK_BINDINGS_KEY_STATE_PRESSED);
|
|
}
|
|
}
|
|
|
|
static void wayland_keyboard_leave(void *data, struct wl_keyboard *keyboard,
|
|
uint32_t serial,
|
|
struct wl_surface *surface) {
|
|
wayland_seat *self = data;
|
|
// TODO?
|
|
}
|
|
|
|
static gboolean wayland_key_repeat(void *data) {
|
|
wayland_seat *self = data;
|
|
|
|
if (self->repeat.key == 0) {
|
|
self->repeat.source = NULL;
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
char *text = nk_bindings_seat_handle_key(wayland->bindings_seat, NULL,
|
|
self->repeat.key,
|
|
NK_BINDINGS_KEY_STATE_PRESS);
|
|
|
|
RofiViewState *state = rofi_view_get_active();
|
|
if (state == NULL) {
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
if (text != NULL) {
|
|
rofi_view_handle_text(state, text);
|
|
}
|
|
|
|
rofi_view_maybe_update(state);
|
|
|
|
return G_SOURCE_CONTINUE;
|
|
}
|
|
|
|
static gboolean wayland_key_repeat_delay(void *data) {
|
|
wayland_seat *self = data;
|
|
|
|
if (self->repeat.key == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
char *text = nk_bindings_seat_handle_key(wayland->bindings_seat, NULL,
|
|
self->repeat.key,
|
|
NK_BINDINGS_KEY_STATE_PRESS);
|
|
|
|
RofiViewState *state = rofi_view_get_active();
|
|
if (state == NULL) {
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
if (text != NULL) {
|
|
rofi_view_handle_text(state, text);
|
|
}
|
|
|
|
guint source_id = g_timeout_add(self->repeat.rate, wayland_key_repeat, data);
|
|
self->repeat.source = g_main_context_find_source_by_id(NULL, source_id);
|
|
|
|
rofi_view_maybe_update(state);
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void wayland_keyboard_key(void *data, struct wl_keyboard *keyboard,
|
|
uint32_t serial, uint32_t time, uint32_t key,
|
|
enum wl_keyboard_key_state kstate) {
|
|
RofiViewState *state = rofi_view_get_active();
|
|
wayland_seat *self = data;
|
|
|
|
gchar *text;
|
|
|
|
wayland->last_seat = self;
|
|
self->serial = serial;
|
|
|
|
xkb_keycode_t keycode = key + 8;
|
|
if (kstate == WL_KEYBOARD_KEY_STATE_RELEASED) {
|
|
if (keycode == self->repeat.key) {
|
|
self->repeat.key = 0;
|
|
if (self->repeat.source != NULL) {
|
|
g_source_destroy(self->repeat.source);
|
|
self->repeat.source = NULL;
|
|
}
|
|
}
|
|
nk_bindings_seat_handle_key(wayland->bindings_seat, NULL, keycode,
|
|
NK_BINDINGS_KEY_STATE_RELEASE);
|
|
} else if (kstate == WL_KEYBOARD_KEY_STATE_PRESSED) {
|
|
char *text = nk_bindings_seat_handle_key(
|
|
wayland->bindings_seat, NULL, keycode, NK_BINDINGS_KEY_STATE_PRESS);
|
|
|
|
if (self->repeat.source != NULL) {
|
|
g_source_destroy(self->repeat.source);
|
|
self->repeat.source = NULL;
|
|
}
|
|
|
|
if (state != NULL) {
|
|
if (text != NULL) {
|
|
rofi_view_handle_text(state, text);
|
|
}
|
|
self->repeat.key = keycode;
|
|
guint source_id =
|
|
g_timeout_add(self->repeat.delay, wayland_key_repeat_delay, data);
|
|
self->repeat.source = g_main_context_find_source_by_id(NULL, source_id);
|
|
}
|
|
}
|
|
|
|
if (state != NULL) {
|
|
rofi_view_maybe_update(state);
|
|
}
|
|
}
|
|
|
|
static void wayland_keyboard_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) {
|
|
wayland_seat *self = data;
|
|
nk_bindings_seat_update_mask(wayland->bindings_seat, NULL, mods_depressed,
|
|
mods_latched, mods_locked, 0, 0, group);
|
|
|
|
RofiViewState *state = rofi_view_get_active();
|
|
if (state != NULL) {
|
|
rofi_view_maybe_update(state);
|
|
}
|
|
}
|
|
|
|
static void wayland_keyboard_repeat_info(void *data,
|
|
struct wl_keyboard *keyboard,
|
|
int32_t rate, int32_t delay) {
|
|
wayland_seat *self = data;
|
|
self->repeat.key = 0;
|
|
self->repeat.rate = rate;
|
|
self->repeat.delay = delay;
|
|
if (self->repeat.source != NULL) {
|
|
g_source_destroy(self->repeat.source);
|
|
self->repeat.source = NULL;
|
|
}
|
|
}
|
|
|
|
static const struct wl_keyboard_listener wayland_keyboard_listener = {
|
|
.keymap = wayland_keyboard_keymap,
|
|
.enter = wayland_keyboard_enter,
|
|
.leave = wayland_keyboard_leave,
|
|
.key = wayland_keyboard_key,
|
|
.modifiers = wayland_keyboard_modifiers,
|
|
.repeat_info = wayland_keyboard_repeat_info,
|
|
};
|
|
|
|
static gboolean wayland_cursor_reload_theme(guint scale);
|
|
|
|
static void wayland_cursor_set_image(int i) {
|
|
struct wl_buffer *buffer;
|
|
struct wl_cursor_image *image;
|
|
image = wayland->cursor.cursor->images[i];
|
|
|
|
wayland->cursor.image = image;
|
|
buffer = wl_cursor_image_get_buffer(wayland->cursor.image);
|
|
wl_surface_set_buffer_scale(wayland->cursor.surface, wayland->scale);
|
|
wl_surface_attach(wayland->cursor.surface, buffer, 0, 0);
|
|
wl_surface_damage(wayland->cursor.surface, 0, 0, wayland->cursor.image->width,
|
|
wayland->cursor.image->height);
|
|
wl_surface_commit(wayland->cursor.surface);
|
|
}
|
|
|
|
static void wayland_cursor_frame_callback(void *data,
|
|
struct wl_callback *callback,
|
|
uint32_t time);
|
|
|
|
static const struct wl_callback_listener
|
|
wayland_cursor_frame_wl_callback_listener = {
|
|
.done = wayland_cursor_frame_callback,
|
|
};
|
|
|
|
static void wayland_cursor_frame_callback(void *data,
|
|
struct wl_callback *callback,
|
|
uint32_t time) {
|
|
int i;
|
|
|
|
if (wayland->cursor.frame_cb != NULL) {
|
|
wl_callback_destroy(wayland->cursor.frame_cb);
|
|
}
|
|
wayland->cursor.frame_cb = wl_surface_frame(wayland->cursor.surface);
|
|
wl_callback_add_listener(wayland->cursor.frame_cb,
|
|
&wayland_cursor_frame_wl_callback_listener, wayland);
|
|
|
|
i = wl_cursor_frame(wayland->cursor.cursor, time);
|
|
wayland_cursor_set_image(i);
|
|
}
|
|
|
|
static void wayland_pointer_send_events(wayland_seat *self) {
|
|
RofiViewState *state = rofi_view_get_active();
|
|
|
|
if (state == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (self->motion.x > -1 || self->motion.y > -1) {
|
|
rofi_view_handle_mouse_motion(state, self->motion.x, self->motion.y,
|
|
config.hover_select);
|
|
self->motion.x = -1;
|
|
self->motion.y = -1;
|
|
}
|
|
|
|
NkBindingsMouseButton button = -1;
|
|
switch (self->button.button) {
|
|
case BTN_LEFT:
|
|
button = NK_BINDINGS_MOUSE_BUTTON_PRIMARY;
|
|
break;
|
|
case BTN_RIGHT:
|
|
button = NK_BINDINGS_MOUSE_BUTTON_SECONDARY;
|
|
break;
|
|
case BTN_MIDDLE:
|
|
button = NK_BINDINGS_MOUSE_BUTTON_MIDDLE;
|
|
break;
|
|
}
|
|
|
|
if (self->button.button > 0) {
|
|
if (self->button.pressed) {
|
|
rofi_view_handle_mouse_motion(state, self->button.x, self->button.y,
|
|
FALSE);
|
|
nk_bindings_seat_handle_button(wayland->bindings_seat, NULL, button,
|
|
NK_BINDINGS_BUTTON_STATE_PRESS,
|
|
self->button.time);
|
|
} else {
|
|
nk_bindings_seat_handle_button(wayland->bindings_seat, NULL, button,
|
|
NK_BINDINGS_BUTTON_STATE_RELEASE,
|
|
self->button.time);
|
|
}
|
|
self->button.button = 0;
|
|
}
|
|
|
|
if (abs(self->wheel.vertical) >= 120) {
|
|
gint v120 = self->wheel.vertical;
|
|
nk_bindings_seat_handle_scroll(wayland->bindings_seat, NULL,
|
|
NK_BINDINGS_SCROLL_AXIS_VERTICAL,
|
|
v120 / 120);
|
|
if (v120 > 0) {
|
|
self->wheel.vertical = v120 % 120;
|
|
} else {
|
|
self->wheel.vertical = -((-v120) % 120);
|
|
}
|
|
}
|
|
|
|
if (abs(self->wheel.horizontal) >= 120) {
|
|
gint v120 = self->wheel.horizontal;
|
|
nk_bindings_seat_handle_scroll(wayland->bindings_seat, NULL,
|
|
NK_BINDINGS_SCROLL_AXIS_HORIZONTAL,
|
|
v120 / 120);
|
|
if (v120 > 0) {
|
|
self->wheel.horizontal = v120 % 120;
|
|
} else {
|
|
self->wheel.horizontal = -((-v120) % 120);
|
|
}
|
|
}
|
|
|
|
rofi_view_maybe_update(state);
|
|
}
|
|
|
|
static struct wl_cursor *
|
|
rofi_cursor_type_to_wl_cursor(struct wl_cursor_theme *theme,
|
|
RofiCursorType type) {
|
|
static const char *const default_names[] = {
|
|
"default", "left_ptr", "top_left_arrow", "left-arrow", NULL};
|
|
static const char *const pointer_names[] = {"pointer", "hand1", NULL};
|
|
static const char *const text_names[] = {"text", "xterm", NULL};
|
|
|
|
const char *const *name;
|
|
struct wl_cursor *cursor = NULL;
|
|
|
|
switch (type) {
|
|
case ROFI_CURSOR_POINTER:
|
|
name = pointer_names;
|
|
break;
|
|
case ROFI_CURSOR_TEXT:
|
|
name = text_names;
|
|
break;
|
|
default:
|
|
name = default_names;
|
|
break;
|
|
}
|
|
for (; cursor == NULL && *name != NULL; ++name) {
|
|
cursor = wl_cursor_theme_get_cursor(theme, *name);
|
|
}
|
|
return cursor;
|
|
}
|
|
|
|
#ifdef HAVE_WAYLAND_CURSOR_SHAPE
|
|
static enum wp_cursor_shape_device_v1_shape
|
|
rofi_cursor_type_to_wp_cursor_shape(RofiCursorType type) {
|
|
switch (type) {
|
|
case ROFI_CURSOR_POINTER:
|
|
return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_POINTER;
|
|
case ROFI_CURSOR_TEXT:
|
|
return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_TEXT;
|
|
default:
|
|
return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void wayland_cursor_update_for_seat(wayland_seat *seat) {
|
|
#ifdef HAVE_WAYLAND_CURSOR_SHAPE
|
|
if (seat->cursor_shape_device != NULL) {
|
|
wp_cursor_shape_device_v1_set_shape(
|
|
seat->cursor_shape_device, seat->pointer_serial,
|
|
rofi_cursor_type_to_wp_cursor_shape(wayland->cursor.type));
|
|
return;
|
|
} else if (wayland->cursor.theme == NULL) {
|
|
// cursor-shape-v1 is available, but the seat haven't seen a pointer yet
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (wayland->cursor.surface == NULL) {
|
|
wayland->cursor.surface = wl_compositor_create_surface(wayland->compositor);
|
|
}
|
|
|
|
if (wayland->cursor.cursor->image_count < 2) {
|
|
wayland_cursor_set_image(0);
|
|
} else {
|
|
wayland_cursor_frame_callback(wayland, wayland->cursor.frame_cb, 0);
|
|
}
|
|
|
|
wl_pointer_set_cursor(
|
|
seat->pointer, seat->pointer_serial, wayland->cursor.surface,
|
|
wayland->cursor.image->hotspot_x / wayland->cursor.scale,
|
|
wayland->cursor.image->hotspot_y / wayland->cursor.scale);
|
|
}
|
|
|
|
static void wayland_pointer_enter(void *data, struct wl_pointer *pointer,
|
|
uint32_t serial, struct wl_surface *surface,
|
|
wl_fixed_t x, wl_fixed_t y) {
|
|
wayland_seat *self = data;
|
|
|
|
self->pointer_serial = serial;
|
|
|
|
#ifdef HAVE_WAYLAND_CURSOR_SHAPE
|
|
if (wayland->cursor_shape_manager != NULL) {
|
|
if (self->cursor_shape_device == NULL) {
|
|
self->cursor_shape_device = wp_cursor_shape_manager_v1_get_pointer(
|
|
wayland->cursor_shape_manager, pointer);
|
|
}
|
|
} else
|
|
#endif
|
|
if (!wayland_cursor_reload_theme(wayland->scale)) {
|
|
return;
|
|
}
|
|
|
|
wayland_cursor_update_for_seat(self);
|
|
}
|
|
|
|
void wayland_display_set_cursor_type(RofiCursorType type) {
|
|
wayland_seat *seat;
|
|
GHashTableIter iter;
|
|
struct wl_cursor *cursor;
|
|
|
|
if (wayland->cursor.type == type) {
|
|
return;
|
|
}
|
|
wayland->cursor.type = type;
|
|
|
|
#ifdef HAVE_WAYLAND_CURSOR_SHAPE
|
|
if (wayland->cursor_shape_manager == NULL)
|
|
#endif
|
|
{
|
|
if (wayland->cursor.theme == NULL) {
|
|
return;
|
|
}
|
|
|
|
cursor = rofi_cursor_type_to_wl_cursor(wayland->cursor.theme, type);
|
|
if (cursor == NULL) {
|
|
g_info("Failed to load cursor type %d", type);
|
|
return;
|
|
}
|
|
wayland->cursor.cursor = cursor;
|
|
}
|
|
|
|
g_hash_table_iter_init(&iter, wayland->seats);
|
|
while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&seat)) {
|
|
if (seat->pointer != NULL) {
|
|
wayland_cursor_update_for_seat(seat);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void wayland_pointer_leave(void *data, struct wl_pointer *pointer,
|
|
uint32_t serial, struct wl_surface *surface) {
|
|
wayland_seat *self = data;
|
|
|
|
if (wayland->cursor.frame_cb != NULL) {
|
|
wl_callback_destroy(wayland->cursor.frame_cb);
|
|
wayland->cursor.frame_cb = NULL;
|
|
}
|
|
}
|
|
|
|
static void wayland_pointer_motion(void *data, struct wl_pointer *pointer,
|
|
uint32_t time, wl_fixed_t x, wl_fixed_t y) {
|
|
wayland_seat *self = data;
|
|
|
|
self->button.x = wl_fixed_to_int(x);
|
|
self->button.y = wl_fixed_to_int(y);
|
|
self->motion.x = wl_fixed_to_int(x);
|
|
self->motion.y = wl_fixed_to_int(y);
|
|
self->motion.time = time;
|
|
}
|
|
|
|
static void wayland_pointer_button(void *data, struct wl_pointer *pointer,
|
|
uint32_t serial, uint32_t time,
|
|
uint32_t button,
|
|
enum wl_pointer_button_state state) {
|
|
wayland_seat *self = data;
|
|
|
|
wayland->last_seat = self;
|
|
self->serial = serial;
|
|
|
|
self->button.time = time;
|
|
self->button.pressed = (state == WL_POINTER_BUTTON_STATE_PRESSED);
|
|
self->button.button = button;
|
|
}
|
|
|
|
static void wayland_pointer_axis(void *data, struct wl_pointer *pointer,
|
|
uint32_t time, enum wl_pointer_axis axis,
|
|
wl_fixed_t value) {}
|
|
|
|
static void wayland_pointer_frame(void *data, struct wl_pointer *pointer) {
|
|
wayland_seat *self = data;
|
|
wayland_pointer_send_events(self);
|
|
}
|
|
|
|
static void
|
|
wayland_pointer_axis_source(void *data, struct wl_pointer *pointer,
|
|
enum wl_pointer_axis_source axis_source) {}
|
|
|
|
static void wayland_pointer_axis_stop(void *data, struct wl_pointer *pointer,
|
|
uint32_t time,
|
|
enum wl_pointer_axis axis) {}
|
|
|
|
static void wayland_pointer_axis_discrete(void *data,
|
|
struct wl_pointer *pointer,
|
|
enum wl_pointer_axis axis,
|
|
int32_t discrete) {
|
|
wayland_seat *self = data;
|
|
|
|
// values are multiplied by 120 for compatibility with the
|
|
// new high-resolution events
|
|
switch (axis) {
|
|
case WL_POINTER_AXIS_VERTICAL_SCROLL:
|
|
self->wheel.vertical += discrete * 120;
|
|
break;
|
|
case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
|
|
self->wheel.horizontal += discrete * 120;
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef WL_POINTER_AXIS_VALUE120_SINCE_VERSION
|
|
static void wayland_pointer_axis120(void *data,
|
|
struct wl_pointer *wl_pointer,
|
|
enum wl_pointer_axis axis,
|
|
int32_t value120)
|
|
{
|
|
wayland_seat *self = data;
|
|
|
|
switch (axis) {
|
|
case WL_POINTER_AXIS_VERTICAL_SCROLL:
|
|
self->wheel.vertical += value120;
|
|
break;
|
|
case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
|
|
self->wheel.horizontal += value120;
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static const struct wl_pointer_listener wayland_pointer_listener = {
|
|
.enter = wayland_pointer_enter,
|
|
.leave = wayland_pointer_leave,
|
|
.motion = wayland_pointer_motion,
|
|
.button = wayland_pointer_button,
|
|
.axis = wayland_pointer_axis,
|
|
.frame = wayland_pointer_frame,
|
|
.axis_source = wayland_pointer_axis_source,
|
|
.axis_stop = wayland_pointer_axis_stop,
|
|
#ifdef WL_POINTER_AXIS_VALUE120_SINCE_VERSION
|
|
.axis_value120 = wayland_pointer_axis120,
|
|
#endif
|
|
.axis_discrete = wayland_pointer_axis_discrete,
|
|
};
|
|
|
|
static void wayland_keyboard_release(wayland_seat *self) {
|
|
if (self->keyboard == NULL) {
|
|
return;
|
|
}
|
|
|
|
wl_keyboard_release(self->keyboard);
|
|
|
|
self->repeat.key = 0;
|
|
if (self->repeat.source != NULL) {
|
|
g_source_destroy(self->repeat.source);
|
|
self->repeat.source = NULL;
|
|
}
|
|
|
|
self->keyboard = NULL;
|
|
}
|
|
|
|
#define CLIPBOARD_READ_INCREMENT 1024
|
|
|
|
typedef void (*clipboard_read_callback)(char *data);
|
|
|
|
struct clipboard_read_info {
|
|
char *buffer;
|
|
size_t size;
|
|
int fd;
|
|
clipboard_read_callback callback;
|
|
};
|
|
|
|
static gboolean clipboard_read_glib_callback(GIOChannel *channel,
|
|
GIOCondition condition,
|
|
gpointer opaque) {
|
|
struct clipboard_read_info *info = opaque;
|
|
gsize read;
|
|
|
|
switch (g_io_channel_read_chars(channel, info->buffer + info->size,
|
|
CLIPBOARD_READ_INCREMENT, &read, NULL)) {
|
|
case G_IO_STATUS_AGAIN:
|
|
return TRUE;
|
|
|
|
case G_IO_STATUS_NORMAL: {
|
|
info->size += read;
|
|
info->buffer =
|
|
g_realloc(info->buffer, info->size + CLIPBOARD_READ_INCREMENT);
|
|
if (!info->buffer) {
|
|
g_io_channel_shutdown(channel, FALSE, NULL);
|
|
g_io_channel_unref(channel);
|
|
close(info->fd);
|
|
g_free(info);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
default:
|
|
info->buffer[info->size] = '\0';
|
|
info->callback(info->buffer);
|
|
g_io_channel_shutdown(channel, FALSE, NULL);
|
|
g_io_channel_unref(channel);
|
|
close(info->fd);
|
|
g_free(info);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static gboolean clipboard_read_data(int fd, clipboard_read_callback callback) {
|
|
GIOChannel *channel = g_io_channel_unix_new(fd);
|
|
|
|
struct clipboard_read_info *info = g_malloc(sizeof *info);
|
|
if (info == NULL) {
|
|
g_io_channel_unref(channel);
|
|
close(fd);
|
|
return FALSE;
|
|
}
|
|
|
|
info->fd = fd;
|
|
info->size = 0;
|
|
info->callback = callback;
|
|
info->buffer = g_malloc(CLIPBOARD_READ_INCREMENT);
|
|
|
|
if (info->buffer == NULL) {
|
|
g_io_channel_unref(channel);
|
|
close(info->fd);
|
|
g_free(info);
|
|
return FALSE;
|
|
}
|
|
|
|
g_io_add_watch(channel, G_IO_IN | G_IO_HUP | G_IO_ERR,
|
|
clipboard_read_glib_callback, info);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void data_offer_handle_offer(void *data, struct wl_data_offer *offer,
|
|
const char *mime_type) {}
|
|
|
|
static void data_offer_handle_source_actions(
|
|
void *data, struct wl_data_offer *wl_data_offer, uint32_t source_actions) {}
|
|
|
|
static void data_offer_handle_action(void *data,
|
|
struct wl_data_offer *wl_data_offer,
|
|
uint32_t dnd_action) {}
|
|
|
|
static const struct wl_data_offer_listener data_offer_listener = {
|
|
.offer = data_offer_handle_offer,
|
|
.source_actions = data_offer_handle_source_actions,
|
|
.action = data_offer_handle_action,
|
|
};
|
|
|
|
static void data_device_handle_data_offer(void *data,
|
|
struct wl_data_device *data_device,
|
|
struct wl_data_offer *offer) {
|
|
wl_data_offer_add_listener(offer, &data_offer_listener, NULL);
|
|
}
|
|
|
|
static void clipboard_default_callback(char *data) {
|
|
if (wayland->clipboard_default_data != NULL) {
|
|
g_free(wayland->clipboard_default_data);
|
|
}
|
|
wayland->clipboard_default_data = data;
|
|
}
|
|
|
|
static void data_device_handle_selection(void *data,
|
|
struct wl_data_device *data_device,
|
|
struct wl_data_offer *offer) {
|
|
if (offer == NULL) {
|
|
// clipboard is empty
|
|
return;
|
|
}
|
|
|
|
int fds[2];
|
|
if (pipe(fds) < 0) {
|
|
return;
|
|
}
|
|
wl_data_offer_receive(offer, "text/plain", fds[1]);
|
|
close(fds[1]);
|
|
|
|
wl_display_roundtrip(wayland->display);
|
|
|
|
clipboard_read_data(fds[0], clipboard_default_callback);
|
|
wl_data_offer_destroy(offer);
|
|
}
|
|
|
|
static const struct wl_data_device_listener data_device_listener = {
|
|
.data_offer = data_device_handle_data_offer,
|
|
.selection = data_device_handle_selection,
|
|
};
|
|
|
|
static void
|
|
primary_selection_handle_offer(void *data,
|
|
struct zwp_primary_selection_offer_v1 *offer,
|
|
const char *mime_type) {}
|
|
|
|
static const struct zwp_primary_selection_offer_v1_listener
|
|
primary_selection_offer_listener = {
|
|
.offer = primary_selection_handle_offer,
|
|
};
|
|
|
|
static void primary_selection_device_handle_data_offer(
|
|
void *data, struct zwp_primary_selection_device_v1 *data_device,
|
|
struct zwp_primary_selection_offer_v1 *offer) {
|
|
zwp_primary_selection_offer_v1_add_listener(
|
|
offer, &primary_selection_offer_listener, NULL);
|
|
}
|
|
|
|
static void clipboard_primary_callback(char *data) {
|
|
if (wayland->clipboard_primary_data != NULL) {
|
|
g_free(wayland->clipboard_primary_data);
|
|
}
|
|
wayland->clipboard_primary_data = data;
|
|
}
|
|
|
|
static void primary_selection_device_handle_selection(
|
|
void *data, struct zwp_primary_selection_device_v1 *data_device,
|
|
struct zwp_primary_selection_offer_v1 *offer) {
|
|
if (offer == NULL) {
|
|
// clipboard is empty
|
|
return;
|
|
}
|
|
|
|
int fds[2];
|
|
if (pipe(fds) < 0) {
|
|
return;
|
|
}
|
|
zwp_primary_selection_offer_v1_receive(offer, "text/plain", fds[1]);
|
|
close(fds[1]);
|
|
|
|
wl_display_roundtrip(wayland->display);
|
|
|
|
clipboard_read_data(fds[0], clipboard_primary_callback);
|
|
zwp_primary_selection_offer_v1_destroy(offer);
|
|
}
|
|
|
|
static const struct zwp_primary_selection_device_v1_listener
|
|
primary_selection_device_listener = {
|
|
.data_offer = primary_selection_device_handle_data_offer,
|
|
.selection = primary_selection_device_handle_selection,
|
|
};
|
|
|
|
static void wayland_pointer_release(wayland_seat *self) {
|
|
if (self->pointer == NULL) {
|
|
return;
|
|
}
|
|
|
|
#ifdef HAVE_WAYLAND_CURSOR_SHAPE
|
|
if (self->cursor_shape_device != NULL) {
|
|
wp_cursor_shape_device_v1_destroy(self->cursor_shape_device);
|
|
self->cursor_shape_device = NULL;
|
|
}
|
|
#endif
|
|
|
|
wl_pointer_release(self->pointer);
|
|
|
|
self->pointer = NULL;
|
|
}
|
|
|
|
static void wayland_seat_release(wayland_seat *self) {
|
|
wayland_keyboard_release(self);
|
|
wayland_pointer_release(self);
|
|
|
|
wl_seat_release(self->seat);
|
|
|
|
g_hash_table_remove(wayland->seats, self->seat);
|
|
|
|
g_free(self);
|
|
}
|
|
|
|
static void wayland_seat_capabilities(void *data, struct wl_seat *seat,
|
|
uint32_t capabilities) {
|
|
wayland_seat *self = data;
|
|
|
|
if ((capabilities & WL_SEAT_CAPABILITY_KEYBOARD) &&
|
|
(self->keyboard == NULL)) {
|
|
self->keyboard = wl_seat_get_keyboard(self->seat);
|
|
wl_keyboard_add_listener(self->keyboard, &wayland_keyboard_listener, self);
|
|
} else if ((!(capabilities & WL_SEAT_CAPABILITY_POINTER)) &&
|
|
(self->keyboard != NULL)) {
|
|
wayland_keyboard_release(self);
|
|
}
|
|
|
|
if ((capabilities & WL_SEAT_CAPABILITY_POINTER) && (self->pointer == NULL)) {
|
|
self->pointer = wl_seat_get_pointer(self->seat);
|
|
wl_pointer_add_listener(self->pointer, &wayland_pointer_listener, self);
|
|
} else if ((!(capabilities & WL_SEAT_CAPABILITY_POINTER)) &&
|
|
(self->pointer != NULL)) {
|
|
wayland_pointer_release(self);
|
|
}
|
|
|
|
if (wayland->data_device_manager != NULL) {
|
|
self->data_device = wl_data_device_manager_get_data_device(
|
|
wayland->data_device_manager, seat);
|
|
wl_data_device_add_listener(self->data_device, &data_device_listener, NULL);
|
|
}
|
|
|
|
if (wayland->primary_selection_device_manager != NULL) {
|
|
self->primary_selection_device =
|
|
zwp_primary_selection_device_manager_v1_get_device(
|
|
wayland->primary_selection_device_manager, seat);
|
|
zwp_primary_selection_device_v1_add_listener(
|
|
self->primary_selection_device, &primary_selection_device_listener,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
static void wayland_seat_name(void *data, struct wl_seat *seat,
|
|
const char *name) {
|
|
wayland_seat *self = data;
|
|
|
|
if (self->name != NULL) {
|
|
g_hash_table_remove(wayland->seats_by_name, self->name);
|
|
}
|
|
self->name = g_strdup(name);
|
|
g_hash_table_insert(wayland->seats_by_name, self->name, self);
|
|
}
|
|
|
|
static const struct wl_seat_listener wayland_seat_listener = {
|
|
.capabilities = wayland_seat_capabilities,
|
|
.name = wayland_seat_name,
|
|
};
|
|
|
|
static void wayland_output_release(wayland_output *self) {
|
|
g_debug("Output release: %s", self->name);
|
|
|
|
if (wl_output_get_version(self->output) >= WL_OUTPUT_RELEASE_SINCE_VERSION) {
|
|
wl_output_release(self->output);
|
|
} else {
|
|
wl_output_destroy(self->output);
|
|
}
|
|
|
|
g_hash_table_remove(wayland->outputs, self->output);
|
|
|
|
g_free(self->name);
|
|
g_free(self);
|
|
}
|
|
|
|
static wayland_output *wayland_output_by_name(const char *name) {
|
|
#ifdef WL_OUTPUT_NAME_SINCE_VERSION
|
|
GHashTableIter iter;
|
|
wayland_output *output;
|
|
|
|
g_debug("Monitor lookup by name : %s", name);
|
|
|
|
g_hash_table_iter_init(&iter, wayland->outputs);
|
|
while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&output)) {
|
|
if (g_strcmp0(output->name, name) == 0) {
|
|
return output;
|
|
}
|
|
}
|
|
#endif
|
|
g_debug("Monitor lookup by name failed: %s", name);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void wayland_output_done(void *data, struct wl_output *output) {
|
|
wayland_output *self = data;
|
|
|
|
self->current = self->pending;
|
|
|
|
g_debug("Output %s: %" PRIi32 "x%" PRIi32 " (%" PRIi32 "x%" PRIi32 "mm)"
|
|
" position %" PRIi32 "x%" PRIi32 " scale %" PRIi32
|
|
" transform %" PRIi32,
|
|
self->name ? self->name : "Unknown", self->current.width,
|
|
self->current.height, self->current.physical_width,
|
|
self->current.physical_height, self->current.x, self->current.y,
|
|
self->current.scale, self->current.transform);
|
|
}
|
|
|
|
static void wayland_output_geometry(void *data, struct wl_output *output,
|
|
int32_t x, int32_t y, int32_t width,
|
|
int32_t height, int32_t subpixel,
|
|
const char *make, const char *model,
|
|
int32_t transform) {
|
|
wayland_output *self = data;
|
|
|
|
self->pending.x = x;
|
|
self->pending.y = y;
|
|
self->pending.physical_width = width;
|
|
self->pending.physical_height = height;
|
|
self->pending.transform = transform;
|
|
}
|
|
|
|
static void wayland_output_mode(void *data, struct wl_output *output,
|
|
enum wl_output_mode flags, int32_t width,
|
|
int32_t height, int32_t refresh) {
|
|
wayland_output *self = data;
|
|
|
|
if (flags & WL_OUTPUT_MODE_CURRENT) {
|
|
self->pending.width = width;
|
|
self->pending.height = height;
|
|
}
|
|
}
|
|
|
|
static void wayland_output_scale(void *data, struct wl_output *output,
|
|
int32_t scale) {
|
|
wayland_output *self = data;
|
|
|
|
self->pending.scale = scale;
|
|
}
|
|
|
|
#ifdef WL_OUTPUT_NAME_SINCE_VERSION
|
|
static void wayland_output_name(void *data, struct wl_output *output,
|
|
const char *name) {
|
|
wayland_output *self = data;
|
|
|
|
g_free(self->name);
|
|
self->name = g_strdup(name);
|
|
}
|
|
#endif
|
|
|
|
#ifdef WL_OUTPUT_DESCRIPTION_SINCE_VERSION
|
|
static void wayland_output_description(void *data, struct wl_output *output,
|
|
const char *name) {}
|
|
#endif
|
|
|
|
static const struct wl_output_listener wayland_output_listener = {
|
|
.geometry = wayland_output_geometry,
|
|
.mode = wayland_output_mode,
|
|
.scale = wayland_output_scale,
|
|
.done = wayland_output_done,
|
|
#ifdef WL_OUTPUT_NAME_SINCE_VERSION
|
|
.name = wayland_output_name,
|
|
#endif
|
|
#ifdef WL_OUTPUT_DESCRIPTION_SINCE_VERSION
|
|
.description = wayland_output_description,
|
|
#endif
|
|
};
|
|
|
|
static void wayland_registry_handle_global(void *data,
|
|
struct wl_registry *registry,
|
|
uint32_t name, const char *interface,
|
|
uint32_t version) {
|
|
g_debug("wayland registry: interface %s", interface);
|
|
|
|
if (g_strcmp0(interface, wl_compositor_interface.name) == 0) {
|
|
wayland->global_names[WAYLAND_GLOBAL_COMPOSITOR] = name;
|
|
wayland->compositor =
|
|
wl_registry_bind(registry, name, &wl_compositor_interface,
|
|
MIN(version, WL_COMPOSITOR_INTERFACE_VERSION));
|
|
} else if (g_strcmp0(interface, zwlr_layer_shell_v1_interface.name) == 0) {
|
|
wayland->global_names[WAYLAND_GLOBAL_LAYER_SHELL] = name;
|
|
wayland->layer_shell =
|
|
wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface,
|
|
MIN(version, WL_LAYER_SHELL_INTERFACE_VERSION));
|
|
} else if (g_strcmp0(interface, zwp_keyboard_shortcuts_inhibit_manager_v1_interface.name) == 0) {
|
|
wayland->global_names[WAYLAND_GLOBAL_KEYBOARD_SHORTCUTS_INHIBITOR] = name;
|
|
wayland->kb_shortcuts_inhibit_manager =
|
|
wl_registry_bind(registry, name, &zwp_keyboard_shortcuts_inhibit_manager_v1_interface,
|
|
MIN(version, WL_KEYBOARD_SHORTCUTS_INHIBITOR_INTERFACE_VERSION));
|
|
} else if (g_strcmp0(interface, wl_shm_interface.name) == 0) {
|
|
wayland->global_names[WAYLAND_GLOBAL_SHM] = name;
|
|
wayland->shm = wl_registry_bind(registry, name, &wl_shm_interface,
|
|
MIN(version, WL_SHM_INTERFACE_VERSION));
|
|
} else if (g_strcmp0(interface, wl_seat_interface.name) == 0) {
|
|
if (version < WL_SEAT_INTERFACE_MIN_VERSION) {
|
|
g_error("Minimum version of wayland seat interface is %u, got %u", WL_SEAT_INTERFACE_MIN_VERSION,
|
|
version);
|
|
return;
|
|
}
|
|
version = MIN(version, WL_SEAT_INTERFACE_MAX_VERSION);
|
|
|
|
wayland_seat *seat = g_new0(wayland_seat, 1);
|
|
seat->context = wayland;
|
|
seat->global_name = name;
|
|
seat->seat = wl_registry_bind(registry, name, &wl_seat_interface, version);
|
|
g_hash_table_insert(wayland->seats, seat->seat, seat);
|
|
|
|
wl_seat_add_listener(seat->seat, &wayland_seat_listener, seat);
|
|
} else if (g_strcmp0(interface, wl_output_interface.name) == 0) {
|
|
if (version < WL_OUTPUT_INTERFACE_MIN_VERSION) {
|
|
g_error("Minimum version of wayland output interface is %u, got %u", WL_OUTPUT_INTERFACE_MIN_VERSION,
|
|
version);
|
|
return;
|
|
}
|
|
version = MIN(version, WL_OUTPUT_INTERFACE_MAX_VERSION);
|
|
|
|
wayland_output *output = g_new0(wayland_output, 1);
|
|
output->context = wayland;
|
|
output->global_name = name;
|
|
output->output =
|
|
wl_registry_bind(registry, name, &wl_output_interface, version);
|
|
output->pending.scale = 1;
|
|
output->current = output->pending;
|
|
|
|
g_hash_table_insert(wayland->outputs, output->output, output);
|
|
|
|
wl_output_add_listener(output->output, &wayland_output_listener, output);
|
|
} else if (strcmp(interface, wl_data_device_manager_interface.name) == 0) {
|
|
wayland->data_device_manager =
|
|
wl_registry_bind(registry, name, &wl_data_device_manager_interface, 3);
|
|
} else if (strcmp(interface,
|
|
zwp_primary_selection_device_manager_v1_interface.name) ==
|
|
0) {
|
|
wayland->primary_selection_device_manager = wl_registry_bind(
|
|
registry, name, &zwp_primary_selection_device_manager_v1_interface, 1);
|
|
}
|
|
#ifdef HAVE_WAYLAND_CURSOR_SHAPE
|
|
else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) {
|
|
wayland->global_names[WAYLAND_GLOBAL_CURSOR_SHAPE] = name;
|
|
wayland->cursor_shape_manager = wl_registry_bind(
|
|
registry, name, &wp_cursor_shape_manager_v1_interface, 1);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void wayland_registry_handle_global_remove(void *data,
|
|
struct wl_registry *registry,
|
|
uint32_t name) {
|
|
wayland_global_name i;
|
|
for (i = 0; i < _WAYLAND_GLOBAL_SIZE; ++i) {
|
|
if (wayland->global_names[i] != name) {
|
|
continue;
|
|
}
|
|
wayland->global_names[i] = 0;
|
|
|
|
switch (i) {
|
|
case WAYLAND_GLOBAL_COMPOSITOR:
|
|
wl_compositor_destroy(wayland->compositor);
|
|
wayland->compositor = NULL;
|
|
break;
|
|
case WAYLAND_GLOBAL_CURSOR_SHAPE:
|
|
#ifdef HAVE_WAYLAND_CURSOR_SHAPE
|
|
wp_cursor_shape_manager_v1_destroy(wayland->cursor_shape_manager);
|
|
wayland->cursor_shape_manager = NULL;
|
|
#endif
|
|
break;
|
|
case WAYLAND_GLOBAL_LAYER_SHELL:
|
|
zwlr_layer_shell_v1_destroy(wayland->layer_shell);
|
|
wayland->layer_shell = NULL;
|
|
break;
|
|
case WAYLAND_GLOBAL_KEYBOARD_SHORTCUTS_INHIBITOR:
|
|
zwp_keyboard_shortcuts_inhibit_manager_v1_destroy(wayland->kb_shortcuts_inhibit_manager);
|
|
wayland->kb_shortcuts_inhibit_manager = NULL;
|
|
break;
|
|
case WAYLAND_GLOBAL_SHM:
|
|
wl_shm_destroy(wayland->shm);
|
|
wayland->shm = NULL;
|
|
break;
|
|
case _WAYLAND_GLOBAL_SIZE:
|
|
g_assert_not_reached();
|
|
}
|
|
return;
|
|
}
|
|
if ((wayland->cursor.theme != NULL) &&
|
|
((wayland->compositor == NULL) || (wayland->shm == NULL))) {
|
|
if (wayland->cursor.frame_cb != NULL) {
|
|
wl_callback_destroy(wayland->cursor.frame_cb);
|
|
wayland->cursor.frame_cb = NULL;
|
|
}
|
|
|
|
wl_surface_destroy(wayland->cursor.surface);
|
|
wl_cursor_theme_destroy(wayland->cursor.theme);
|
|
wayland->cursor.surface = NULL;
|
|
wayland->cursor.image = NULL;
|
|
wayland->cursor.cursor = NULL;
|
|
wayland->cursor.theme = NULL;
|
|
wayland->cursor.scale = 0;
|
|
}
|
|
|
|
GHashTableIter iter;
|
|
|
|
wayland_seat *seat;
|
|
g_hash_table_iter_init(&iter, wayland->seats);
|
|
while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&seat)) {
|
|
if (seat->global_name != name) {
|
|
continue;
|
|
}
|
|
|
|
g_hash_table_iter_remove(&iter);
|
|
wayland_seat_release(seat);
|
|
return;
|
|
}
|
|
|
|
wayland_output *output;
|
|
g_hash_table_iter_init(&iter, wayland->outputs);
|
|
while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&output)) {
|
|
if (output->global_name != name) {
|
|
continue;
|
|
}
|
|
|
|
g_hash_table_iter_remove(&iter);
|
|
wayland_output_release(output);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static const struct wl_registry_listener wayland_registry_listener = {
|
|
.global = wayland_registry_handle_global,
|
|
.global_remove = wayland_registry_handle_global_remove,
|
|
};
|
|
|
|
static void wayland_layer_shell_surface_configure(
|
|
void *data, struct zwlr_layer_surface_v1 *surface, uint32_t serial,
|
|
uint32_t width, uint32_t height) {
|
|
wayland->layer_width = width;
|
|
wayland->layer_height = height;
|
|
zwlr_layer_surface_v1_ack_configure(surface, serial);
|
|
}
|
|
|
|
static void wayland_surface_destroy(void) {
|
|
if (wayland->wlr_surface != NULL) {
|
|
zwlr_layer_surface_v1_destroy(wayland->wlr_surface);
|
|
wayland->wlr_surface = NULL;
|
|
}
|
|
if (wayland->surface != NULL) {
|
|
wl_surface_destroy(wayland->surface);
|
|
wayland->surface = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
wayland_layer_shell_surface_closed(void *data,
|
|
struct zwlr_layer_surface_v1 *surface) {
|
|
g_debug("Layer shell surface closed");
|
|
|
|
wayland_surface_destroy();
|
|
|
|
// In this case, we recreate the layer shell surface the best we can and
|
|
// re-initialize everything:
|
|
|
|
// recreate layer shell
|
|
wayland_display_late_setup();
|
|
|
|
// create new buffers with the correct scaled size
|
|
rofi_view_pool_refresh();
|
|
|
|
RofiViewState *state = rofi_view_get_active();
|
|
if (state != NULL) {
|
|
rofi_view_set_size(state, -1, -1);
|
|
}
|
|
}
|
|
|
|
static const struct zwlr_layer_surface_v1_listener
|
|
wayland_layer_shell_surface_listener = {
|
|
.configure = wayland_layer_shell_surface_configure,
|
|
.closed = wayland_layer_shell_surface_closed,
|
|
};
|
|
|
|
static gboolean wayland_error(gpointer user_data) {
|
|
g_main_loop_quit(wayland->main_loop);
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static gboolean wayland_cursor_reload_theme(guint scale) {
|
|
if (wayland->cursor.theme != NULL) {
|
|
if (wayland->cursor.scale == scale)
|
|
return TRUE;
|
|
|
|
wl_cursor_theme_destroy(wayland->cursor.theme);
|
|
wayland->cursor.theme = NULL;
|
|
wayland->cursor.cursor = NULL;
|
|
}
|
|
|
|
guint64 cursor_size = 24;
|
|
char *env_cursor_size = (char *)g_getenv("XCURSOR_SIZE");
|
|
if (env_cursor_size && strlen(env_cursor_size) > 0) {
|
|
guint64 size = g_ascii_strtoull(env_cursor_size, NULL, 10);
|
|
if (0 < size && size < G_MAXUINT64) {
|
|
cursor_size = size;
|
|
}
|
|
}
|
|
cursor_size *= scale;
|
|
|
|
wayland->cursor.theme = wl_cursor_theme_load(wayland->cursor.theme_name,
|
|
cursor_size, wayland->shm);
|
|
if (wayland->cursor.theme != NULL) {
|
|
wayland->cursor.cursor = rofi_cursor_type_to_wl_cursor(
|
|
wayland->cursor.theme, wayland->cursor.type);
|
|
if (wayland->cursor.cursor == NULL) {
|
|
wl_cursor_theme_destroy(wayland->cursor.theme);
|
|
wayland->cursor.theme = NULL;
|
|
return FALSE;
|
|
} else {
|
|
wayland->cursor.scale = scale;
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean wayland_display_setup(GMainLoop *main_loop,
|
|
NkBindings *bindings) {
|
|
wayland->main_loop = main_loop;
|
|
|
|
char *display = (char *)g_getenv("WAYLAND_DISPLAY");
|
|
wayland->main_loop_source = g_water_wayland_source_new(NULL, display);
|
|
if (wayland->main_loop_source == NULL) {
|
|
g_warning("Could not connect to the Wayland compositor");
|
|
return FALSE;
|
|
}
|
|
|
|
g_water_wayland_source_set_error_callback(wayland->main_loop_source,
|
|
wayland_error, NULL, NULL);
|
|
|
|
wayland->buffer_count = 3;
|
|
wayland->cursor.type = ROFI_CURSOR_DEFAULT;
|
|
wayland->scale = 1;
|
|
|
|
wayland->outputs = g_hash_table_new(g_direct_hash, g_direct_equal);
|
|
wayland->seats = g_hash_table_new(g_direct_hash, g_direct_equal);
|
|
wayland->seats_by_name =
|
|
g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
|
|
|
|
wayland->display =
|
|
g_water_wayland_source_get_display(wayland->main_loop_source);
|
|
wayland->registry = wl_display_get_registry(wayland->display);
|
|
wl_registry_add_listener(wayland->registry, &wayland_registry_listener, NULL);
|
|
wl_display_roundtrip(wayland->display);
|
|
|
|
if (wayland->compositor == NULL || wayland->shm == NULL ||
|
|
g_hash_table_size(wayland->outputs) == 0 ||
|
|
g_hash_table_size(wayland->seats) == 0) {
|
|
g_error("Could not connect to wayland compositor");
|
|
return FALSE;
|
|
}
|
|
if (wayland->layer_shell == NULL) {
|
|
g_error("Rofi on wayland requires support for the layer shell protocol");
|
|
return FALSE;
|
|
}
|
|
|
|
wayland->bindings_seat = nk_bindings_seat_new(bindings, XKB_CONTEXT_NO_FLAGS);
|
|
|
|
// Wait for output information
|
|
wl_display_roundtrip(wayland->display);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean wayland_display_late_setup(void) {
|
|
wayland_output *output = wayland_output_by_name(config.monitor);
|
|
|
|
wayland->surface = wl_compositor_create_surface(wayland->compositor);
|
|
wayland->wlr_surface = zwlr_layer_shell_v1_get_layer_surface(
|
|
wayland->layer_shell, wayland->surface,
|
|
(output != NULL ? output->output : NULL),
|
|
ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, "rofi");
|
|
|
|
// Set size zero and anchor on all corners to get the usable screen size
|
|
// see https://github.com/swaywm/wlroots/pull/2422
|
|
zwlr_layer_surface_v1_set_anchor(wayland->wlr_surface,
|
|
ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
|
|
ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
|
|
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
|
|
ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT);
|
|
zwlr_layer_surface_v1_set_size(wayland->wlr_surface, 0, 0);
|
|
zwlr_layer_surface_v1_set_keyboard_interactivity(wayland->wlr_surface, 1);
|
|
zwlr_layer_surface_v1_add_listener(
|
|
wayland->wlr_surface, &wayland_layer_shell_surface_listener, NULL);
|
|
|
|
if (config.global_kb && wayland->kb_shortcuts_inhibit_manager) {
|
|
g_debug("inhibit shortcuts from compositor");
|
|
GHashTableIter iter;
|
|
wayland_seat *seat;
|
|
g_hash_table_iter_init(&iter, wayland->seats);
|
|
while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&seat)) {
|
|
// we don't need to keep track of these, they will get inactive when the
|
|
// surface is destroyed
|
|
zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts(wayland->kb_shortcuts_inhibit_manager,
|
|
wayland->surface, seat->seat);
|
|
}
|
|
}
|
|
|
|
wl_surface_add_listener(wayland->surface, &wayland_surface_interface,
|
|
wayland);
|
|
wl_surface_commit(wayland->surface);
|
|
wl_display_roundtrip(wayland->display);
|
|
wayland_frame_callback(wayland, wayland->frame_cb, 0);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean display_get_surface_dimensions(int *width, int *height) {
|
|
if (wayland->layer_width != 0) {
|
|
if (width != NULL) {
|
|
*width = wayland->layer_width;
|
|
}
|
|
if (height != NULL) {
|
|
*height = wayland->layer_height;
|
|
}
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void display_set_surface_dimensions(int width, int height, int x_margin,
|
|
int y_margin, int loc) {
|
|
|
|
wayland->layer_width = width;
|
|
wayland->layer_height = height;
|
|
zwlr_layer_surface_v1_set_size(wayland->wlr_surface, width, height);
|
|
|
|
uint32_t wlr_anchor = 0;
|
|
switch (loc) {
|
|
case WL_NORTH_WEST:
|
|
wlr_anchor =
|
|
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP;
|
|
break;
|
|
case WL_NORTH:
|
|
wlr_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP;
|
|
break;
|
|
case WL_NORTH_EAST:
|
|
wlr_anchor =
|
|
ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP;
|
|
break;
|
|
case WL_EAST:
|
|
wlr_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
|
|
break;
|
|
case WL_SOUTH_EAST:
|
|
wlr_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
|
|
ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
|
|
break;
|
|
case WL_SOUTH:
|
|
wlr_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
|
|
break;
|
|
case WL_SOUTH_WEST:
|
|
wlr_anchor =
|
|
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
|
|
break;
|
|
case WL_WEST:
|
|
wlr_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT;
|
|
break;
|
|
case WL_CENTER:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (height == 0) {
|
|
wlr_anchor |=
|
|
ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP;
|
|
}
|
|
if (width == 0) {
|
|
wlr_anchor |=
|
|
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
|
|
}
|
|
|
|
zwlr_layer_surface_v1_set_anchor(wayland->wlr_surface, wlr_anchor);
|
|
|
|
// NOTE: Setting margin for edges we are not anchored to has no effect, so we
|
|
// can safely set contradictory margins (e.g. top vs bottom) - at most one of
|
|
// the margins on a given axis will have effect.
|
|
// This also means that margin has no effect if the window is centered. :(
|
|
zwlr_layer_surface_v1_set_margin(wayland->wlr_surface, y_margin, -x_margin,
|
|
-y_margin, x_margin);
|
|
}
|
|
|
|
static void wayland_display_early_cleanup(void) {
|
|
if (wayland->main_loop_source == NULL) {
|
|
return;
|
|
}
|
|
|
|
wayland_surface_destroy();
|
|
wl_display_flush(wayland->display);
|
|
}
|
|
|
|
static void wayland_display_cleanup(void) {
|
|
if (wayland->main_loop_source == NULL) {
|
|
return;
|
|
}
|
|
|
|
nk_bindings_seat_free(wayland->bindings_seat);
|
|
g_hash_table_unref(wayland->seats_by_name);
|
|
g_hash_table_unref(wayland->seats);
|
|
g_hash_table_unref(wayland->outputs);
|
|
wl_registry_destroy(wayland->registry);
|
|
wl_display_flush(wayland->display);
|
|
g_water_wayland_source_free(wayland->main_loop_source);
|
|
}
|
|
|
|
static void wayland_display_dump_monitor_layout(void) {
|
|
int is_term = isatty(fileno(stdout));
|
|
GHashTableIter iter;
|
|
wayland_output *output;
|
|
|
|
g_hash_table_iter_init(&iter, wayland->outputs);
|
|
printf("Monitor layout:\n");
|
|
while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&output)) {
|
|
printf("%s ID%s: %" PRIu32, is_term ? color_bold : "",
|
|
is_term ? color_reset : "", output->global_name);
|
|
printf("\n");
|
|
printf("%s name%s: %s\n", is_term ? color_bold : "",
|
|
is_term ? color_reset : "", output->name);
|
|
printf("%s scale%s: %" PRIi32 "\n", is_term ? color_bold : "",
|
|
is_term ? color_reset : "", output->current.scale);
|
|
printf("%s position%s: %" PRIi32 ",%" PRIi32 "\n",
|
|
is_term ? color_bold : "", is_term ? color_reset : "",
|
|
output->current.x, output->current.y);
|
|
printf("%s size%s: %" PRIi32 ",%" PRIi32 "\n",
|
|
is_term ? color_bold : "", is_term ? color_reset : "",
|
|
output->current.width, output->current.height);
|
|
|
|
if (output->current.physical_width > 0 &&
|
|
output->current.physical_height > 0) {
|
|
printf("%s size%s: %" PRIi32 "mm,%" PRIi32
|
|
"mm dpi: %.0f,%.0f\n",
|
|
is_term ? color_bold : "", is_term ? color_reset : "",
|
|
output->current.physical_width, output->current.physical_height,
|
|
wayland_output_get_dpi(output, output->current.scale, width),
|
|
wayland_output_get_dpi(output, output->current.scale, height));
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
|
|
static void
|
|
wayland_display_startup_notification(RofiHelperExecuteContext *context,
|
|
GSpawnChildSetupFunc *child_setup,
|
|
gpointer *user_data) {}
|
|
|
|
static int wayland_display_monitor_active(workarea *mon) {
|
|
// TODO: do something?
|
|
return FALSE;
|
|
}
|
|
|
|
static void wayland_display_set_input_focus(guint w) {}
|
|
|
|
static void wayland_display_revert_input_focus(void) {}
|
|
|
|
static const struct _view_proxy *wayland_display_view_proxy(void) {
|
|
return wayland_view_proxy;
|
|
}
|
|
|
|
static guint wayland_display_scale(void) { return wayland->scale; }
|
|
|
|
static char *wayland_get_clipboard_data(int type) {
|
|
switch (type) {
|
|
case CLIPBOARD_DEFAULT:
|
|
return wayland->clipboard_default_data;
|
|
case CLIPBOARD_PRIMARY:
|
|
return wayland->clipboard_primary_data;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void wayland_set_fullscreen_mode(void) {
|
|
if (!wayland->wlr_surface) {
|
|
return;
|
|
}
|
|
zwlr_layer_surface_v1_set_exclusive_zone(wayland->wlr_surface, -1);
|
|
zwlr_layer_surface_v1_set_size(wayland->wlr_surface, 0, 0);
|
|
wl_surface_commit(wayland->surface);
|
|
wl_display_roundtrip(wayland->display);
|
|
|
|
rofi_view_pool_refresh();
|
|
}
|
|
|
|
static display_proxy display_ = {
|
|
.setup = wayland_display_setup,
|
|
.late_setup = wayland_display_late_setup,
|
|
.early_cleanup = wayland_display_early_cleanup,
|
|
.cleanup = wayland_display_cleanup,
|
|
.dump_monitor_layout = wayland_display_dump_monitor_layout,
|
|
.startup_notification = wayland_display_startup_notification,
|
|
.monitor_active = wayland_display_monitor_active,
|
|
.set_input_focus = wayland_display_set_input_focus,
|
|
.revert_input_focus = wayland_display_revert_input_focus,
|
|
.scale = wayland_display_scale,
|
|
.get_clipboard_data = wayland_get_clipboard_data,
|
|
.set_fullscreen_mode = wayland_set_fullscreen_mode,
|
|
|
|
.view = wayland_display_view_proxy,
|
|
};
|
|
|
|
display_proxy *const wayland_proxy = &display_;
|