2019-10-27 15:57:23 +01:00
|
|
|
#include "wayland.h"
|
|
|
|
|
|
2019-10-27 19:08:48 +01:00
|
|
|
#include <stdlib.h>
|
2019-12-01 19:22:45 +01:00
|
|
|
#include <string.h>
|
2019-10-27 19:16:12 +01:00
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
2019-10-27 19:08:48 +01:00
|
|
|
#include <sys/timerfd.h>
|
2019-10-27 19:11:35 +01:00
|
|
|
#include <sys/epoll.h>
|
2019-10-27 19:08:48 +01:00
|
|
|
|
2019-10-27 15:57:23 +01:00
|
|
|
#include <wayland-client.h>
|
|
|
|
|
#include <wayland-cursor.h>
|
|
|
|
|
#include <xdg-shell.h>
|
|
|
|
|
#include <xkbcommon/xkbcommon-compose.h>
|
|
|
|
|
|
2019-11-17 19:19:55 +01:00
|
|
|
#include <tllist.h>
|
2019-10-27 15:57:23 +01:00
|
|
|
#include <xdg-output-unstable-v1.h>
|
|
|
|
|
#include <xdg-decoration-unstable-v1.h>
|
|
|
|
|
|
|
|
|
|
#define LOG_MODULE "wayland"
|
|
|
|
|
#define LOG_ENABLE_DBG 0
|
|
|
|
|
#include "log.h"
|
|
|
|
|
|
2019-12-31 16:12:48 +01:00
|
|
|
#include "config.h"
|
2019-10-27 18:43:07 +01:00
|
|
|
#include "terminal.h"
|
2019-10-27 19:08:48 +01:00
|
|
|
#include "input.h"
|
|
|
|
|
#include "render.h"
|
|
|
|
|
#include "selection.h"
|
2019-10-27 15:57:23 +01:00
|
|
|
|
2019-10-27 19:08:48 +01:00
|
|
|
#define min(x, y) ((x) < (y) ? (x) : (y))
|
|
|
|
|
#define max(x, y) ((x) > (y) ? (x) : (y))
|
|
|
|
|
|
2019-11-28 19:35:47 +01:00
|
|
|
static bool wayl_reload_cursor_theme(
|
|
|
|
|
struct wayland *wayl, const struct terminal *term);
|
|
|
|
|
|
2019-10-27 19:08:48 +01:00
|
|
|
static void
|
|
|
|
|
shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
|
|
|
|
|
{
|
|
|
|
|
struct wayland *wayl = data;
|
|
|
|
|
if (format == WL_SHM_FORMAT_ARGB8888)
|
|
|
|
|
wayl->have_argb8888 = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct wl_shm_listener shm_listener = {
|
|
|
|
|
.format = &shm_format,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial)
|
|
|
|
|
{
|
|
|
|
|
LOG_DBG("wm base ping");
|
|
|
|
|
xdg_wm_base_pong(shell, serial);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct xdg_wm_base_listener xdg_wm_base_listener = {
|
|
|
|
|
.ping = &xdg_wm_base_ping,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
|
|
|
|
|
enum wl_seat_capability caps)
|
|
|
|
|
{
|
|
|
|
|
struct wayland *wayl = data;
|
|
|
|
|
|
|
|
|
|
if (wayl->keyboard != NULL) {
|
|
|
|
|
wl_keyboard_release(wayl->keyboard);
|
|
|
|
|
wayl->keyboard = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (wayl->pointer.pointer != NULL) {
|
|
|
|
|
wl_pointer_release(wayl->pointer.pointer);
|
|
|
|
|
wayl->pointer.pointer = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (caps & WL_SEAT_CAPABILITY_KEYBOARD) {
|
|
|
|
|
wayl->keyboard = wl_seat_get_keyboard(wl_seat);
|
|
|
|
|
wl_keyboard_add_listener(wayl->keyboard, &keyboard_listener, wayl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (caps & WL_SEAT_CAPABILITY_POINTER) {
|
|
|
|
|
wayl->pointer.pointer = wl_seat_get_pointer(wl_seat);
|
|
|
|
|
wl_pointer_add_listener(wayl->pointer.pointer, &pointer_listener, wayl);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct wl_seat_listener seat_listener = {
|
|
|
|
|
.capabilities = seat_handle_capabilities,
|
|
|
|
|
.name = seat_handle_name,
|
|
|
|
|
};
|
|
|
|
|
|
2019-12-05 19:35:34 +01:00
|
|
|
static void
|
|
|
|
|
output_update_ppi(struct monitor *mon)
|
|
|
|
|
{
|
|
|
|
|
int x_inches = mon->width_mm * 0.03937008;
|
|
|
|
|
int y_inches = mon->height_mm * 0.03937008;
|
|
|
|
|
mon->x_ppi = mon->width_px / x_inches;
|
|
|
|
|
mon->y_ppi = mon->height_px / y_inches;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-27 19:08:48 +01:00
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
struct monitor *mon = data;
|
|
|
|
|
mon->width_mm = physical_width;
|
|
|
|
|
mon->height_mm = physical_height;
|
2019-12-05 19:35:34 +01:00
|
|
|
mon->inch = sqrt(pow(mon->width_mm, 2) + pow(mon->height_mm, 2)) * 0.03937008;
|
|
|
|
|
mon->make = make != NULL ? strdup(make) : NULL;
|
|
|
|
|
mon->model = model != NULL ? strdup(model) : NULL;
|
|
|
|
|
output_update_ppi(mon);
|
2019-10-27 19:08:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
output_mode(void *data, struct wl_output *wl_output, uint32_t flags,
|
|
|
|
|
int32_t width, int32_t height, int32_t refresh)
|
|
|
|
|
{
|
|
|
|
|
if ((flags & WL_OUTPUT_MODE_CURRENT) == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
struct monitor *mon = data;
|
|
|
|
|
mon->refresh = (float)refresh / 1000;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
output_done(void *data, struct wl_output *wl_output)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
output_scale(void *data, struct wl_output *wl_output, int32_t factor)
|
|
|
|
|
{
|
|
|
|
|
struct monitor *mon = data;
|
|
|
|
|
|
|
|
|
|
mon->scale = factor;
|
|
|
|
|
|
2019-10-30 20:02:06 +01:00
|
|
|
tll_foreach(mon->wayl->terms, it) {
|
|
|
|
|
struct terminal *term = it->item;
|
2020-01-03 12:45:16 +01:00
|
|
|
int scale = term->scale;
|
|
|
|
|
|
|
|
|
|
render_resize(term, term->width / scale, term->height / scale, true);
|
2019-10-27 19:36:45 +01:00
|
|
|
wayl_reload_cursor_theme(mon->wayl, term);
|
2019-10-27 19:08:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct wl_output_listener output_listener = {
|
|
|
|
|
.geometry = &output_geometry,
|
|
|
|
|
.mode = &output_mode,
|
|
|
|
|
.done = &output_done,
|
|
|
|
|
.scale = &output_scale,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
xdg_output_handle_logical_position(
|
|
|
|
|
void *data, struct zxdg_output_v1 *xdg_output, int32_t x, int32_t y)
|
|
|
|
|
{
|
|
|
|
|
struct monitor *mon = data;
|
|
|
|
|
mon->x = x;
|
|
|
|
|
mon->y = y;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output,
|
|
|
|
|
int32_t width, int32_t height)
|
|
|
|
|
{
|
|
|
|
|
struct monitor *mon = data;
|
|
|
|
|
mon->width_px = width;
|
|
|
|
|
mon->height_px = height;
|
2019-12-05 19:35:34 +01:00
|
|
|
output_update_ppi(mon);
|
2019-10-27 19:08:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output,
|
|
|
|
|
const char *name)
|
|
|
|
|
{
|
|
|
|
|
struct monitor *mon = data;
|
|
|
|
|
mon->name = strdup(name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
xdg_output_handle_description(void *data, struct zxdg_output_v1 *xdg_output,
|
|
|
|
|
const char *description)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-31 15:43:15 +01:00
|
|
|
static const struct zxdg_output_v1_listener xdg_output_listener = {
|
2019-10-27 19:08:48 +01:00
|
|
|
.logical_position = xdg_output_handle_logical_position,
|
|
|
|
|
.logical_size = xdg_output_handle_logical_size,
|
|
|
|
|
.done = xdg_output_handle_done,
|
|
|
|
|
.name = xdg_output_handle_name,
|
|
|
|
|
.description = xdg_output_handle_description,
|
|
|
|
|
};
|
|
|
|
|
|
2019-12-31 15:39:40 +01:00
|
|
|
static void
|
|
|
|
|
clock_id(void *data, struct wp_presentation *wp_presentation, uint32_t clk_id)
|
|
|
|
|
{
|
|
|
|
|
struct wayland *wayl = data;
|
|
|
|
|
wayl->presentation_clock_id = clk_id;
|
|
|
|
|
LOG_DBG("presentation clock ID: %u", clk_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct wp_presentation_listener presentation_listener = {
|
|
|
|
|
.clock_id = &clock_id,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2019-11-03 15:39:26 +01:00
|
|
|
static bool
|
|
|
|
|
verify_iface_version(const char *iface, uint32_t version, uint32_t wanted)
|
|
|
|
|
{
|
|
|
|
|
if (version >= wanted)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
LOG_ERR("%s: need interface version %u, but compositor only implements %u",
|
|
|
|
|
iface, wanted, version);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-27 19:08:48 +01:00
|
|
|
static void
|
|
|
|
|
handle_global(void *data, struct wl_registry *registry,
|
|
|
|
|
uint32_t name, const char *interface, uint32_t version)
|
|
|
|
|
{
|
|
|
|
|
LOG_DBG("global: %s, version=%u", interface, version);
|
|
|
|
|
struct wayland *wayl = data;
|
|
|
|
|
|
|
|
|
|
if (strcmp(interface, wl_compositor_interface.name) == 0) {
|
2019-11-03 15:39:26 +01:00
|
|
|
const uint32_t required = 4;
|
|
|
|
|
if (!verify_iface_version(interface, version, required))
|
|
|
|
|
return;
|
|
|
|
|
|
2019-10-27 19:08:48 +01:00
|
|
|
wayl->compositor = wl_registry_bind(
|
2019-11-03 15:39:26 +01:00
|
|
|
wayl->registry, name, &wl_compositor_interface, required);
|
2019-10-27 19:08:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if (strcmp(interface, wl_subcompositor_interface.name) == 0) {
|
2019-11-03 15:39:26 +01:00
|
|
|
const uint32_t required = 1;
|
|
|
|
|
if (!verify_iface_version(interface, version, required))
|
|
|
|
|
return;
|
|
|
|
|
|
2019-10-27 19:08:48 +01:00
|
|
|
wayl->sub_compositor = wl_registry_bind(
|
2019-11-03 15:39:26 +01:00
|
|
|
wayl->registry, name, &wl_subcompositor_interface, required);
|
2019-10-27 19:08:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if (strcmp(interface, wl_shm_interface.name) == 0) {
|
2019-11-03 15:39:26 +01:00
|
|
|
const uint32_t required = 1;
|
|
|
|
|
if (!verify_iface_version(interface, version, required))
|
|
|
|
|
return;
|
|
|
|
|
|
2019-10-27 19:08:48 +01:00
|
|
|
wayl->shm = wl_registry_bind(
|
2019-11-03 15:39:26 +01:00
|
|
|
wayl->registry, name, &wl_shm_interface, required);
|
2019-10-27 19:08:48 +01:00
|
|
|
wl_shm_add_listener(wayl->shm, &shm_listener, wayl);
|
|
|
|
|
wl_display_roundtrip(wayl->display);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
|
2019-11-03 15:39:26 +01:00
|
|
|
const uint32_t required = 1;
|
|
|
|
|
if (!verify_iface_version(interface, version, required))
|
|
|
|
|
return;
|
|
|
|
|
|
2019-10-27 19:08:48 +01:00
|
|
|
wayl->shell = wl_registry_bind(
|
2019-11-03 15:39:26 +01:00
|
|
|
wayl->registry, name, &xdg_wm_base_interface, required);
|
2019-10-27 19:08:48 +01:00
|
|
|
xdg_wm_base_add_listener(wayl->shell, &xdg_wm_base_listener, wayl);
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-03 15:39:26 +01:00
|
|
|
else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) {
|
|
|
|
|
const uint32_t required = 1;
|
|
|
|
|
if (!verify_iface_version(interface, version, required))
|
|
|
|
|
return;
|
|
|
|
|
|
2019-10-27 19:08:48 +01:00
|
|
|
wayl->xdg_decoration_manager = wl_registry_bind(
|
2019-11-03 15:39:26 +01:00
|
|
|
wayl->registry, name, &zxdg_decoration_manager_v1_interface, required);
|
|
|
|
|
}
|
2019-10-27 19:08:48 +01:00
|
|
|
|
|
|
|
|
else if (strcmp(interface, wl_seat_interface.name) == 0) {
|
2019-11-03 15:39:26 +01:00
|
|
|
const uint32_t required = 5;
|
|
|
|
|
if (!verify_iface_version(interface, version, required))
|
|
|
|
|
return;
|
|
|
|
|
|
2019-10-27 19:08:48 +01:00
|
|
|
wayl->seat = wl_registry_bind(
|
2019-11-03 15:39:26 +01:00
|
|
|
wayl->registry, name, &wl_seat_interface, required);
|
2019-10-27 19:08:48 +01:00
|
|
|
wl_seat_add_listener(wayl->seat, &seat_listener, wayl);
|
|
|
|
|
wl_display_roundtrip(wayl->display);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) {
|
2019-11-03 16:14:35 +01:00
|
|
|
const uint32_t required = 1;
|
|
|
|
|
if (!verify_iface_version(interface, version, required))
|
|
|
|
|
return;
|
|
|
|
|
|
2019-10-27 19:08:48 +01:00
|
|
|
wayl->xdg_output_manager = wl_registry_bind(
|
2019-11-03 16:14:35 +01:00
|
|
|
wayl->registry, name, &zxdg_output_manager_v1_interface,
|
|
|
|
|
min(version, 2));
|
2019-10-27 19:08:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if (strcmp(interface, wl_output_interface.name) == 0) {
|
2019-11-03 15:39:26 +01:00
|
|
|
const uint32_t required = 3;
|
|
|
|
|
if (!verify_iface_version(interface, version, required))
|
|
|
|
|
return;
|
|
|
|
|
|
2019-10-27 19:08:48 +01:00
|
|
|
struct wl_output *output = wl_registry_bind(
|
2019-11-03 15:39:26 +01:00
|
|
|
wayl->registry, name, &wl_output_interface, required);
|
2019-10-27 19:08:48 +01:00
|
|
|
|
|
|
|
|
tll_push_back(
|
|
|
|
|
wayl->monitors, ((struct monitor){.wayl = wayl, .output = output}));
|
|
|
|
|
|
|
|
|
|
struct monitor *mon = &tll_back(wayl->monitors);
|
|
|
|
|
wl_output_add_listener(output, &output_listener, mon);
|
|
|
|
|
|
2019-11-03 15:39:26 +01:00
|
|
|
if (wayl->xdg_output_manager != NULL) {
|
|
|
|
|
mon->xdg = zxdg_output_manager_v1_get_xdg_output(
|
|
|
|
|
wayl->xdg_output_manager, mon->output);
|
|
|
|
|
zxdg_output_v1_add_listener(mon->xdg, &xdg_output_listener, mon);
|
|
|
|
|
}
|
2019-10-27 19:08:48 +01:00
|
|
|
wl_display_roundtrip(wayl->display);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if (strcmp(interface, wl_data_device_manager_interface.name) == 0) {
|
2019-11-03 15:39:26 +01:00
|
|
|
const uint32_t required = 1;
|
|
|
|
|
if (!verify_iface_version(interface, version, required))
|
|
|
|
|
return;
|
|
|
|
|
|
2019-10-27 19:08:48 +01:00
|
|
|
wayl->data_device_manager = wl_registry_bind(
|
2019-11-03 15:39:26 +01:00
|
|
|
wayl->registry, name, &wl_data_device_manager_interface, required);
|
2019-10-27 19:08:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if (strcmp(interface, zwp_primary_selection_device_manager_v1_interface.name) == 0) {
|
2019-11-03 15:39:26 +01:00
|
|
|
const uint32_t required = 1;
|
|
|
|
|
if (!verify_iface_version(interface, version, required))
|
|
|
|
|
return;
|
|
|
|
|
|
2019-10-27 19:08:48 +01:00
|
|
|
wayl->primary_selection_device_manager = wl_registry_bind(
|
2019-11-03 15:39:26 +01:00
|
|
|
wayl->registry, name,
|
|
|
|
|
&zwp_primary_selection_device_manager_v1_interface, required);
|
2019-10-27 19:08:48 +01:00
|
|
|
}
|
2019-12-31 15:39:40 +01:00
|
|
|
|
|
|
|
|
else if (strcmp(interface, wp_presentation_interface.name) == 0) {
|
2019-12-31 16:12:48 +01:00
|
|
|
if (wayl->conf->presentation_timings) {
|
|
|
|
|
const uint32_t required = 1;
|
|
|
|
|
if (!verify_iface_version(interface, version, required))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
wayl->presentation = wl_registry_bind(
|
|
|
|
|
wayl->registry, name, &wp_presentation_interface, required);
|
|
|
|
|
wp_presentation_add_listener(
|
|
|
|
|
wayl->presentation, &presentation_listener, wayl);
|
|
|
|
|
}
|
2019-12-31 15:39:40 +01:00
|
|
|
}
|
2019-10-27 19:08:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
surface_enter(void *data, struct wl_surface *wl_surface,
|
|
|
|
|
struct wl_output *wl_output)
|
2019-10-27 15:57:23 +01:00
|
|
|
{
|
2020-01-03 13:40:37 +01:00
|
|
|
struct wl_window *win = data;
|
|
|
|
|
struct terminal *term = win->term;
|
2019-10-27 19:08:48 +01:00
|
|
|
|
2020-01-03 13:40:37 +01:00
|
|
|
tll_foreach(term->wl->monitors, it) {
|
2019-10-27 19:08:48 +01:00
|
|
|
if (it->item.output == wl_output) {
|
|
|
|
|
LOG_DBG("mapped on %s", it->item.name);
|
|
|
|
|
tll_push_back(term->window->on_outputs, &it->item);
|
|
|
|
|
|
|
|
|
|
/* Resize, since scale-to-use may have changed */
|
2020-01-03 12:45:16 +01:00
|
|
|
int scale = term->scale;
|
|
|
|
|
render_resize(term, term->width / scale, term->height / scale, true);
|
2020-01-03 13:40:37 +01:00
|
|
|
wayl_reload_cursor_theme(term->wl, term);
|
2019-10-27 19:08:48 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG_ERR("mapped on unknown output");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
surface_leave(void *data, struct wl_surface *wl_surface,
|
|
|
|
|
struct wl_output *wl_output)
|
|
|
|
|
{
|
2020-01-03 13:40:37 +01:00
|
|
|
struct wl_window *win = data;
|
|
|
|
|
struct terminal *term = win->term;
|
2019-10-27 19:08:48 +01:00
|
|
|
|
|
|
|
|
tll_foreach(term->window->on_outputs, it) {
|
|
|
|
|
if (it->item->output != wl_output)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
LOG_DBG("unmapped from %s", it->item->name);
|
|
|
|
|
tll_remove(term->window->on_outputs, it);
|
|
|
|
|
|
|
|
|
|
/* Resize, since scale-to-use may have changed */
|
2020-01-03 12:45:16 +01:00
|
|
|
int scale = term->scale;
|
|
|
|
|
render_resize(term, term->width / scale, term->height / scale, true);
|
2020-01-03 13:40:37 +01:00
|
|
|
wayl_reload_cursor_theme(term->wl, term);
|
2019-10-27 19:08:48 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG_ERR("unmapped from unknown output");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct wl_surface_listener surface_listener = {
|
|
|
|
|
.enter = &surface_enter,
|
|
|
|
|
.leave = &surface_leave,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel,
|
|
|
|
|
int32_t width, int32_t height, struct wl_array *states)
|
|
|
|
|
{
|
2020-01-02 16:06:35 +01:00
|
|
|
bool is_focused = false;
|
2020-01-03 12:52:18 +01:00
|
|
|
#if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG
|
2020-01-03 12:49:04 +01:00
|
|
|
char state_str[2048];
|
|
|
|
|
int state_chars = 0;
|
|
|
|
|
|
|
|
|
|
static const char *const strings[] = {
|
|
|
|
|
[XDG_TOPLEVEL_STATE_MAXIMIZED] = "maximized",
|
|
|
|
|
[XDG_TOPLEVEL_STATE_FULLSCREEN] = "fullscreen",
|
|
|
|
|
[XDG_TOPLEVEL_STATE_RESIZING] = "resizing",
|
|
|
|
|
[XDG_TOPLEVEL_STATE_ACTIVATED] = "activated",
|
|
|
|
|
[XDG_TOPLEVEL_STATE_TILED_LEFT] = "tiled:left",
|
|
|
|
|
[XDG_TOPLEVEL_STATE_TILED_RIGHT] = "tiled:right",
|
|
|
|
|
[XDG_TOPLEVEL_STATE_TILED_TOP] = "tiled:top",
|
|
|
|
|
[XDG_TOPLEVEL_STATE_TILED_BOTTOM] = "tiled:bottom",
|
|
|
|
|
};
|
|
|
|
|
#endif
|
2020-01-02 17:38:50 +01:00
|
|
|
|
2020-01-02 16:06:35 +01:00
|
|
|
enum xdg_toplevel_state *state;
|
|
|
|
|
wl_array_for_each(state, states) {
|
2020-01-03 12:49:04 +01:00
|
|
|
switch (*state) {
|
|
|
|
|
case XDG_TOPLEVEL_STATE_ACTIVATED:
|
2020-01-02 16:06:35 +01:00
|
|
|
is_focused = true;
|
2020-01-03 12:49:04 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case XDG_TOPLEVEL_STATE_MAXIMIZED:
|
|
|
|
|
case XDG_TOPLEVEL_STATE_FULLSCREEN:
|
|
|
|
|
case XDG_TOPLEVEL_STATE_RESIZING:
|
|
|
|
|
case XDG_TOPLEVEL_STATE_TILED_LEFT:
|
|
|
|
|
case XDG_TOPLEVEL_STATE_TILED_RIGHT:
|
|
|
|
|
case XDG_TOPLEVEL_STATE_TILED_TOP:
|
|
|
|
|
case XDG_TOPLEVEL_STATE_TILED_BOTTOM:
|
|
|
|
|
/* Ignored */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-03 12:52:18 +01:00
|
|
|
#if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG
|
2020-01-03 12:49:04 +01:00
|
|
|
if (*state >= XDG_TOPLEVEL_STATE_MAXIMIZED &&
|
|
|
|
|
*state <= XDG_TOPLEVEL_STATE_TILED_BOTTOM)
|
|
|
|
|
{
|
|
|
|
|
state_chars += snprintf(
|
|
|
|
|
&state_str[state_chars], sizeof(state_str) - state_chars,
|
|
|
|
|
"%s, ", strings[*state]);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2020-01-02 16:06:35 +01:00
|
|
|
}
|
|
|
|
|
|
2020-01-03 12:52:18 +01:00
|
|
|
#if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG
|
2020-01-03 12:49:04 +01:00
|
|
|
if (state_chars > 2)
|
|
|
|
|
state_str[state_chars - 2] = '\0';
|
|
|
|
|
|
|
|
|
|
LOG_DBG("xdg-toplevel: configure: size=%dx%d, states=%s",
|
|
|
|
|
width, height, state_str);
|
2020-01-03 12:52:18 +01:00
|
|
|
#endif
|
2020-01-03 12:49:04 +01:00
|
|
|
|
2020-01-03 13:46:15 +01:00
|
|
|
struct wl_window *win = data;
|
|
|
|
|
struct terminal *term = win->term;
|
2020-01-02 17:38:50 +01:00
|
|
|
|
2020-01-03 11:20:13 +01:00
|
|
|
if (is_focused)
|
2020-01-02 19:37:01 +01:00
|
|
|
term_visual_focus_in(term);
|
2020-01-03 11:20:13 +01:00
|
|
|
else
|
2020-01-02 19:37:01 +01:00
|
|
|
term_visual_focus_out(term);
|
|
|
|
|
|
2020-01-03 12:54:03 +01:00
|
|
|
render_resize(term, width, height, false);
|
2019-10-27 19:08:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel)
|
|
|
|
|
{
|
2020-01-03 13:46:15 +01:00
|
|
|
struct wl_window *win = data;
|
|
|
|
|
struct terminal *term = win->term;
|
2019-10-27 19:08:48 +01:00
|
|
|
LOG_DBG("xdg-toplevel: close");
|
2019-10-30 20:05:34 +01:00
|
|
|
term_shutdown(term);
|
2019-10-27 19:08:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
|
|
|
|
|
.configure = &xdg_toplevel_configure,
|
|
|
|
|
.close = &xdg_toplevel_close,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
xdg_surface_configure(void *data, struct xdg_surface *xdg_surface,
|
|
|
|
|
uint32_t serial)
|
|
|
|
|
{
|
2020-01-03 12:45:58 +01:00
|
|
|
LOG_DBG("xdg-surface: configure");
|
2019-10-27 19:08:48 +01:00
|
|
|
xdg_surface_ack_configure(xdg_surface, serial);
|
2020-01-03 12:45:58 +01:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Changes done in e.g. xdg-toplevel-configure will be ignored
|
|
|
|
|
* since the 'configure' event hasn't been ack:ed yet.
|
|
|
|
|
*
|
|
|
|
|
* Unfortunately, *this* function is called *last*, meaning we
|
|
|
|
|
* have no way of acking the configure before we resize the
|
|
|
|
|
* terminal in xdg-toplevel-configure.
|
|
|
|
|
*
|
|
|
|
|
* So, refresh here, to ensure changes take effect as soon as possible.
|
|
|
|
|
*/
|
2020-01-03 13:41:35 +01:00
|
|
|
struct wl_window *win = data;
|
|
|
|
|
struct terminal *term = win->term;
|
|
|
|
|
|
2020-01-03 12:45:58 +01:00
|
|
|
if (term->width > 0 && term->height > 0)
|
|
|
|
|
render_refresh(term);
|
2019-10-27 19:08:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct xdg_surface_listener xdg_surface_listener = {
|
|
|
|
|
.configure = &xdg_surface_configure,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
xdg_toplevel_decoration_configure(void *data,
|
|
|
|
|
struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1,
|
|
|
|
|
uint32_t mode)
|
|
|
|
|
{
|
|
|
|
|
switch (mode) {
|
|
|
|
|
case ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE:
|
|
|
|
|
LOG_ERR("unimplemented: client-side decorations");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE:
|
|
|
|
|
LOG_DBG("using server-side decorations");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
LOG_ERR("unimplemented: unknown XDG toplevel decoration mode: %u", mode);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct zxdg_toplevel_decoration_v1_listener xdg_toplevel_decoration_listener = {
|
|
|
|
|
.configure = &xdg_toplevel_decoration_configure,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
|
|
|
|
|
{
|
|
|
|
|
LOG_WARN("global removed: %u", name);
|
|
|
|
|
assert(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct wl_registry_listener registry_listener = {
|
|
|
|
|
.global = &handle_global,
|
|
|
|
|
.global_remove = &handle_global_remove,
|
|
|
|
|
};
|
|
|
|
|
|
2019-10-27 19:11:35 +01:00
|
|
|
static bool
|
|
|
|
|
fdm_wayl(struct fdm *fdm, int fd, int events, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct wayland *wayl = data;
|
|
|
|
|
int event_count = wl_display_dispatch(wayl->display);
|
|
|
|
|
|
|
|
|
|
if (events & EPOLLHUP) {
|
2019-11-23 13:56:11 +01:00
|
|
|
LOG_WARN("disconnected from Wayland");
|
2019-10-27 19:11:35 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-29 22:09:56 +01:00
|
|
|
wl_display_flush(wayl->display);
|
2019-10-30 20:02:06 +01:00
|
|
|
return event_count != -1;
|
2019-10-27 19:11:35 +01:00
|
|
|
}
|
|
|
|
|
|
2019-10-27 19:16:12 +01:00
|
|
|
static bool
|
|
|
|
|
fdm_repeat(struct fdm *fdm, int fd, int events, void *data)
|
|
|
|
|
{
|
|
|
|
|
if (events & EPOLLHUP)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
struct wayland *wayl = data;
|
|
|
|
|
uint64_t expiration_count;
|
|
|
|
|
ssize_t ret = read(
|
|
|
|
|
wayl->kbd.repeat.fd, &expiration_count, sizeof(expiration_count));
|
|
|
|
|
|
|
|
|
|
if (ret < 0) {
|
2019-11-02 01:44:01 +01:00
|
|
|
if (errno == EAGAIN)
|
|
|
|
|
return true;
|
|
|
|
|
|
2019-10-27 19:16:12 +01:00
|
|
|
LOG_ERRNO("failed to read repeat key from repeat timer fd");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wayl->kbd.repeat.dont_re_repeat = true;
|
|
|
|
|
for (size_t i = 0; i < expiration_count; i++)
|
|
|
|
|
input_repeat(wayl, wayl->kbd.repeat.key);
|
|
|
|
|
wayl->kbd.repeat.dont_re_repeat = false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-27 19:08:48 +01:00
|
|
|
struct wayland *
|
2019-12-31 16:12:48 +01:00
|
|
|
wayl_init(const struct config *conf, struct fdm *fdm)
|
2019-10-27 19:08:48 +01:00
|
|
|
{
|
|
|
|
|
struct wayland *wayl = calloc(1, sizeof(*wayl));
|
2019-12-31 16:12:48 +01:00
|
|
|
wayl->conf = conf;
|
2019-10-27 19:08:48 +01:00
|
|
|
wayl->fdm = fdm;
|
2019-11-03 15:39:26 +01:00
|
|
|
wayl->kbd.repeat.fd = -1;
|
2019-10-27 19:08:48 +01:00
|
|
|
|
|
|
|
|
wayl->display = wl_display_connect(NULL);
|
|
|
|
|
if (wayl->display == NULL) {
|
|
|
|
|
LOG_ERR("failed to connect to wayland; no compositor running?");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wayl->registry = wl_display_get_registry(wayl->display);
|
|
|
|
|
if (wayl->registry == NULL) {
|
|
|
|
|
LOG_ERR("failed to get wayland registry");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wl_registry_add_listener(wayl->registry, ®istry_listener, wayl);
|
|
|
|
|
wl_display_roundtrip(wayl->display);
|
|
|
|
|
|
|
|
|
|
if (wayl->compositor == NULL) {
|
|
|
|
|
LOG_ERR("no compositor");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
2019-11-03 15:39:26 +01:00
|
|
|
if (wayl->sub_compositor == NULL) {
|
|
|
|
|
LOG_ERR("no sub compositor");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
2019-10-27 19:08:48 +01:00
|
|
|
if (wayl->shm == NULL) {
|
|
|
|
|
LOG_ERR("no shared memory buffers interface");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
if (wayl->shell == NULL) {
|
|
|
|
|
LOG_ERR("no XDG shell interface");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
if (wayl->seat == NULL) {
|
|
|
|
|
LOG_ERR("no seat available");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
if (wayl->data_device_manager == NULL) {
|
|
|
|
|
LOG_ERR("no clipboard available "
|
|
|
|
|
"(wl_data_device_manager not implemented by server)");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
2019-11-03 15:39:26 +01:00
|
|
|
if (!wayl->have_argb8888) {
|
|
|
|
|
LOG_ERR("compositor does not support ARGB surfaces");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-27 19:08:48 +01:00
|
|
|
if (wayl->primary_selection_device_manager == NULL)
|
|
|
|
|
LOG_WARN("no primary selection available");
|
|
|
|
|
|
2020-01-01 16:09:16 +01:00
|
|
|
if (conf->presentation_timings && wayl->presentation == NULL)
|
|
|
|
|
LOG_WARN("presentation time interface not implemented by compositor; "
|
|
|
|
|
"timings will not be available");
|
|
|
|
|
|
2019-10-27 19:08:48 +01:00
|
|
|
tll_foreach(wayl->monitors, it) {
|
2019-12-05 19:35:34 +01:00
|
|
|
LOG_INFO(
|
|
|
|
|
"%s: %dx%d+%dx%d@%dHz %s (%.2f\", PPI=%dx%d, scale=%d)",
|
|
|
|
|
it->item.name, it->item.width_px, it->item.height_px,
|
|
|
|
|
it->item.x, it->item.y, (int)round(it->item.refresh), it->item.model, it->item.inch,
|
|
|
|
|
it->item.x_ppi, it->item.y_ppi,
|
|
|
|
|
it->item.scale);
|
2019-10-27 19:08:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Clipboard */
|
|
|
|
|
wayl->data_device = wl_data_device_manager_get_data_device(
|
|
|
|
|
wayl->data_device_manager, wayl->seat);
|
|
|
|
|
wl_data_device_add_listener(wayl->data_device, &data_device_listener, wayl);
|
|
|
|
|
|
|
|
|
|
/* Primary selection */
|
|
|
|
|
if (wayl->primary_selection_device_manager != NULL) {
|
|
|
|
|
wayl->primary_selection_device = zwp_primary_selection_device_manager_v1_get_device(
|
|
|
|
|
wayl->primary_selection_device_manager, wayl->seat);
|
|
|
|
|
zwp_primary_selection_device_v1_add_listener(
|
|
|
|
|
wayl->primary_selection_device, &primary_selection_device_listener, wayl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Cursor */
|
|
|
|
|
unsigned cursor_size = 24;
|
|
|
|
|
const char *cursor_theme = getenv("XCURSOR_THEME");
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
const char *env_cursor_size = getenv("XCURSOR_SIZE");
|
|
|
|
|
if (env_cursor_size != NULL) {
|
|
|
|
|
unsigned size;
|
|
|
|
|
if (sscanf(env_cursor_size, "%u", &size) == 1)
|
|
|
|
|
cursor_size = size;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Note: theme is (re)loaded on scale and output changes */
|
|
|
|
|
LOG_INFO("cursor theme: %s, size: %u", cursor_theme, cursor_size);
|
|
|
|
|
wayl->pointer.size = cursor_size;
|
|
|
|
|
wayl->pointer.theme_name = cursor_theme != NULL ? strdup(cursor_theme) : NULL;
|
|
|
|
|
|
|
|
|
|
wayl->pointer.surface = wl_compositor_create_surface(wayl->compositor);
|
|
|
|
|
if (wayl->pointer.surface == NULL) {
|
|
|
|
|
LOG_ERR("failed to create cursor surface");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-01 20:02:02 +01:00
|
|
|
/* All wayland initialization done - make it so */
|
|
|
|
|
wl_display_roundtrip(wayl->display);
|
|
|
|
|
|
2019-11-02 01:14:40 +01:00
|
|
|
wayl->kbd.repeat.fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK);
|
2019-10-27 19:08:48 +01:00
|
|
|
if (wayl->kbd.repeat.fd == -1) {
|
|
|
|
|
LOG_ERRNO("failed to create keyboard repeat timer FD");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-03 00:25:17 +01:00
|
|
|
if (!fdm_add(fdm, wl_display_get_fd(wayl->display), EPOLLIN, &fdm_wayl, wayl) ||
|
|
|
|
|
!fdm_add(fdm, wayl->kbd.repeat.fd, EPOLLIN, &fdm_repeat, wayl))
|
|
|
|
|
{
|
2019-10-27 19:16:12 +01:00
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-27 19:08:48 +01:00
|
|
|
return wayl;
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
if (wayl != NULL)
|
|
|
|
|
wayl_destroy(wayl);
|
|
|
|
|
return NULL;
|
2019-10-27 15:57:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
wayl_destroy(struct wayland *wayl)
|
|
|
|
|
{
|
2019-10-27 19:16:25 +01:00
|
|
|
if (wayl == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
2019-11-01 20:25:08 +01:00
|
|
|
tll_foreach(wayl->terms, it) {
|
|
|
|
|
static bool have_warned = false;
|
|
|
|
|
if (!have_warned) {
|
|
|
|
|
have_warned = true;
|
|
|
|
|
LOG_WARN("there are terminals still running");
|
|
|
|
|
term_destroy(it->item);
|
|
|
|
|
}
|
2019-10-27 19:16:12 +01:00
|
|
|
}
|
|
|
|
|
|
2019-11-01 20:25:08 +01:00
|
|
|
tll_free(wayl->terms);
|
|
|
|
|
|
2019-11-03 15:39:26 +01:00
|
|
|
if (wayl->kbd.repeat.fd != -1)
|
|
|
|
|
fdm_del(wayl->fdm, wayl->kbd.repeat.fd);
|
2019-11-01 19:59:39 +01:00
|
|
|
|
2019-10-27 15:57:23 +01:00
|
|
|
tll_foreach(wayl->monitors, it) {
|
|
|
|
|
free(it->item.name);
|
|
|
|
|
if (it->item.xdg != NULL)
|
|
|
|
|
zxdg_output_v1_destroy(it->item.xdg);
|
|
|
|
|
if (it->item.output != NULL)
|
|
|
|
|
wl_output_destroy(it->item.output);
|
2019-12-05 19:35:34 +01:00
|
|
|
free(it->item.make);
|
|
|
|
|
free(it->item.model);
|
2019-10-27 15:57:23 +01:00
|
|
|
tll_remove(wayl->monitors, it);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (wayl->xdg_output_manager != NULL)
|
|
|
|
|
zxdg_output_manager_v1_destroy(wayl->xdg_output_manager);
|
2019-10-27 18:43:07 +01:00
|
|
|
if (wayl->shell != NULL)
|
|
|
|
|
xdg_wm_base_destroy(wayl->shell);
|
|
|
|
|
|
|
|
|
|
if (wayl->xdg_decoration_manager != NULL)
|
|
|
|
|
zxdg_decoration_manager_v1_destroy(wayl->xdg_decoration_manager);
|
2019-10-27 15:57:23 +01:00
|
|
|
|
2019-12-31 15:39:40 +01:00
|
|
|
if (wayl->presentation != NULL)
|
|
|
|
|
wp_presentation_destroy(wayl->presentation);
|
|
|
|
|
|
2019-10-27 17:07:44 +01:00
|
|
|
if (wayl->kbd.xkb_compose_state != NULL)
|
|
|
|
|
xkb_compose_state_unref(wayl->kbd.xkb_compose_state);
|
|
|
|
|
if (wayl->kbd.xkb_compose_table != NULL)
|
|
|
|
|
xkb_compose_table_unref(wayl->kbd.xkb_compose_table);
|
|
|
|
|
if (wayl->kbd.xkb_keymap != NULL)
|
|
|
|
|
xkb_keymap_unref(wayl->kbd.xkb_keymap);
|
|
|
|
|
if (wayl->kbd.xkb_state != NULL)
|
|
|
|
|
xkb_state_unref(wayl->kbd.xkb_state);
|
|
|
|
|
if (wayl->kbd.xkb != NULL)
|
|
|
|
|
xkb_context_unref(wayl->kbd.xkb);
|
|
|
|
|
|
2019-10-27 16:15:32 +01:00
|
|
|
if (wayl->clipboard.data_source != NULL)
|
|
|
|
|
wl_data_source_destroy(wayl->clipboard.data_source);
|
|
|
|
|
if (wayl->clipboard.data_offer != NULL)
|
|
|
|
|
wl_data_offer_destroy(wayl->clipboard.data_offer);
|
|
|
|
|
free(wayl->clipboard.text);
|
|
|
|
|
if (wayl->primary.data_source != NULL)
|
|
|
|
|
zwp_primary_selection_source_v1_destroy(wayl->primary.data_source);
|
|
|
|
|
if (wayl->primary.data_offer != NULL)
|
|
|
|
|
zwp_primary_selection_offer_v1_destroy(wayl->primary.data_offer);
|
|
|
|
|
free(wayl->primary.text);
|
|
|
|
|
|
2019-10-27 15:57:23 +01:00
|
|
|
free(wayl->pointer.theme_name);
|
|
|
|
|
if (wayl->pointer.theme != NULL)
|
|
|
|
|
wl_cursor_theme_destroy(wayl->pointer.theme);
|
|
|
|
|
if (wayl->pointer.pointer != NULL)
|
|
|
|
|
wl_pointer_destroy(wayl->pointer.pointer);
|
|
|
|
|
if (wayl->pointer.surface != NULL)
|
|
|
|
|
wl_surface_destroy(wayl->pointer.surface);
|
|
|
|
|
if (wayl->keyboard != NULL)
|
|
|
|
|
wl_keyboard_destroy(wayl->keyboard);
|
|
|
|
|
if (wayl->data_device != NULL)
|
|
|
|
|
wl_data_device_destroy(wayl->data_device);
|
|
|
|
|
if (wayl->data_device_manager != NULL)
|
|
|
|
|
wl_data_device_manager_destroy(wayl->data_device_manager);
|
|
|
|
|
if (wayl->primary_selection_device != NULL)
|
|
|
|
|
zwp_primary_selection_device_v1_destroy(wayl->primary_selection_device);
|
|
|
|
|
if (wayl->primary_selection_device_manager != NULL)
|
|
|
|
|
zwp_primary_selection_device_manager_v1_destroy(wayl->primary_selection_device_manager);
|
|
|
|
|
if (wayl->seat != NULL)
|
|
|
|
|
wl_seat_destroy(wayl->seat);
|
|
|
|
|
if (wayl->shm != NULL)
|
|
|
|
|
wl_shm_destroy(wayl->shm);
|
|
|
|
|
if (wayl->sub_compositor != NULL)
|
|
|
|
|
wl_subcompositor_destroy(wayl->sub_compositor);
|
|
|
|
|
if (wayl->compositor != NULL)
|
|
|
|
|
wl_compositor_destroy(wayl->compositor);
|
|
|
|
|
if (wayl->registry != NULL)
|
|
|
|
|
wl_registry_destroy(wayl->registry);
|
2019-10-27 19:11:35 +01:00
|
|
|
if (wayl->display != NULL) {
|
2019-11-01 19:59:39 +01:00
|
|
|
fdm_del_no_close(wayl->fdm, wl_display_get_fd(wayl->display));
|
2019-10-27 15:57:23 +01:00
|
|
|
wl_display_disconnect(wayl->display);
|
2019-10-27 19:11:35 +01:00
|
|
|
}
|
2019-10-27 19:16:42 +01:00
|
|
|
|
|
|
|
|
free(wayl);
|
2019-10-27 15:57:23 +01:00
|
|
|
}
|
2019-10-27 16:01:03 +01:00
|
|
|
|
2019-10-27 19:08:48 +01:00
|
|
|
struct wl_window *
|
2020-01-03 13:37:03 +01:00
|
|
|
wayl_win_init(struct terminal *term)
|
2019-10-27 19:08:48 +01:00
|
|
|
{
|
2020-01-03 13:37:03 +01:00
|
|
|
struct wayland *wayl = term->wl;
|
|
|
|
|
|
2019-10-27 19:08:48 +01:00
|
|
|
struct wl_window *win = calloc(1, sizeof(*win));
|
2020-01-03 13:37:03 +01:00
|
|
|
win->term = term;
|
2019-10-27 19:08:48 +01:00
|
|
|
|
|
|
|
|
win->surface = wl_compositor_create_surface(wayl->compositor);
|
|
|
|
|
if (win->surface == NULL) {
|
|
|
|
|
LOG_ERR("failed to create wayland surface");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-03 13:40:37 +01:00
|
|
|
wl_surface_add_listener(win->surface, &surface_listener, win);
|
2019-10-27 19:08:48 +01:00
|
|
|
|
|
|
|
|
win->xdg_surface = xdg_wm_base_get_xdg_surface(wayl->shell, win->surface);
|
2020-01-03 13:41:35 +01:00
|
|
|
xdg_surface_add_listener(win->xdg_surface, &xdg_surface_listener, win);
|
2019-10-27 19:08:48 +01:00
|
|
|
|
|
|
|
|
win->xdg_toplevel = xdg_surface_get_toplevel(win->xdg_surface);
|
2020-01-03 13:46:15 +01:00
|
|
|
xdg_toplevel_add_listener(win->xdg_toplevel, &xdg_toplevel_listener, win);
|
2019-10-27 19:08:48 +01:00
|
|
|
|
|
|
|
|
xdg_toplevel_set_app_id(win->xdg_toplevel, "foot");
|
|
|
|
|
|
|
|
|
|
/* Request server-side decorations */
|
|
|
|
|
win->xdg_toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
|
|
|
|
|
wayl->xdg_decoration_manager, win->xdg_toplevel);
|
|
|
|
|
zxdg_toplevel_decoration_v1_set_mode(
|
|
|
|
|
win->xdg_toplevel_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
|
|
|
|
|
zxdg_toplevel_decoration_v1_add_listener(
|
2020-01-03 13:46:15 +01:00
|
|
|
win->xdg_toplevel_decoration, &xdg_toplevel_decoration_listener, win);
|
2019-10-27 19:08:48 +01:00
|
|
|
|
|
|
|
|
/* Scrollback search box */
|
|
|
|
|
win->search_surface = wl_compositor_create_surface(wayl->compositor);
|
|
|
|
|
win->search_sub_surface = wl_subcompositor_get_subsurface(
|
|
|
|
|
wayl->sub_compositor, win->search_surface, win->surface);
|
|
|
|
|
wl_subsurface_set_desync(win->search_sub_surface);
|
|
|
|
|
|
|
|
|
|
wl_surface_commit(win->surface);
|
|
|
|
|
return win;
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
if (win != NULL)
|
|
|
|
|
wayl_win_destroy(win);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-27 16:01:03 +01:00
|
|
|
void
|
|
|
|
|
wayl_win_destroy(struct wl_window *win)
|
|
|
|
|
{
|
2019-10-30 20:25:16 +01:00
|
|
|
if (win == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
2019-11-01 20:19:53 +01:00
|
|
|
/*
|
|
|
|
|
* First, unmap all surfaces to trigger things like
|
|
|
|
|
* keyboard_leave() and wl_pointer_leave().
|
|
|
|
|
*
|
|
|
|
|
* This ensures we remove all references to *this* window from the
|
|
|
|
|
* global wayland struct (since it no longer has neither keyboard
|
|
|
|
|
* nor mouse focus).
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* Scrollback search */
|
|
|
|
|
wl_surface_attach(win->search_surface, NULL, 0, 0);
|
|
|
|
|
wl_surface_commit(win->search_surface);
|
2020-01-03 13:37:03 +01:00
|
|
|
wl_display_roundtrip(win->term->wl->display);
|
2019-11-01 20:19:53 +01:00
|
|
|
|
|
|
|
|
/* Main window */
|
|
|
|
|
wl_surface_attach(win->surface, NULL, 0, 0);
|
|
|
|
|
wl_surface_commit(win->surface);
|
2020-01-03 13:37:03 +01:00
|
|
|
wl_display_roundtrip(win->term->wl->display);
|
2019-11-01 20:19:53 +01:00
|
|
|
|
2019-10-27 16:01:03 +01:00
|
|
|
tll_free(win->on_outputs);
|
|
|
|
|
if (win->search_sub_surface != NULL)
|
|
|
|
|
wl_subsurface_destroy(win->search_sub_surface);
|
|
|
|
|
if (win->search_surface != NULL)
|
|
|
|
|
wl_surface_destroy(win->search_surface);
|
|
|
|
|
if (win->frame_callback != NULL)
|
|
|
|
|
wl_callback_destroy(win->frame_callback);
|
|
|
|
|
if (win->xdg_toplevel_decoration != NULL)
|
|
|
|
|
zxdg_toplevel_decoration_v1_destroy(win->xdg_toplevel_decoration);
|
|
|
|
|
if (win->xdg_toplevel != NULL)
|
|
|
|
|
xdg_toplevel_destroy(win->xdg_toplevel);
|
|
|
|
|
if (win->xdg_surface != NULL)
|
|
|
|
|
xdg_surface_destroy(win->xdg_surface);
|
|
|
|
|
if (win->surface != NULL)
|
|
|
|
|
wl_surface_destroy(win->surface);
|
2019-11-01 20:19:53 +01:00
|
|
|
|
2020-01-03 13:37:03 +01:00
|
|
|
wl_display_roundtrip(win->term->wl->display);
|
2019-11-01 20:45:57 +01:00
|
|
|
|
2019-10-27 19:16:42 +01:00
|
|
|
free(win);
|
2019-10-27 16:01:03 +01:00
|
|
|
}
|
2019-10-27 18:43:07 +01:00
|
|
|
|
2019-10-27 19:36:45 +01:00
|
|
|
bool
|
2019-11-28 19:35:47 +01:00
|
|
|
wayl_cursor_set(struct wayland *wayl, const struct terminal *term)
|
2019-10-27 19:36:45 +01:00
|
|
|
{
|
2019-11-28 19:35:47 +01:00
|
|
|
if (wayl->pointer.theme == NULL)
|
|
|
|
|
return false;
|
2019-10-27 19:36:45 +01:00
|
|
|
|
2020-01-02 15:58:52 +01:00
|
|
|
if (wayl->mouse_focus == NULL) {
|
2019-11-30 12:09:57 +01:00
|
|
|
wayl->pointer.xcursor = NULL;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-02 15:58:52 +01:00
|
|
|
if (wayl->mouse_focus != term) {
|
2019-11-28 19:35:47 +01:00
|
|
|
/* This terminal doesn't have mouse focus */
|
|
|
|
|
return true;
|
2019-10-27 19:36:45 +01:00
|
|
|
}
|
|
|
|
|
|
2019-11-30 12:09:57 +01:00
|
|
|
if (wayl->pointer.xcursor == term->xcursor)
|
|
|
|
|
return true;
|
|
|
|
|
|
2019-11-28 19:35:47 +01:00
|
|
|
wayl->pointer.cursor = wl_cursor_theme_get_cursor(wayl->pointer.theme, term->xcursor);
|
|
|
|
|
if (wayl->pointer.cursor == NULL) {
|
|
|
|
|
LOG_ERR("%s: failed to load xcursor pointer '%s'",
|
|
|
|
|
wayl->pointer.theme_name, term->xcursor);
|
2019-10-27 19:36:45 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-30 12:09:57 +01:00
|
|
|
wayl->pointer.xcursor = term->xcursor;
|
|
|
|
|
|
2019-10-27 19:36:45 +01:00
|
|
|
const int scale = term->scale;
|
|
|
|
|
struct wl_cursor_image *image = wayl->pointer.cursor->images[0];
|
|
|
|
|
|
|
|
|
|
wl_surface_attach(
|
|
|
|
|
wayl->pointer.surface, wl_cursor_image_get_buffer(image), 0, 0);
|
|
|
|
|
|
|
|
|
|
wl_pointer_set_cursor(
|
|
|
|
|
wayl->pointer.pointer, wayl->pointer.serial,
|
|
|
|
|
wayl->pointer.surface,
|
|
|
|
|
image->hotspot_x / scale, image->hotspot_y / scale);
|
|
|
|
|
|
|
|
|
|
wl_surface_damage_buffer(
|
|
|
|
|
wayl->pointer.surface, 0, 0, INT32_MAX, INT32_MAX);
|
|
|
|
|
|
2019-11-28 19:35:47 +01:00
|
|
|
wl_surface_set_buffer_scale(wayl->pointer.surface, scale);
|
2019-10-27 19:36:45 +01:00
|
|
|
wl_surface_commit(wayl->pointer.surface);
|
2019-11-28 19:35:47 +01:00
|
|
|
return true;
|
2019-10-27 19:36:45 +01:00
|
|
|
}
|
|
|
|
|
|
2019-11-28 19:35:47 +01:00
|
|
|
static bool
|
|
|
|
|
wayl_reload_cursor_theme(struct wayland *wayl, const struct terminal *term)
|
|
|
|
|
{
|
|
|
|
|
if (wayl->pointer.size == 0)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
if (wayl->pointer.theme != NULL) {
|
|
|
|
|
wl_cursor_theme_destroy(wayl->pointer.theme);
|
|
|
|
|
wayl->pointer.theme = NULL;
|
|
|
|
|
wayl->pointer.cursor = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG_DBG("reloading cursor theme: %s@%d",
|
|
|
|
|
wayl->pointer.theme_name, wayl->pointer.size);
|
|
|
|
|
|
|
|
|
|
wayl->pointer.theme = wl_cursor_theme_load(
|
|
|
|
|
wayl->pointer.theme_name, wayl->pointer.size * term->scale, wayl->shm);
|
|
|
|
|
|
|
|
|
|
if (wayl->pointer.theme == NULL) {
|
|
|
|
|
LOG_ERR("failed to load cursor theme");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return wayl_cursor_set(wayl, term);
|
|
|
|
|
}
|
2019-10-27 19:36:45 +01:00
|
|
|
|
2019-10-27 18:43:07 +01:00
|
|
|
struct terminal *
|
|
|
|
|
wayl_terminal_from_surface(struct wayland *wayl, struct wl_surface *surface)
|
|
|
|
|
{
|
2019-10-30 20:02:06 +01:00
|
|
|
tll_foreach(wayl->terms, it) {
|
2019-11-29 22:10:28 +01:00
|
|
|
if (it->item->window->surface == surface ||
|
|
|
|
|
it->item->window->search_surface == surface)
|
|
|
|
|
{
|
2019-10-30 20:02:06 +01:00
|
|
|
return it->item;
|
2019-11-29 22:10:28 +01:00
|
|
|
}
|
2019-10-30 20:02:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(false);
|
2019-11-29 22:10:28 +01:00
|
|
|
LOG_WARN("surface %p doesn't map to a terminal", surface);
|
2019-10-30 20:02:06 +01:00
|
|
|
return NULL;
|
2019-10-27 18:43:07 +01:00
|
|
|
}
|