mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-15 04:27:52 -05:00
Merge branch 'fdm'
This commit is contained in:
commit
ad0f8a02d5
17 changed files with 1945 additions and 1483 deletions
124
fdm.c
Normal file
124
fdm.c
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
#include "fdm.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <sys/epoll.h>
|
||||
|
||||
#define LOG_MODULE "fdm"
|
||||
#define LOG_ENABLE_DBG 0
|
||||
#include "log.h"
|
||||
#include "tllist.h"
|
||||
|
||||
struct fd {
|
||||
int fd;
|
||||
void *data;
|
||||
fdm_handler_t handler;
|
||||
};
|
||||
|
||||
struct fdm {
|
||||
int epoll_fd;
|
||||
tll(struct fd) fds;
|
||||
};
|
||||
|
||||
struct fdm *
|
||||
fdm_init(void)
|
||||
{
|
||||
int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
|
||||
if (epoll_fd == -1) {
|
||||
LOG_ERRNO("failed to create epoll FD");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct fdm *fdm = malloc(sizeof(*fdm));
|
||||
*fdm = (struct fdm){
|
||||
.epoll_fd = epoll_fd,
|
||||
.fds = tll_init(),
|
||||
};
|
||||
return fdm;
|
||||
}
|
||||
|
||||
void
|
||||
fdm_destroy(struct fdm *fdm)
|
||||
{
|
||||
if (fdm == NULL)
|
||||
return;
|
||||
|
||||
if (tll_length(fdm->fds) > 0)
|
||||
LOG_WARN("FD list not empty");
|
||||
|
||||
assert(tll_length(fdm->fds) == 0);
|
||||
|
||||
tll_free(fdm->fds);
|
||||
close(fdm->epoll_fd);
|
||||
free(fdm);
|
||||
}
|
||||
|
||||
bool
|
||||
fdm_add(struct fdm *fdm, int fd, int events, fdm_handler_t handler, void *data)
|
||||
{
|
||||
#if defined(_DEBUG)
|
||||
tll_foreach(fdm->fds, it) {
|
||||
if (it->item.fd == fd) {
|
||||
LOG_ERR("FD=%d already registered", fd);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
tll_push_back(
|
||||
fdm->fds, ((struct fd){.fd = fd, .data = data, .handler = handler}));
|
||||
|
||||
struct epoll_event ev = {
|
||||
.events = events,
|
||||
.data = {.ptr = &tll_back(fdm->fds)},
|
||||
};
|
||||
|
||||
if (epoll_ctl(fdm->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
|
||||
LOG_ERRNO("failed to register FD with epoll");
|
||||
tll_pop_back(fdm->fds);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
fdm_del(struct fdm *fdm, int fd)
|
||||
{
|
||||
tll_foreach(fdm->fds, it) {
|
||||
if (it->item.fd == fd) {
|
||||
if (epoll_ctl(fdm->epoll_fd, EPOLL_CTL_DEL, fd, NULL) < 0)
|
||||
LOG_ERRNO("failed to unregister FD=%d from epoll", fd);
|
||||
|
||||
tll_remove(fdm->fds, it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_ERR("no such FD: %d", fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
fdm_poll(struct fdm *fdm)
|
||||
{
|
||||
struct epoll_event events[tll_length(fdm->fds)];
|
||||
int ret = epoll_wait(fdm->epoll_fd, events, tll_length(fdm->fds), -1);
|
||||
if (ret == -1) {
|
||||
if (errno == EINTR)
|
||||
return true;
|
||||
|
||||
LOG_ERRNO("failed to epoll");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < ret; i++) {
|
||||
struct fd *fd = events[i].data.ptr;
|
||||
if (!fd->handler(fdm, fd->fd, events[i].events, fd->data))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
15
fdm.h
Normal file
15
fdm.h
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
struct fdm;
|
||||
|
||||
typedef bool (*fdm_handler_t)(struct fdm *fdm, int fd, int events, void *data);
|
||||
|
||||
struct fdm *fdm_init(void);
|
||||
void fdm_destroy(struct fdm *fdm);
|
||||
|
||||
bool fdm_add(struct fdm *fdm, int fd, int events, fdm_handler_t handler, void *data);
|
||||
bool fdm_del(struct fdm *fdm, int fd);
|
||||
|
||||
bool fdm_poll(struct fdm *fdm);
|
||||
264
input.c
264
input.c
|
|
@ -30,50 +30,50 @@ static void
|
|||
keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
|
||||
uint32_t format, int32_t fd, uint32_t size)
|
||||
{
|
||||
struct terminal *term = data;
|
||||
struct wayland *wayl = data;
|
||||
|
||||
char *map_str = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
|
||||
/* TODO: free old context + keymap */
|
||||
if (term->kbd.xkb_compose_state != NULL) {
|
||||
xkb_compose_state_unref(term->kbd.xkb_compose_state);
|
||||
term->kbd.xkb_compose_state = NULL;
|
||||
if (wayl->kbd.xkb_compose_state != NULL) {
|
||||
xkb_compose_state_unref(wayl->kbd.xkb_compose_state);
|
||||
wayl->kbd.xkb_compose_state = NULL;
|
||||
}
|
||||
if (term->kbd.xkb_compose_table != NULL) {
|
||||
xkb_compose_table_unref(term->kbd.xkb_compose_table);
|
||||
term->kbd.xkb_compose_table = NULL;
|
||||
if (wayl->kbd.xkb_compose_table != NULL) {
|
||||
xkb_compose_table_unref(wayl->kbd.xkb_compose_table);
|
||||
wayl->kbd.xkb_compose_table = NULL;
|
||||
}
|
||||
if (term->kbd.xkb_keymap != NULL) {
|
||||
xkb_keymap_unref(term->kbd.xkb_keymap);
|
||||
term->kbd.xkb_keymap = NULL;
|
||||
if (wayl->kbd.xkb_keymap != NULL) {
|
||||
xkb_keymap_unref(wayl->kbd.xkb_keymap);
|
||||
wayl->kbd.xkb_keymap = NULL;
|
||||
}
|
||||
if (term->kbd.xkb_state != NULL) {
|
||||
xkb_state_unref(term->kbd.xkb_state);
|
||||
term->kbd.xkb_state = NULL;
|
||||
if (wayl->kbd.xkb_state != NULL) {
|
||||
xkb_state_unref(wayl->kbd.xkb_state);
|
||||
wayl->kbd.xkb_state = NULL;
|
||||
}
|
||||
if (term->kbd.xkb != NULL) {
|
||||
xkb_context_unref(term->kbd.xkb);
|
||||
term->kbd.xkb = NULL;
|
||||
if (wayl->kbd.xkb != NULL) {
|
||||
xkb_context_unref(wayl->kbd.xkb);
|
||||
wayl->kbd.xkb = NULL;
|
||||
}
|
||||
|
||||
term->kbd.xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||
term->kbd.xkb_keymap = xkb_keymap_new_from_string(
|
||||
term->kbd.xkb, map_str, XKB_KEYMAP_FORMAT_TEXT_V1,
|
||||
wayl->kbd.xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||
wayl->kbd.xkb_keymap = xkb_keymap_new_from_string(
|
||||
wayl->kbd.xkb, map_str, XKB_KEYMAP_FORMAT_TEXT_V1,
|
||||
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
|
||||
/* TODO: initialize in enter? */
|
||||
term->kbd.xkb_state = xkb_state_new(term->kbd.xkb_keymap);
|
||||
wayl->kbd.xkb_state = xkb_state_new(wayl->kbd.xkb_keymap);
|
||||
|
||||
term->kbd.mod_shift = xkb_keymap_mod_get_index(term->kbd.xkb_keymap, "Shift");
|
||||
term->kbd.mod_alt = xkb_keymap_mod_get_index(term->kbd.xkb_keymap, "Mod1") ;
|
||||
term->kbd.mod_ctrl = xkb_keymap_mod_get_index(term->kbd.xkb_keymap, "Control");
|
||||
term->kbd.mod_meta = xkb_keymap_mod_get_index(term->kbd.xkb_keymap, "Mod4");
|
||||
wayl->kbd.mod_shift = xkb_keymap_mod_get_index(wayl->kbd.xkb_keymap, "Shift");
|
||||
wayl->kbd.mod_alt = xkb_keymap_mod_get_index(wayl->kbd.xkb_keymap, "Mod1") ;
|
||||
wayl->kbd.mod_ctrl = xkb_keymap_mod_get_index(wayl->kbd.xkb_keymap, "Control");
|
||||
wayl->kbd.mod_meta = xkb_keymap_mod_get_index(wayl->kbd.xkb_keymap, "Mod4");
|
||||
|
||||
/* Compose (dead keys) */
|
||||
term->kbd.xkb_compose_table = xkb_compose_table_new_from_locale(
|
||||
term->kbd.xkb, setlocale(LC_CTYPE, NULL), XKB_COMPOSE_COMPILE_NO_FLAGS);
|
||||
term->kbd.xkb_compose_state = xkb_compose_state_new(
|
||||
term->kbd.xkb_compose_table, XKB_COMPOSE_STATE_NO_FLAGS);
|
||||
wayl->kbd.xkb_compose_table = xkb_compose_table_new_from_locale(
|
||||
wayl->kbd.xkb, setlocale(LC_CTYPE, NULL), XKB_COMPOSE_COMPILE_NO_FLAGS);
|
||||
wayl->kbd.xkb_compose_state = xkb_compose_state_new(
|
||||
wayl->kbd.xkb_compose_table, XKB_COMPOSE_STATE_NO_FLAGS);
|
||||
|
||||
munmap(map_str, size);
|
||||
close(fd);
|
||||
|
|
@ -83,20 +83,22 @@ static void
|
|||
keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
|
||||
struct wl_surface *surface, struct wl_array *keys)
|
||||
{
|
||||
struct terminal *term = data;
|
||||
term->input_serial = serial;
|
||||
term_focus_in(term);
|
||||
struct wayland *wayl = data;
|
||||
wayl->input_serial = serial;
|
||||
wayl->focused = wayl_terminal_from_surface(wayl, surface);
|
||||
assert(wayl->focused != NULL);
|
||||
term_focus_in(wayl->focused);
|
||||
}
|
||||
|
||||
static bool
|
||||
start_repeater(struct terminal *term, uint32_t key)
|
||||
start_repeater(struct wayland *wayl, uint32_t key)
|
||||
{
|
||||
if (term->kbd.repeat.dont_re_repeat)
|
||||
if (wayl->kbd.repeat.dont_re_repeat)
|
||||
return true;
|
||||
|
||||
struct itimerspec t = {
|
||||
.it_value = {.tv_sec = 0, .tv_nsec = term->kbd.repeat.delay * 1000000},
|
||||
.it_interval = {.tv_sec = 0, .tv_nsec = 1000000000 / term->kbd.repeat.rate},
|
||||
.it_value = {.tv_sec = 0, .tv_nsec = wayl->kbd.repeat.delay * 1000000},
|
||||
.it_interval = {.tv_sec = 0, .tv_nsec = 1000000000 / wayl->kbd.repeat.rate},
|
||||
};
|
||||
|
||||
if (t.it_value.tv_nsec >= 1000000000) {
|
||||
|
|
@ -107,22 +109,22 @@ start_repeater(struct terminal *term, uint32_t key)
|
|||
t.it_interval.tv_sec += t.it_interval.tv_nsec / 1000000000;
|
||||
t.it_interval.tv_nsec %= 1000000000;
|
||||
}
|
||||
if (timerfd_settime(term->kbd.repeat.fd, 0, &t, NULL) < 0) {
|
||||
if (timerfd_settime(wayl->kbd.repeat.fd, 0, &t, NULL) < 0) {
|
||||
LOG_ERRNO("failed to arm keyboard repeat timer");
|
||||
return false;
|
||||
}
|
||||
|
||||
term->kbd.repeat.key = key;
|
||||
wayl->kbd.repeat.key = key;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
stop_repeater(struct terminal *term, uint32_t key)
|
||||
stop_repeater(struct wayland *wayl, uint32_t key)
|
||||
{
|
||||
if (key != -1 && key != term->kbd.repeat.key)
|
||||
if (key != -1 && key != wayl->kbd.repeat.key)
|
||||
return true;
|
||||
|
||||
if (timerfd_settime(term->kbd.repeat.fd, 0, &(struct itimerspec){{0}}, NULL) < 0) {
|
||||
if (timerfd_settime(wayl->kbd.repeat.fd, 0, &(struct itimerspec){{0}}, NULL) < 0) {
|
||||
LOG_ERRNO("failed to disarm keyboard repeat timer");
|
||||
return false;
|
||||
}
|
||||
|
|
@ -134,30 +136,33 @@ static void
|
|||
keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
|
||||
struct wl_surface *surface)
|
||||
{
|
||||
struct terminal *term = data;
|
||||
struct wayland *wayl = data;
|
||||
|
||||
stop_repeater(term, -1);
|
||||
term_focus_out(term);
|
||||
stop_repeater(wayl, -1);
|
||||
term_focus_out(wayl->focused);
|
||||
wayl->focused = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
|
||||
uint32_t time, uint32_t key, uint32_t state)
|
||||
{
|
||||
struct terminal *term = data;
|
||||
struct wayland *wayl = data;
|
||||
struct terminal *term = wayl->focused;
|
||||
assert(term != NULL);
|
||||
|
||||
const xkb_mod_mask_t ctrl = 1 << term->kbd.mod_ctrl;
|
||||
const xkb_mod_mask_t alt = 1 << term->kbd.mod_alt;
|
||||
const xkb_mod_mask_t shift = 1 << term->kbd.mod_shift;
|
||||
const xkb_mod_mask_t meta = 1 << term->kbd.mod_meta;
|
||||
const xkb_mod_mask_t ctrl = 1 << wayl->kbd.mod_ctrl;
|
||||
const xkb_mod_mask_t alt = 1 << wayl->kbd.mod_alt;
|
||||
const xkb_mod_mask_t shift = 1 << wayl->kbd.mod_shift;
|
||||
const xkb_mod_mask_t meta = 1 << wayl->kbd.mod_meta;
|
||||
|
||||
if (state == XKB_KEY_UP) {
|
||||
stop_repeater(term, key);
|
||||
stop_repeater(wayl, key);
|
||||
return;
|
||||
}
|
||||
|
||||
key += 8;
|
||||
xkb_keysym_t sym = xkb_state_key_get_one_sym(term->kbd.xkb_state, key);
|
||||
xkb_keysym_t sym = xkb_state_key_get_one_sym(wayl->kbd.xkb_state, key);
|
||||
|
||||
#if 0
|
||||
char foo[100];
|
||||
|
|
@ -165,22 +170,22 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
|
|||
LOG_INFO("%s", foo);
|
||||
#endif
|
||||
|
||||
xkb_compose_state_feed(term->kbd.xkb_compose_state, sym);
|
||||
xkb_compose_state_feed(wayl->kbd.xkb_compose_state, sym);
|
||||
enum xkb_compose_status compose_status = xkb_compose_state_get_status(
|
||||
term->kbd.xkb_compose_state);
|
||||
wayl->kbd.xkb_compose_state);
|
||||
|
||||
if (compose_status == XKB_COMPOSE_COMPOSING)
|
||||
return;
|
||||
|
||||
xkb_mod_mask_t mods = xkb_state_serialize_mods(
|
||||
term->kbd.xkb_state, XKB_STATE_MODS_DEPRESSED);
|
||||
//xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods(term->kbd.xkb_state, key);
|
||||
wayl->kbd.xkb_state, XKB_STATE_MODS_DEPRESSED);
|
||||
//xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods(wayl->kbd.xkb_state, key);
|
||||
xkb_mod_mask_t consumed = 0x0;
|
||||
xkb_mod_mask_t significant = ctrl | alt | shift | meta;
|
||||
xkb_mod_mask_t effective_mods = mods & ~consumed & significant;
|
||||
|
||||
if (term->is_searching) {
|
||||
start_repeater(term, key - 8);
|
||||
start_repeater(wayl, key - 8);
|
||||
search_input(term, key, sym, effective_mods);
|
||||
return;
|
||||
}
|
||||
|
|
@ -188,7 +193,7 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
|
|||
#if 0
|
||||
for (size_t i = 0; i < 32; i++) {
|
||||
if (mods & (1 << i)) {
|
||||
LOG_INFO("%s", xkb_keymap_mod_get_name(term->kbd.xkb_keymap, i));
|
||||
LOG_INFO("%s", xkb_keymap_mod_get_name(wayl->kbd.xkb_keymap, i));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -200,10 +205,10 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
|
|||
bool found_map = false;
|
||||
|
||||
enum modifier keymap_mods = MOD_NONE;
|
||||
keymap_mods |= term->kbd.shift ? MOD_SHIFT : MOD_NONE;
|
||||
keymap_mods |= term->kbd.alt ? MOD_ALT : MOD_NONE;
|
||||
keymap_mods |= term->kbd.ctrl ? MOD_CTRL : MOD_NONE;
|
||||
keymap_mods |= term->kbd.meta ? MOD_META : MOD_NONE;
|
||||
keymap_mods |= wayl->kbd.shift ? MOD_SHIFT : MOD_NONE;
|
||||
keymap_mods |= wayl->kbd.alt ? MOD_ALT : MOD_NONE;
|
||||
keymap_mods |= wayl->kbd.ctrl ? MOD_CTRL : MOD_NONE;
|
||||
keymap_mods |= wayl->kbd.meta ? MOD_META : MOD_NONE;
|
||||
|
||||
if (effective_mods == shift) {
|
||||
if (sym == XKB_KEY_Page_Up) {
|
||||
|
|
@ -268,11 +273,11 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
|
|||
|
||||
if (compose_status == XKB_COMPOSE_COMPOSED) {
|
||||
count = xkb_compose_state_get_utf8(
|
||||
term->kbd.xkb_compose_state, (char *)buf, sizeof(buf));
|
||||
xkb_compose_state_reset(term->kbd.xkb_compose_state);
|
||||
wayl->kbd.xkb_compose_state, (char *)buf, sizeof(buf));
|
||||
xkb_compose_state_reset(wayl->kbd.xkb_compose_state);
|
||||
} else {
|
||||
count = xkb_state_key_get_utf8(
|
||||
term->kbd.xkb_state, key, (char *)buf, sizeof(buf));
|
||||
wayl->kbd.xkb_state, key, (char *)buf, sizeof(buf));
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
|
|
@ -324,7 +329,7 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
|
|||
}
|
||||
}
|
||||
|
||||
start_repeater(term, key - 8);
|
||||
start_repeater(wayl, key - 8);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -332,33 +337,33 @@ keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
|
|||
uint32_t mods_depressed, uint32_t mods_latched,
|
||||
uint32_t mods_locked, uint32_t group)
|
||||
{
|
||||
struct terminal *term = data;
|
||||
struct wayland *wayl = data;
|
||||
|
||||
LOG_DBG("modifiers: depressed=0x%x, latched=0x%x, locked=0x%x, group=%u",
|
||||
mods_depressed, mods_latched, mods_locked, group);
|
||||
|
||||
xkb_state_update_mask(
|
||||
term->kbd.xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
|
||||
wayl->kbd.xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
|
||||
|
||||
/* Update state of modifiers we're interrested in for e.g mouse events */
|
||||
term->kbd.shift = xkb_state_mod_index_is_active(
|
||||
term->kbd.xkb_state, term->kbd.mod_shift, XKB_STATE_MODS_DEPRESSED);
|
||||
term->kbd.alt = xkb_state_mod_index_is_active(
|
||||
term->kbd.xkb_state, term->kbd.mod_alt, XKB_STATE_MODS_DEPRESSED);
|
||||
term->kbd.ctrl = xkb_state_mod_index_is_active(
|
||||
term->kbd.xkb_state, term->kbd.mod_ctrl, XKB_STATE_MODS_DEPRESSED);
|
||||
term->kbd.meta = xkb_state_mod_index_is_active(
|
||||
term->kbd.xkb_state, term->kbd.mod_meta, XKB_STATE_MODS_DEPRESSED);
|
||||
wayl->kbd.shift = xkb_state_mod_index_is_active(
|
||||
wayl->kbd.xkb_state, wayl->kbd.mod_shift, XKB_STATE_MODS_DEPRESSED);
|
||||
wayl->kbd.alt = xkb_state_mod_index_is_active(
|
||||
wayl->kbd.xkb_state, wayl->kbd.mod_alt, XKB_STATE_MODS_DEPRESSED);
|
||||
wayl->kbd.ctrl = xkb_state_mod_index_is_active(
|
||||
wayl->kbd.xkb_state, wayl->kbd.mod_ctrl, XKB_STATE_MODS_DEPRESSED);
|
||||
wayl->kbd.meta = xkb_state_mod_index_is_active(
|
||||
wayl->kbd.xkb_state, wayl->kbd.mod_meta, XKB_STATE_MODS_DEPRESSED);
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard,
|
||||
int32_t rate, int32_t delay)
|
||||
{
|
||||
struct terminal *term = data;
|
||||
struct wayland *wayl = data;
|
||||
LOG_DBG("keyboard repeat: rate=%d, delay=%d", rate, delay);
|
||||
term->kbd.repeat.rate = rate;
|
||||
term->kbd.repeat.delay = delay;
|
||||
wayl->kbd.repeat.rate = rate;
|
||||
wayl->kbd.repeat.delay = delay;
|
||||
}
|
||||
|
||||
const struct wl_keyboard_listener keyboard_listener = {
|
||||
|
|
@ -371,9 +376,9 @@ const struct wl_keyboard_listener keyboard_listener = {
|
|||
};
|
||||
|
||||
void
|
||||
input_repeat(struct terminal *term, uint32_t key)
|
||||
input_repeat(struct wayland *wayl, uint32_t key)
|
||||
{
|
||||
keyboard_key(term, NULL, 0, 0, key, XKB_KEY_DOWN);
|
||||
keyboard_key(wayl, NULL, 0, 0, key, XKB_KEY_DOWN);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -381,28 +386,35 @@ wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
|
|||
uint32_t serial, struct wl_surface *surface,
|
||||
wl_fixed_t surface_x, wl_fixed_t surface_y)
|
||||
{
|
||||
struct terminal *term = data;
|
||||
struct wayland *wayl = data;
|
||||
struct terminal *term = wayl_terminal_from_surface(wayl, surface);
|
||||
|
||||
wayl->moused = term;
|
||||
|
||||
int x = wl_fixed_to_int(surface_x) * term->scale;
|
||||
int y = wl_fixed_to_int(surface_y) * term->scale;
|
||||
|
||||
term->mouse.col = x / term->cell_width;
|
||||
term->mouse.row = y / term->cell_height;
|
||||
wayl->mouse.col = x / term->cell_width;
|
||||
wayl->mouse.row = y / term->cell_height;
|
||||
|
||||
render_update_cursor_surface(term);
|
||||
wayl_update_cursor_surface(wayl, term);
|
||||
}
|
||||
|
||||
static void
|
||||
wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t serial, struct wl_surface *surface)
|
||||
{
|
||||
struct wayland *wayl = data;
|
||||
wayl->moused = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y)
|
||||
{
|
||||
struct terminal *term = data;
|
||||
struct wayland *wayl = data;
|
||||
struct terminal *term = wayl->moused;
|
||||
assert(term != NULL);
|
||||
|
||||
int x = wl_fixed_to_int(surface_x) * term->scale;
|
||||
int y = wl_fixed_to_int(surface_y) * term->scale;
|
||||
|
|
@ -413,24 +425,24 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
|
|||
if (col < 0 || row < 0 || col >= term->cols || row >= term->rows)
|
||||
return;
|
||||
|
||||
bool update_selection = term->mouse.button == BTN_LEFT;
|
||||
bool update_selection = wayl->mouse.button == BTN_LEFT;
|
||||
bool update_selection_early = term->selection.end.row == -1;
|
||||
|
||||
if (update_selection && update_selection_early)
|
||||
selection_update(term, col, row);
|
||||
|
||||
if (col == term->mouse.col && row == term->mouse.row)
|
||||
if (col == wayl->mouse.col && row == wayl->mouse.row)
|
||||
return;
|
||||
|
||||
term->mouse.col = col;
|
||||
term->mouse.row = row;
|
||||
wayl->mouse.col = col;
|
||||
wayl->mouse.row = row;
|
||||
|
||||
if (update_selection && !update_selection_early)
|
||||
selection_update(term, col, row);
|
||||
|
||||
term_mouse_motion(
|
||||
term, term->mouse.button, term->mouse.row, term->mouse.col,
|
||||
term->kbd.shift, term->kbd.alt, term->kbd.ctrl);
|
||||
term, wayl->mouse.button, wayl->mouse.row, wayl->mouse.col,
|
||||
wayl->kbd.shift, wayl->kbd.alt, wayl->kbd.ctrl);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -439,7 +451,9 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
|
|||
{
|
||||
LOG_DBG("BUTTON: button=%x, state=%u", button, state);
|
||||
|
||||
struct terminal *term = data;
|
||||
struct wayland *wayl = data;
|
||||
struct terminal *term = wayl->moused;
|
||||
assert(term != NULL);
|
||||
|
||||
search_cancel(term);
|
||||
|
||||
|
|
@ -448,43 +462,43 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
|
|||
/* Time since last click */
|
||||
struct timeval now, since_last;
|
||||
gettimeofday(&now, NULL);
|
||||
timersub(&now, &term->mouse.last_time, &since_last);
|
||||
timersub(&now, &wayl->mouse.last_time, &since_last);
|
||||
|
||||
/* Double- or triple click? */
|
||||
if (button == term->mouse.last_button &&
|
||||
if (button == wayl->mouse.last_button &&
|
||||
since_last.tv_sec == 0 &&
|
||||
since_last.tv_usec <= 300 * 1000)
|
||||
{
|
||||
term->mouse.count++;
|
||||
wayl->mouse.count++;
|
||||
} else
|
||||
term->mouse.count = 1;
|
||||
wayl->mouse.count = 1;
|
||||
|
||||
if (button == BTN_LEFT) {
|
||||
switch (term->mouse.count) {
|
||||
switch (wayl->mouse.count) {
|
||||
case 1:
|
||||
selection_start(term, term->mouse.col, term->mouse.row);
|
||||
selection_start(term, wayl->mouse.col, wayl->mouse.row);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
selection_mark_word(term, term->mouse.col, term->mouse.row,
|
||||
term->kbd.ctrl, serial);
|
||||
selection_mark_word(term, wayl->mouse.col, wayl->mouse.row,
|
||||
wayl->kbd.ctrl, serial);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
selection_mark_row(term, term->mouse.row, serial);
|
||||
selection_mark_row(term, wayl->mouse.row, serial);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (term->mouse.count == 1 && button == BTN_MIDDLE && selection_enabled(term))
|
||||
if (wayl->mouse.count == 1 && button == BTN_MIDDLE && selection_enabled(term))
|
||||
selection_from_primary(term);
|
||||
selection_cancel(term);
|
||||
}
|
||||
|
||||
term->mouse.button = button; /* For motion events */
|
||||
term->mouse.last_button = button;
|
||||
term->mouse.last_time = now;
|
||||
term_mouse_down(term, button, term->mouse.row, term->mouse.col,
|
||||
term->kbd.shift, term->kbd.alt, term->kbd.ctrl);
|
||||
wayl->mouse.button = button; /* For motion events */
|
||||
wayl->mouse.last_button = button;
|
||||
wayl->mouse.last_time = now;
|
||||
term_mouse_down(term, button, wayl->mouse.row, wayl->mouse.col,
|
||||
wayl->kbd.shift, wayl->kbd.alt, wayl->kbd.ctrl);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -494,16 +508,19 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
|
|||
else
|
||||
selection_finalize(term, serial);
|
||||
|
||||
term->mouse.button = 0; /* For motion events */
|
||||
term_mouse_up(term, button, term->mouse.row, term->mouse.col,
|
||||
term->kbd.shift, term->kbd.alt, term->kbd.ctrl);
|
||||
wayl->mouse.button = 0; /* For motion events */
|
||||
term_mouse_up(term, button, wayl->mouse.row, wayl->mouse.col,
|
||||
wayl->kbd.shift, wayl->kbd.alt, wayl->kbd.ctrl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mouse_scroll(struct terminal *term, int amount)
|
||||
mouse_scroll(struct wayland *wayl, int amount)
|
||||
{
|
||||
struct terminal *term = wayl->moused;
|
||||
assert(term != NULL);
|
||||
|
||||
int button = amount < 0 ? BTN_BACK : BTN_FORWARD;
|
||||
|
||||
void (*scrollback)(struct terminal *term, int rows)
|
||||
|
|
@ -520,15 +537,15 @@ mouse_scroll(struct terminal *term, int amount)
|
|||
*/
|
||||
|
||||
xkb_keycode_t key = xkb_keymap_key_by_name(
|
||||
term->kbd.xkb_keymap, button == BTN_BACK ? "UP" : "DOWN");
|
||||
wayl->kbd.xkb_keymap, button == BTN_BACK ? "UP" : "DOWN");
|
||||
|
||||
for (int i = 0; i < amount; i++)
|
||||
keyboard_key(term, NULL, term->input_serial, 0, key - 8, XKB_KEY_DOWN);
|
||||
keyboard_key(term, NULL, term->input_serial, 0, key - 8, XKB_KEY_UP);
|
||||
keyboard_key(term, NULL, wayl->input_serial, 0, key - 8, XKB_KEY_DOWN);
|
||||
keyboard_key(term, NULL, wayl->input_serial, 0, key - 8, XKB_KEY_UP);
|
||||
} else {
|
||||
for (int i = 0; i < amount; i++)
|
||||
term_mouse_down(term, button, term->mouse.row, term->mouse.col,
|
||||
term->kbd.shift, term->kbd.alt, term->kbd.ctrl);
|
||||
term_mouse_down(term, button, wayl->mouse.row, wayl->mouse.col,
|
||||
wayl->kbd.shift, wayl->kbd.alt, wayl->kbd.ctrl);
|
||||
|
||||
scrollback(term, amount);
|
||||
}
|
||||
|
|
@ -541,11 +558,12 @@ wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
|
|||
if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL)
|
||||
return;
|
||||
|
||||
struct terminal *term = data;
|
||||
if (term->mouse.have_discrete)
|
||||
struct wayland *wayl = data;
|
||||
|
||||
if (wayl->mouse.have_discrete)
|
||||
return;
|
||||
|
||||
mouse_scroll(term, wl_fixed_to_int(value));
|
||||
mouse_scroll(wayl, wl_fixed_to_int(value));
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -555,16 +573,16 @@ wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer,
|
|||
if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL)
|
||||
return;
|
||||
|
||||
struct terminal *term = data;
|
||||
term->mouse.have_discrete = true;
|
||||
mouse_scroll(term, discrete);
|
||||
struct wayland *wayl = data;
|
||||
wayl->mouse.have_discrete = true;
|
||||
mouse_scroll(wayl, discrete);
|
||||
}
|
||||
|
||||
static void
|
||||
wl_pointer_frame(void *data, struct wl_pointer *wl_pointer)
|
||||
{
|
||||
struct terminal *term = data;
|
||||
term->mouse.have_discrete = false;
|
||||
struct wayland *wayl = data;
|
||||
wayl->mouse.have_discrete = false;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
|||
4
input.h
4
input.h
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include "terminal.h"
|
||||
#include "wayland.h"
|
||||
|
||||
extern const struct wl_keyboard_listener keyboard_listener;
|
||||
extern const struct wl_pointer_listener pointer_listener;
|
||||
|
||||
void input_repeat(struct terminal *term, uint32_t key);
|
||||
void input_repeat(struct wayland *wayl, uint32_t key);
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ executable(
|
|||
'config.c', 'config.h',
|
||||
'commands.c', 'commands.h',
|
||||
'csi.c', 'csi.h',
|
||||
'fdm.c', 'fdm.h',
|
||||
'font.c', 'font.h',
|
||||
'grid.c', 'grid.h',
|
||||
'input.c', 'input.h',
|
||||
|
|
@ -83,6 +84,7 @@ executable(
|
|||
'tokenize.c', 'tokenize.h',
|
||||
'tllist.h',
|
||||
'vt.c', 'vt.h',
|
||||
'wayland.c', 'wayland.h',
|
||||
wl_proto_src + wl_proto_headers, version,
|
||||
dependencies: [threads, math, freetype, fontconfig, pixman, wayland_client, wayland_cursor, xkb],
|
||||
install: true)
|
||||
|
|
|
|||
6
osc.c
6
osc.c
|
|
@ -30,14 +30,14 @@ osc_to_clipboard(struct terminal *term, const char *target,
|
|||
switch (*t) {
|
||||
case 'c': {
|
||||
char *copy = strdup(decoded);
|
||||
if (!text_to_clipboard(term, copy, term->input_serial))
|
||||
if (!text_to_clipboard(term, copy, term->wl->input_serial))
|
||||
free(copy);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'p': {
|
||||
char *copy = strdup(decoded);
|
||||
if (!text_to_primary(term, copy, term->input_serial))
|
||||
if (!text_to_primary(term, copy, term->wl->input_serial))
|
||||
free(copy);
|
||||
break;
|
||||
}
|
||||
|
|
@ -130,7 +130,7 @@ osc_from_clipboard(struct terminal *term, const char *source)
|
|||
|
||||
switch (src) {
|
||||
case 'c':
|
||||
text_from_clipboard(term, term->input_serial, &from_clipboard_cb, &ctx);
|
||||
text_from_clipboard(term, term->wl->input_serial, &from_clipboard_cb, &ctx);
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
|
|
|
|||
118
render.c
118
render.c
|
|
@ -330,7 +330,8 @@ grid_render_scroll(struct terminal *term, struct buffer *buf,
|
|||
raw + src_y * buf->stride,
|
||||
height * buf->stride);
|
||||
|
||||
wl_surface_damage_buffer(term->wl.surface, term->x_margin, dst_y, term->width - term->x_margin, height);
|
||||
wl_surface_damage_buffer(
|
||||
term->window->surface, term->x_margin, dst_y, term->width - term->x_margin, height);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -355,7 +356,8 @@ grid_render_scroll_reverse(struct terminal *term, struct buffer *buf,
|
|||
raw + src_y * buf->stride,
|
||||
height * buf->stride);
|
||||
|
||||
wl_surface_damage_buffer(term->wl.surface, term->x_margin, dst_y, term->width - term->x_margin, height);
|
||||
wl_surface_damage_buffer(
|
||||
term->window->surface, term->x_margin, dst_y, term->width - term->x_margin, height);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -372,6 +374,7 @@ render_worker_thread(void *_ctx)
|
|||
struct render_worker_context *ctx = _ctx;
|
||||
struct terminal *term = ctx->term;
|
||||
const int my_id = ctx->my_id;
|
||||
free(ctx);
|
||||
|
||||
char proc_title[16];
|
||||
snprintf(proc_title, sizeof(proc_title), "foot:render:%d", my_id);
|
||||
|
|
@ -438,8 +441,8 @@ grid_render(struct terminal *term)
|
|||
assert(term->width > 0);
|
||||
assert(term->height > 0);
|
||||
|
||||
struct buffer *buf = shm_get_buffer(term->wl.shm, term->width, term->height, 1 + term->render.workers.count);
|
||||
wl_surface_attach(term->wl.surface, buf->wl_buf, 0, 0);
|
||||
struct buffer *buf = shm_get_buffer(term->wl->shm, term->width, term->height, 1 + term->render.workers.count);
|
||||
wl_surface_attach(term->window->surface, buf->wl_buf, 0, 0);
|
||||
pixman_image_t *pix = buf->pix;
|
||||
|
||||
bool all_clean = tll_length(term->grid->scroll_damage) == 0;
|
||||
|
|
@ -487,13 +490,13 @@ grid_render(struct terminal *term)
|
|||
{0, bmargin, term->width, bmargin_height}}); /* Bottom */
|
||||
|
||||
wl_surface_damage_buffer(
|
||||
term->wl.surface, 0, 0, term->width, term->y_margin);
|
||||
term->window->surface, 0, 0, term->width, term->y_margin);
|
||||
wl_surface_damage_buffer(
|
||||
term->wl.surface, 0, 0, term->x_margin, term->height);
|
||||
term->window->surface, 0, 0, term->x_margin, term->height);
|
||||
wl_surface_damage_buffer(
|
||||
term->wl.surface, rmargin, 0, rmargin_width, term->height);
|
||||
term->window->surface, rmargin, 0, rmargin_width, term->height);
|
||||
wl_surface_damage_buffer(
|
||||
term->wl.surface, 0, bmargin, term->width, bmargin_height);
|
||||
term->window->surface, 0, bmargin, term->width, bmargin_height);
|
||||
|
||||
/* Force a full grid refresh */
|
||||
term_damage_view(term);
|
||||
|
|
@ -514,7 +517,7 @@ grid_render(struct terminal *term)
|
|||
render_cell(term, pix, cell, at.col, at.row, false);
|
||||
|
||||
wl_surface_damage_buffer(
|
||||
term->wl.surface,
|
||||
term->window->surface,
|
||||
term->x_margin + at.col * term->cell_width,
|
||||
term->y_margin + at.row * term->cell_height,
|
||||
term->cell_width, term->cell_height);
|
||||
|
|
@ -577,7 +580,7 @@ grid_render(struct terminal *term)
|
|||
all_clean = false;
|
||||
|
||||
wl_surface_damage_buffer(
|
||||
term->wl.surface,
|
||||
term->window->surface,
|
||||
term->x_margin, term->y_margin + r * term->cell_height,
|
||||
term->width - term->x_margin, term->cell_height);
|
||||
}
|
||||
|
|
@ -600,7 +603,7 @@ grid_render(struct terminal *term)
|
|||
all_clean = false;
|
||||
|
||||
wl_surface_damage_buffer(
|
||||
term->wl.surface,
|
||||
term->window->surface,
|
||||
term->x_margin, term->y_margin + r * term->cell_height,
|
||||
term->width - term->x_margin, term->cell_height);
|
||||
}
|
||||
|
|
@ -682,7 +685,7 @@ grid_render(struct terminal *term)
|
|||
term, pix, cell, term->cursor.col, view_aligned_row, true);
|
||||
|
||||
wl_surface_damage_buffer(
|
||||
term->wl.surface,
|
||||
term->window->surface,
|
||||
term->x_margin + term->cursor.col * term->cell_width,
|
||||
term->y_margin + view_aligned_row * term->cell_height,
|
||||
cols_updated * term->cell_width, term->cell_height);
|
||||
|
|
@ -702,18 +705,18 @@ grid_render(struct terminal *term)
|
|||
1, &(pixman_rectangle16_t){0, 0, term->width, term->height});
|
||||
|
||||
wl_surface_damage_buffer(
|
||||
term->wl.surface, 0, 0, term->width, term->height);
|
||||
term->window->surface, 0, 0, term->width, term->height);
|
||||
}
|
||||
|
||||
assert(term->grid->offset >= 0 && term->grid->offset < term->grid->num_rows);
|
||||
assert(term->grid->view >= 0 && term->grid->view < term->grid->num_rows);
|
||||
|
||||
assert(term->render.frame_callback == NULL);
|
||||
term->render.frame_callback = wl_surface_frame(term->wl.surface);
|
||||
wl_callback_add_listener(term->render.frame_callback, &frame_listener, term);
|
||||
assert(term->window->frame_callback == NULL);
|
||||
term->window->frame_callback = wl_surface_frame(term->window->surface);
|
||||
wl_callback_add_listener(term->window->frame_callback, &frame_listener, term);
|
||||
|
||||
wl_surface_set_buffer_scale(term->wl.surface, term->scale);
|
||||
wl_surface_commit(term->wl.surface);
|
||||
wl_surface_set_buffer_scale(term->window->surface, term->scale);
|
||||
wl_surface_commit(term->window->surface);
|
||||
|
||||
#if TIME_FRAME_RENDERING
|
||||
struct timeval end_time;
|
||||
|
|
@ -731,16 +734,16 @@ frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_da
|
|||
{
|
||||
struct terminal *term = data;
|
||||
|
||||
assert(term->render.frame_callback == wl_callback);
|
||||
assert(term->window->frame_callback == wl_callback);
|
||||
wl_callback_destroy(wl_callback);
|
||||
term->render.frame_callback = NULL;
|
||||
term->window->frame_callback = NULL;
|
||||
grid_render(term);
|
||||
}
|
||||
|
||||
void
|
||||
render_search_box(struct terminal *term)
|
||||
{
|
||||
assert(term->wl.search_sub_surface != NULL);
|
||||
assert(term->window->search_sub_surface != NULL);
|
||||
|
||||
/* TODO: at least sway allows the subsurface to extend outside the
|
||||
* main window. Do we want that? */
|
||||
|
|
@ -749,7 +752,7 @@ render_search_box(struct terminal *term)
|
|||
const int width = 2 * margin + max(20, term->search.len) * term->cell_width;
|
||||
const int height = 2 * margin + 1 * term->cell_height;
|
||||
|
||||
struct buffer *buf = shm_get_buffer(term->wl.shm, width, height, 1);
|
||||
struct buffer *buf = shm_get_buffer(term->wl->shm, width, height, 1);
|
||||
|
||||
/* Background - yellow on empty/match, red on mismatch */
|
||||
pixman_color_t color = color_hex_to_pixman(
|
||||
|
|
@ -788,13 +791,13 @@ render_search_box(struct terminal *term)
|
|||
draw_bar(term, buf->pix, font, &fg, x, y);
|
||||
|
||||
wl_subsurface_set_position(
|
||||
term->wl.search_sub_surface,
|
||||
term->window->search_sub_surface,
|
||||
term->width - width - margin, term->height - height - margin);
|
||||
|
||||
wl_surface_damage_buffer(term->wl.search_surface, 0, 0, width, height);
|
||||
wl_surface_attach(term->wl.search_surface, buf->wl_buf, 0, 0);
|
||||
wl_surface_set_buffer_scale(term->wl.search_surface, scale);
|
||||
wl_surface_commit(term->wl.search_surface);
|
||||
wl_surface_damage_buffer(term->window->search_surface, 0, 0, width, height);
|
||||
wl_surface_attach(term->window->search_surface, buf->wl_buf, 0, 0);
|
||||
wl_surface_set_buffer_scale(term->window->search_surface, scale);
|
||||
wl_surface_commit(term->window->search_surface);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -826,7 +829,7 @@ void
|
|||
render_resize(struct terminal *term, int width, int height)
|
||||
{
|
||||
int scale = -1;
|
||||
tll_foreach(term->wl.on_outputs, it) {
|
||||
tll_foreach(term->window->on_outputs, it) {
|
||||
if (it->item->scale > scale)
|
||||
scale = it->item->scale;
|
||||
}
|
||||
|
|
@ -947,67 +950,12 @@ render_resize(struct terminal *term, int width, int height)
|
|||
void
|
||||
render_set_title(struct terminal *term, const char *title)
|
||||
{
|
||||
xdg_toplevel_set_title(term->wl.xdg_toplevel, title);
|
||||
}
|
||||
|
||||
bool
|
||||
render_reload_cursor_theme(struct terminal *term)
|
||||
{
|
||||
if (term->wl.pointer.size == 0)
|
||||
return true;
|
||||
|
||||
if (term->wl.pointer.theme != NULL) {
|
||||
wl_cursor_theme_destroy(term->wl.pointer.theme);
|
||||
term->wl.pointer.theme = NULL;
|
||||
term->wl.pointer.cursor = NULL;
|
||||
}
|
||||
|
||||
LOG_DBG("reloading cursor theme: %s@%d",
|
||||
term->wl.pointer.theme_name, term->wl.pointer.size);
|
||||
|
||||
term->wl.pointer.theme = wl_cursor_theme_load(
|
||||
term->wl.pointer.theme_name, term->wl.pointer.size * term->scale, term->wl.shm);
|
||||
if (term->wl.pointer.theme == NULL) {
|
||||
LOG_ERR("failed to load cursor theme");
|
||||
return false;
|
||||
}
|
||||
|
||||
term->wl.pointer.cursor = wl_cursor_theme_get_cursor(
|
||||
term->wl.pointer.theme, "left_ptr");
|
||||
assert(term->wl.pointer.cursor != NULL);
|
||||
render_update_cursor_surface(term);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
render_update_cursor_surface(struct terminal *term)
|
||||
{
|
||||
if (term->wl.pointer.cursor == NULL)
|
||||
return;
|
||||
|
||||
const int scale = term->scale;
|
||||
wl_surface_set_buffer_scale(term->wl.pointer.surface, scale);
|
||||
|
||||
struct wl_cursor_image *image = term->wl.pointer.cursor->images[0];
|
||||
|
||||
wl_surface_attach(
|
||||
term->wl.pointer.surface, wl_cursor_image_get_buffer(image), 0, 0);
|
||||
|
||||
wl_pointer_set_cursor(
|
||||
term->wl.pointer.pointer, term->wl.pointer.serial,
|
||||
term->wl.pointer.surface,
|
||||
image->hotspot_x / scale, image->hotspot_y / scale);
|
||||
|
||||
wl_surface_damage_buffer(
|
||||
term->wl.pointer.surface, 0, 0, INT32_MAX, INT32_MAX);
|
||||
|
||||
wl_surface_commit(term->wl.pointer.surface);
|
||||
xdg_toplevel_set_title(term->window->xdg_toplevel, title);
|
||||
}
|
||||
|
||||
void
|
||||
render_refresh(struct terminal *term)
|
||||
{
|
||||
if (term->render.frame_callback == NULL)
|
||||
if (term->window->frame_callback == NULL)
|
||||
grid_render(term);
|
||||
}
|
||||
|
|
|
|||
2
render.h
2
render.h
|
|
@ -11,8 +11,6 @@ void render_set_title(struct terminal *term, const char *title);
|
|||
void render_refresh(struct terminal *term);
|
||||
|
||||
void render_search_box(struct terminal *term);
|
||||
bool render_reload_cursor_theme(struct terminal *term);
|
||||
void render_update_cursor_surface(struct terminal *term);
|
||||
|
||||
struct render_worker_context {
|
||||
int my_id;
|
||||
|
|
|
|||
22
search.c
22
search.c
|
|
@ -19,8 +19,8 @@
|
|||
static void
|
||||
search_cancel_keep_selection(struct terminal *term)
|
||||
{
|
||||
wl_surface_attach(term->wl.search_surface, NULL, 0, 0);
|
||||
wl_surface_commit(term->wl.search_surface);
|
||||
wl_surface_attach(term->window->search_surface, NULL, 0, 0);
|
||||
wl_surface_commit(term->window->search_surface);
|
||||
|
||||
free(term->search.buf);
|
||||
term->search.buf = NULL;
|
||||
|
|
@ -294,13 +294,13 @@ search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym, xkb_mod_mask
|
|||
{
|
||||
LOG_DBG("search: input: sym=%d/0x%x, mods=0x%08x", sym, sym, mods);
|
||||
|
||||
const xkb_mod_mask_t ctrl = 1 << term->kbd.mod_ctrl;
|
||||
const xkb_mod_mask_t alt = 1 << term->kbd.mod_alt;
|
||||
//const xkb_mod_mask_t shift = 1 << term->kbd.mod_shift;
|
||||
//const xkb_mod_mask_t meta = 1 << term->kbd.mod_meta;
|
||||
const xkb_mod_mask_t ctrl = 1 << term->wl->kbd.mod_ctrl;
|
||||
const xkb_mod_mask_t alt = 1 << term->wl->kbd.mod_alt;
|
||||
//const xkb_mod_mask_t shift = 1 << term->wl->kbd.mod_shift;
|
||||
//const xkb_mod_mask_t meta = 1 << term->wl->kbd.mod_meta;
|
||||
|
||||
enum xkb_compose_status compose_status = xkb_compose_state_get_status(
|
||||
term->kbd.xkb_compose_state);
|
||||
term->wl->kbd.xkb_compose_state);
|
||||
|
||||
/* Cancel search */
|
||||
if ((mods == 0 && sym == XKB_KEY_Escape) ||
|
||||
|
|
@ -317,7 +317,7 @@ search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym, xkb_mod_mask
|
|||
|
||||
/* "Commit" search - copy selection to primary and cancel search */
|
||||
else if (mods == 0 && sym == XKB_KEY_Return) {
|
||||
selection_finalize(term, term->input_serial);
|
||||
selection_finalize(term, term->wl->input_serial);
|
||||
search_cancel_keep_selection(term);
|
||||
return;
|
||||
}
|
||||
|
|
@ -449,11 +449,11 @@ search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym, xkb_mod_mask
|
|||
|
||||
if (compose_status == XKB_COMPOSE_COMPOSED) {
|
||||
count = xkb_compose_state_get_utf8(
|
||||
term->kbd.xkb_compose_state, (char *)buf, sizeof(buf));
|
||||
xkb_compose_state_reset(term->kbd.xkb_compose_state);
|
||||
term->wl->kbd.xkb_compose_state, (char *)buf, sizeof(buf));
|
||||
xkb_compose_state_reset(term->wl->kbd.xkb_compose_state);
|
||||
} else {
|
||||
count = xkb_state_key_get_utf8(
|
||||
term->kbd.xkb_state, key, (char *)buf, sizeof(buf));
|
||||
term->wl->kbd.xkb_state, key, (char *)buf, sizeof(buf));
|
||||
}
|
||||
|
||||
const char *src = (const char *)buf;
|
||||
|
|
|
|||
65
selection.c
65
selection.c
|
|
@ -22,7 +22,7 @@ selection_enabled(const struct terminal *term)
|
|||
{
|
||||
return
|
||||
term->mouse_tracking == MOUSE_NONE ||
|
||||
term->kbd.shift ||
|
||||
term->wl->kbd.shift ||
|
||||
term->is_searching;
|
||||
}
|
||||
|
||||
|
|
@ -319,7 +319,7 @@ static void
|
|||
send(void *data, struct wl_data_source *wl_data_source, const char *mime_type,
|
||||
int32_t fd)
|
||||
{
|
||||
const struct clipboard *clipboard
|
||||
const struct wl_clipboard *clipboard
|
||||
= wl_data_source_get_user_data(wl_data_source);
|
||||
|
||||
assert(clipboard != NULL);
|
||||
|
|
@ -346,7 +346,7 @@ send(void *data, struct wl_data_source *wl_data_source, const char *mime_type,
|
|||
static void
|
||||
cancelled(void *data, struct wl_data_source *wl_data_source)
|
||||
{
|
||||
struct clipboard *clipboard = wl_data_source_get_user_data(wl_data_source);
|
||||
struct wl_clipboard *clipboard = wl_data_source_get_user_data(wl_data_source);
|
||||
assert(clipboard->data_source == wl_data_source);
|
||||
|
||||
wl_data_source_destroy(clipboard->data_source);
|
||||
|
|
@ -386,7 +386,7 @@ primary_send(void *data,
|
|||
struct zwp_primary_selection_source_v1 *zwp_primary_selection_source_v1,
|
||||
const char *mime_type, int32_t fd)
|
||||
{
|
||||
const struct primary *primary
|
||||
const struct wl_primary *primary
|
||||
= zwp_primary_selection_source_v1_get_user_data(zwp_primary_selection_source_v1);
|
||||
|
||||
assert(primary != NULL);
|
||||
|
|
@ -414,7 +414,7 @@ static void
|
|||
primary_cancelled(void *data,
|
||||
struct zwp_primary_selection_source_v1 *zwp_primary_selection_source_v1)
|
||||
{
|
||||
struct primary *primary = zwp_primary_selection_source_v1_get_user_data(
|
||||
struct wl_primary *primary = zwp_primary_selection_source_v1_get_user_data(
|
||||
zwp_primary_selection_source_v1);
|
||||
//assert(primary->data_source == zwp_primary_selection_source_v1);
|
||||
|
||||
|
|
@ -434,12 +434,12 @@ static const struct zwp_primary_selection_source_v1_listener primary_selection_s
|
|||
bool
|
||||
text_to_clipboard(struct terminal *term, char *text, uint32_t serial)
|
||||
{
|
||||
if (term->selection.clipboard.data_source != NULL) {
|
||||
/* Kill previous data source */
|
||||
struct clipboard *clipboard = &term->selection.clipboard;
|
||||
struct wl_clipboard *clipboard = &term->wl->clipboard;
|
||||
|
||||
if (term->wl->clipboard.data_source != NULL) {
|
||||
/* Kill previous data source */
|
||||
assert(clipboard->serial != 0);
|
||||
wl_data_device_set_selection(term->wl.data_device, NULL, clipboard->serial);
|
||||
wl_data_device_set_selection(term->wl->data_device, NULL, clipboard->serial);
|
||||
wl_data_source_destroy(clipboard->data_source);
|
||||
free(clipboard->text);
|
||||
|
||||
|
|
@ -447,10 +447,8 @@ text_to_clipboard(struct terminal *term, char *text, uint32_t serial)
|
|||
clipboard->serial = 0;
|
||||
}
|
||||
|
||||
struct clipboard *clipboard = &term->selection.clipboard;
|
||||
|
||||
clipboard->data_source
|
||||
= wl_data_device_manager_create_data_source(term->wl.data_device_manager);
|
||||
= wl_data_device_manager_create_data_source(term->wl->data_device_manager);
|
||||
|
||||
if (clipboard->data_source == NULL) {
|
||||
LOG_ERR("failed to create clipboard data source");
|
||||
|
|
@ -462,7 +460,7 @@ text_to_clipboard(struct terminal *term, char *text, uint32_t serial)
|
|||
/* Configure source */
|
||||
wl_data_source_offer(clipboard->data_source, "text/plain;charset=utf-8");
|
||||
wl_data_source_add_listener(clipboard->data_source, &data_source_listener, term);
|
||||
wl_data_device_set_selection(term->wl.data_device, clipboard->data_source, serial);
|
||||
wl_data_device_set_selection(term->wl->data_device, clipboard->data_source, serial);
|
||||
wl_data_source_set_user_data(clipboard->data_source, clipboard);
|
||||
|
||||
/* Needed when sending the selection to other client */
|
||||
|
|
@ -484,7 +482,7 @@ text_from_clipboard(struct terminal *term, uint32_t serial,
|
|||
void (*cb)(const char *data, size_t size, void *user),
|
||||
void *user)
|
||||
{
|
||||
struct clipboard *clipboard = &term->selection.clipboard;
|
||||
struct wl_clipboard *clipboard = &term->wl->clipboard;
|
||||
if (clipboard->data_offer == NULL)
|
||||
return;
|
||||
|
||||
|
|
@ -501,7 +499,7 @@ text_from_clipboard(struct terminal *term, uint32_t serial,
|
|||
/* Give write-end of pipe to other client */
|
||||
wl_data_offer_receive(
|
||||
clipboard->data_offer, "text/plain;charset=utf-8", write_fd);
|
||||
wl_display_roundtrip(term->wl.display);
|
||||
wl_display_roundtrip(term->wl->display);
|
||||
|
||||
/* Don't keep our copy of the write-end open (or we'll never get EOF) */
|
||||
close(write_fd);
|
||||
|
|
@ -541,7 +539,7 @@ from_clipboard_cb(const char *data, size_t size, void *user)
|
|||
void
|
||||
selection_from_clipboard(struct terminal *term, uint32_t serial)
|
||||
{
|
||||
struct clipboard *clipboard = &term->selection.clipboard;
|
||||
struct wl_clipboard *clipboard = &term->wl->clipboard;
|
||||
if (clipboard->data_offer == NULL)
|
||||
return;
|
||||
|
||||
|
|
@ -557,17 +555,18 @@ selection_from_clipboard(struct terminal *term, uint32_t serial)
|
|||
bool
|
||||
text_to_primary(struct terminal *term, char *text, uint32_t serial)
|
||||
{
|
||||
if (term->wl.primary_selection_device_manager == NULL)
|
||||
if (term->wl->primary_selection_device_manager == NULL)
|
||||
return false;
|
||||
|
||||
struct wl_primary *primary = &term->wl->primary;
|
||||
|
||||
/* TODO: somehow share code with the clipboard equivalent */
|
||||
if (term->selection.primary.data_source != NULL) {
|
||||
if (term->wl->primary.data_source != NULL) {
|
||||
/* Kill previous data source */
|
||||
struct primary *primary = &term->selection.primary;
|
||||
|
||||
assert(primary->serial != 0);
|
||||
zwp_primary_selection_device_v1_set_selection(
|
||||
term->wl.primary_selection_device, NULL, primary->serial);
|
||||
term->wl->primary_selection_device, NULL, primary->serial);
|
||||
zwp_primary_selection_source_v1_destroy(primary->data_source);
|
||||
free(primary->text);
|
||||
|
||||
|
|
@ -575,11 +574,9 @@ text_to_primary(struct terminal *term, char *text, uint32_t serial)
|
|||
primary->serial = 0;
|
||||
}
|
||||
|
||||
struct primary *primary = &term->selection.primary;
|
||||
|
||||
primary->data_source
|
||||
= zwp_primary_selection_device_manager_v1_create_source(
|
||||
term->wl.primary_selection_device_manager);
|
||||
term->wl->primary_selection_device_manager);
|
||||
|
||||
if (primary->data_source == NULL) {
|
||||
LOG_ERR("failed to create clipboard data source");
|
||||
|
|
@ -592,7 +589,7 @@ text_to_primary(struct terminal *term, char *text, uint32_t serial)
|
|||
/* Configure source */
|
||||
zwp_primary_selection_source_v1_offer(primary->data_source, "text/plain;charset=utf-8");
|
||||
zwp_primary_selection_source_v1_add_listener(primary->data_source, &primary_selection_source_listener, term);
|
||||
zwp_primary_selection_device_v1_set_selection(term->wl.primary_selection_device, primary->data_source, serial);
|
||||
zwp_primary_selection_device_v1_set_selection(term->wl->primary_selection_device, primary->data_source, serial);
|
||||
zwp_primary_selection_source_v1_set_user_data(primary->data_source, primary);
|
||||
|
||||
/* Needed when sending the selection to other client */
|
||||
|
|
@ -603,7 +600,7 @@ text_to_primary(struct terminal *term, char *text, uint32_t serial)
|
|||
void
|
||||
selection_to_primary(struct terminal *term, uint32_t serial)
|
||||
{
|
||||
if (term->wl.primary_selection_device_manager == NULL)
|
||||
if (term->wl->primary_selection_device_manager == NULL)
|
||||
return;
|
||||
|
||||
/* Get selection as a string */
|
||||
|
|
@ -617,10 +614,10 @@ text_from_primary(
|
|||
struct terminal *term, void (*cb)(const char *data, size_t size, void *user),
|
||||
void *user)
|
||||
{
|
||||
if (term->wl.primary_selection_device_manager == NULL)
|
||||
if (term->wl->primary_selection_device_manager == NULL)
|
||||
return;
|
||||
|
||||
struct primary *primary = &term->selection.primary;
|
||||
struct wl_primary *primary = &term->wl->primary;
|
||||
if (primary->data_offer == NULL)
|
||||
return;
|
||||
|
||||
|
|
@ -637,7 +634,7 @@ text_from_primary(
|
|||
/* Give write-end of pipe to other client */
|
||||
zwp_primary_selection_offer_v1_receive(
|
||||
primary->data_offer, "text/plain;charset=utf-8", write_fd);
|
||||
wl_display_roundtrip(term->wl.display);
|
||||
wl_display_roundtrip(term->wl->display);
|
||||
|
||||
/* Don't keep our copy of the write-end open (or we'll never get EOF) */
|
||||
close(write_fd);
|
||||
|
|
@ -670,10 +667,10 @@ text_from_primary(
|
|||
void
|
||||
selection_from_primary(struct terminal *term)
|
||||
{
|
||||
if (term->wl.primary_selection_device_manager == NULL)
|
||||
if (term->wl->primary_selection_device_manager == NULL)
|
||||
return;
|
||||
|
||||
struct clipboard *clipboard = &term->selection.clipboard;
|
||||
struct wl_clipboard *clipboard = &term->wl->clipboard;
|
||||
if (clipboard->data_offer == NULL)
|
||||
return;
|
||||
|
||||
|
|
@ -745,8 +742,8 @@ selection(void *data, struct wl_data_device *wl_data_device,
|
|||
{
|
||||
/* Selection offer from other client */
|
||||
|
||||
struct terminal *term = data;
|
||||
struct clipboard *clipboard = &term->selection.clipboard;
|
||||
struct wayland *wayl = data;
|
||||
struct wl_clipboard *clipboard = &wayl->clipboard;
|
||||
|
||||
if (clipboard->data_offer != NULL)
|
||||
wl_data_offer_destroy(clipboard->data_offer);
|
||||
|
|
@ -794,8 +791,8 @@ primary_selection(void *data,
|
|||
{
|
||||
/* Selection offer from other client, for primary */
|
||||
|
||||
struct terminal *term = data;
|
||||
struct primary *primary = &term->selection.primary;
|
||||
struct wayland *wayl = data;
|
||||
struct wl_primary *primary = &wayl->primary;
|
||||
|
||||
if (primary->data_offer != NULL)
|
||||
zwp_primary_selection_offer_v1_destroy(primary->data_offer);
|
||||
|
|
|
|||
2
slave.c
2
slave.c
|
|
@ -13,7 +13,7 @@
|
|||
#include "log.h"
|
||||
|
||||
void
|
||||
slave_spawn(int ptmx, char *const argv[], int err_fd)
|
||||
slave_exec(int ptmx, char *const argv[], int err_fd)
|
||||
{
|
||||
int pts = -1;
|
||||
const char *pts_name = ptsname(ptmx);
|
||||
|
|
|
|||
2
slave.h
2
slave.h
|
|
@ -1,4 +1,4 @@
|
|||
#pragma once
|
||||
#include <stdbool.h>
|
||||
|
||||
void slave_spawn(int ptmx, char *const argv[], int err_fd);
|
||||
void slave_exec(int ptmx, char *const argv[], int err_fd);
|
||||
|
|
|
|||
602
terminal.c
602
terminal.c
|
|
@ -3,8 +3,13 @@
|
|||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/timerfd.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/input-event-codes.h>
|
||||
|
||||
#define LOG_MODULE "terminal"
|
||||
|
|
@ -14,10 +19,601 @@
|
|||
#include "render.h"
|
||||
#include "vt.h"
|
||||
#include "selection.h"
|
||||
#include "config.h"
|
||||
#include "tokenize.h"
|
||||
#include "slave.h"
|
||||
|
||||
#define min(x, y) ((x) < (y) ? (x) : (y))
|
||||
#define max(x, y) ((x) > (y) ? (x) : (y))
|
||||
|
||||
static bool
|
||||
fdm_ptmx(struct fdm *fdm, int fd, int events, void *data)
|
||||
{
|
||||
struct terminal *term = data;
|
||||
|
||||
if (events & EPOLLHUP) {
|
||||
term->quit = true;
|
||||
|
||||
if (!(events & EPOLLIN))
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(events & EPOLLIN);
|
||||
|
||||
uint8_t buf[24 * 1024];
|
||||
ssize_t count = read(term->ptmx, buf, sizeof(buf));
|
||||
|
||||
if (count < 0) {
|
||||
if (errno == EAGAIN)
|
||||
return true;
|
||||
|
||||
LOG_ERRNO("failed to read from pseudo terminal");
|
||||
return false;
|
||||
}
|
||||
|
||||
vt_from_slave(term, buf, count);
|
||||
|
||||
/*
|
||||
* We likely need to re-render. But, we don't want to
|
||||
* do it immediately. Often, a single client operation
|
||||
* is done through multiple writes. Many times, we're
|
||||
* so fast that we render mid-operation frames.
|
||||
*
|
||||
* For example, we might end up rendering a frame
|
||||
* where the client just erased a line, while in the
|
||||
* next frame, the client wrote to the same line. This
|
||||
* causes screen "flashes".
|
||||
*
|
||||
* Mitigate by always incuring a small delay before
|
||||
* rendering the next frame. This gives the client
|
||||
* some time to finish the operation (and thus gives
|
||||
* us time to receive the last writes before doing any
|
||||
* actual rendering).
|
||||
*
|
||||
* We incur this delay *every* time we receive
|
||||
* input. To ensure we don't delay rendering
|
||||
* indefinitely, we start a second timer that is only
|
||||
* reset when we render.
|
||||
*
|
||||
* Note that when the client is producing data at a
|
||||
* very high pace, we're rate limited by the wayland
|
||||
* compositor anyway. The delay we introduce here only
|
||||
* has any effect when the renderer is idle.
|
||||
*
|
||||
* TODO: this adds input latency. Can we somehow hint
|
||||
* ourselves we just received keyboard input, and in
|
||||
* this case *not* delay rendering?
|
||||
*/
|
||||
if (term->window->frame_callback == NULL) {
|
||||
/* First timeout - reset each time we receive input. */
|
||||
timerfd_settime(
|
||||
term->delayed_render_timer.lower_fd, 0,
|
||||
&(struct itimerspec){.it_value = {.tv_nsec = 1000000}},
|
||||
NULL);
|
||||
|
||||
/* Second timeout - only reset when we render. Set to one
|
||||
* frame (assuming 60Hz) */
|
||||
if (!term->delayed_render_timer.is_armed) {
|
||||
timerfd_settime(
|
||||
term->delayed_render_timer.upper_fd, 0,
|
||||
&(struct itimerspec){.it_value = {.tv_nsec = 16666666}},
|
||||
NULL);
|
||||
term->delayed_render_timer.is_armed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return !(events & EPOLLHUP);
|
||||
}
|
||||
|
||||
static bool
|
||||
fdm_flash(struct fdm *fdm, int fd, int events, void *data)
|
||||
{
|
||||
if (events & EPOLLHUP)
|
||||
return false;
|
||||
|
||||
struct terminal *term = data;
|
||||
uint64_t expiration_count;
|
||||
ssize_t ret = read(
|
||||
term->flash.fd, &expiration_count, sizeof(expiration_count));
|
||||
|
||||
if (ret < 0) {
|
||||
if (errno == EAGAIN)
|
||||
return true;
|
||||
|
||||
LOG_ERRNO("failed to read flash timer");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_DBG("flash timer expired %llu times",
|
||||
(unsigned long long)expiration_count);
|
||||
|
||||
term->flash.active = false;
|
||||
term_damage_view(term);
|
||||
render_refresh(term);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
fdm_blink(struct fdm *fdm, int fd, int events, void *data)
|
||||
{
|
||||
if (events & EPOLLHUP)
|
||||
return false;
|
||||
|
||||
struct terminal *term = data;
|
||||
uint64_t expiration_count;
|
||||
ssize_t ret = read(
|
||||
term->blink.fd, &expiration_count, sizeof(expiration_count));
|
||||
|
||||
if (ret < 0) {
|
||||
if (errno == EAGAIN)
|
||||
return true;
|
||||
|
||||
LOG_ERRNO("failed to read blink timer");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_DBG("blink timer expired %llu times",
|
||||
(unsigned long long)expiration_count);
|
||||
|
||||
term->blink.state = term->blink.state == BLINK_ON
|
||||
? BLINK_OFF : BLINK_ON;
|
||||
|
||||
/* Scan all visible cells and mark rows with blinking cells dirty */
|
||||
for (int r = 0; r < term->rows; r++) {
|
||||
struct row *row = grid_row_in_view(term->grid, r);
|
||||
for (int col = 0; col < term->cols; col++) {
|
||||
struct cell *cell = &row->cells[col];
|
||||
|
||||
if (cell->attrs.blink) {
|
||||
cell->attrs.clean = 0;
|
||||
row->dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render_refresh(term);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
fdm_delayed_render(struct fdm *fdm, int fd, int events, void *data)
|
||||
{
|
||||
if (events & EPOLLHUP)
|
||||
return false;
|
||||
|
||||
struct terminal *term = data;
|
||||
assert(term->delayed_render_timer.is_armed);
|
||||
|
||||
uint64_t unused;
|
||||
ssize_t ret1 = 0;
|
||||
ssize_t ret2 = 0;
|
||||
|
||||
if (fd == term->delayed_render_timer.lower_fd)
|
||||
ret1 = read(term->delayed_render_timer.lower_fd, &unused, sizeof(unused));
|
||||
if (fd == term->delayed_render_timer.upper_fd)
|
||||
ret2 = read(term->delayed_render_timer.upper_fd, &unused, sizeof(unused));
|
||||
|
||||
if ((ret1 < 0 || ret2 < 0) && errno != EAGAIN)
|
||||
LOG_ERRNO("failed to read timeout timer");
|
||||
else if (ret1 > 0 || ret2 > 0) {
|
||||
render_refresh(term);
|
||||
|
||||
/* Reset timers */
|
||||
term->delayed_render_timer.is_armed = false;
|
||||
timerfd_settime(term->delayed_render_timer.lower_fd, 0, &(struct itimerspec){.it_value = {0}}, NULL);
|
||||
timerfd_settime(term->delayed_render_timer.upper_fd, 0, &(struct itimerspec){.it_value = {0}}, NULL);
|
||||
} else
|
||||
assert(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct terminal *
|
||||
term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl,
|
||||
int argc, char *const *argv)
|
||||
{
|
||||
int ptmx = -1;
|
||||
int flash_fd = -1;
|
||||
int blink_fd = -1;
|
||||
int delay_lower_fd = -1;
|
||||
int delay_upper_fd = -1;
|
||||
|
||||
struct terminal *term = NULL;
|
||||
|
||||
if ((ptmx = posix_openpt(O_RDWR | O_NOCTTY)) == -1) {
|
||||
LOG_ERRNO("failed to open PTY");
|
||||
goto close_fds;
|
||||
}
|
||||
if ((flash_fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK)) == -1) {
|
||||
LOG_ERRNO("failed to create flash timer FD");
|
||||
goto close_fds;
|
||||
}
|
||||
if ((blink_fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK)) == -1) {
|
||||
LOG_ERRNO("failed to create blink timer FD");
|
||||
goto close_fds;
|
||||
}
|
||||
if ((delay_lower_fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK)) == -1 ||
|
||||
(delay_upper_fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK)) == -1)
|
||||
{
|
||||
LOG_ERRNO("failed to create delayed rendering timer FDs");
|
||||
goto close_fds;
|
||||
}
|
||||
|
||||
term = malloc(sizeof(*term));
|
||||
*term = (struct terminal) {
|
||||
.fdm = fdm,
|
||||
.quit = false,
|
||||
.ptmx = ptmx,
|
||||
.cursor_keys_mode = CURSOR_KEYS_NORMAL,
|
||||
.keypad_keys_mode = KEYPAD_NUMERICAL,
|
||||
.auto_margin = true,
|
||||
.window_title_stack = tll_init(),
|
||||
.scale = 1,
|
||||
.flash = {.fd = flash_fd},
|
||||
.blink = {.fd = blink_fd},
|
||||
.vt = {
|
||||
.state = 1, /* STATE_GROUND */
|
||||
},
|
||||
.colors = {
|
||||
.default_fg = conf->colors.fg,
|
||||
.default_bg = conf->colors.bg,
|
||||
.default_table = {
|
||||
conf->colors.regular[0],
|
||||
conf->colors.regular[1],
|
||||
conf->colors.regular[2],
|
||||
conf->colors.regular[3],
|
||||
conf->colors.regular[4],
|
||||
conf->colors.regular[5],
|
||||
conf->colors.regular[6],
|
||||
conf->colors.regular[7],
|
||||
|
||||
conf->colors.bright[0],
|
||||
conf->colors.bright[1],
|
||||
conf->colors.bright[2],
|
||||
conf->colors.bright[3],
|
||||
conf->colors.bright[4],
|
||||
conf->colors.bright[5],
|
||||
conf->colors.bright[6],
|
||||
conf->colors.bright[7],
|
||||
},
|
||||
.alpha = conf->colors.alpha,
|
||||
},
|
||||
.default_cursor_style = conf->cursor.style,
|
||||
.cursor_style = conf->cursor.style,
|
||||
.default_cursor_color = {
|
||||
.text = conf->cursor.color.text,
|
||||
.cursor = conf->cursor.color.cursor,
|
||||
},
|
||||
.cursor_color = {
|
||||
.text = conf->cursor.color.text,
|
||||
.cursor = conf->cursor.color.cursor,
|
||||
},
|
||||
.selection = {
|
||||
.start = {-1, -1},
|
||||
.end = {-1, -1},
|
||||
},
|
||||
.normal = {.damage = tll_init(), .scroll_damage = tll_init()},
|
||||
.alt = {.damage = tll_init(), .scroll_damage = tll_init()},
|
||||
.grid = &term->normal,
|
||||
.wl = wayl,
|
||||
.render = {
|
||||
.scrollback_lines = conf->scrollback_lines,
|
||||
.workers = {
|
||||
.count = conf->render_worker_count,
|
||||
.queue = tll_init(),
|
||||
},
|
||||
},
|
||||
.delayed_render_timer = {
|
||||
.is_armed = false,
|
||||
.lower_fd = delay_lower_fd,
|
||||
.upper_fd = delay_upper_fd,
|
||||
},
|
||||
};
|
||||
|
||||
LOG_INFO("using %zu rendering threads", term->render.workers.count);
|
||||
|
||||
|
||||
/* Initialize 'current' colors from the default colors */
|
||||
term->colors.fg = term->colors.default_fg;
|
||||
term->colors.bg = term->colors.default_bg;
|
||||
|
||||
/* Initialize the 256 gray-scale color cube */
|
||||
{
|
||||
/* First 16 entries have already been initialized from conf */
|
||||
for (size_t r = 0; r < 6; r++) {
|
||||
for (size_t g = 0; g < 6; g++) {
|
||||
for (size_t b = 0; b < 6; b++) {
|
||||
term->colors.default_table[16 + r * 6 * 6 + g * 6 + b]
|
||||
= r * 51 << 16 | g * 51 << 8 | b * 51;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 24; i++)
|
||||
term->colors.default_table[232 + i] = i * 11 << 16 | i * 11 << 8 | i * 11;
|
||||
|
||||
memcpy(term->colors.table, term->colors.default_table, sizeof(term->colors.table));
|
||||
}
|
||||
|
||||
sem_init(&term->render.workers.start, 0, 0);
|
||||
sem_init(&term->render.workers.done, 0, 0);
|
||||
mtx_init(&term->render.workers.lock, mtx_plain);
|
||||
cnd_init(&term->render.workers.cond);
|
||||
|
||||
term->render.workers.threads = calloc(
|
||||
term->render.workers.count, sizeof(term->render.workers.threads[0]));
|
||||
|
||||
for (size_t i = 0; i < term->render.workers.count; i++) {
|
||||
struct render_worker_context *ctx = malloc(sizeof(*ctx));
|
||||
*ctx = (struct render_worker_context) {
|
||||
.term = term,
|
||||
.my_id = 1 + i,
|
||||
};
|
||||
thrd_create(&term->render.workers.threads[i], &render_worker_thread, ctx);
|
||||
}
|
||||
|
||||
font_list_t font_names = tll_init();
|
||||
tll_foreach(conf->fonts, it)
|
||||
tll_push_back(font_names, it->item);
|
||||
|
||||
if ((term->fonts[0] = font_from_name(font_names, "")) == NULL) {
|
||||
tll_free(font_names);
|
||||
goto err;
|
||||
}
|
||||
|
||||
term->fonts[1] = font_from_name(font_names, "style=bold");
|
||||
term->fonts[2] = font_from_name(font_names, "style=italic");
|
||||
term->fonts[3] = font_from_name(font_names, "style=bold italic");
|
||||
|
||||
tll_free(font_names);
|
||||
|
||||
{
|
||||
FT_Face ft_face = term->fonts[0]->face;
|
||||
int max_x_advance = ft_face->size->metrics.max_advance / 64;
|
||||
int height = ft_face->size->metrics.height / 64;
|
||||
int descent = ft_face->size->metrics.descender / 64;
|
||||
int ascent = ft_face->size->metrics.ascender / 64;
|
||||
|
||||
term->fextents.height = height * term->fonts[0]->pixel_size_fixup;
|
||||
term->fextents.descent = -descent * term->fonts[0]->pixel_size_fixup;
|
||||
term->fextents.ascent = ascent * term->fonts[0]->pixel_size_fixup;
|
||||
term->fextents.max_x_advance = max_x_advance * term->fonts[0]->pixel_size_fixup;
|
||||
|
||||
LOG_DBG("metrics: height: %d, descent: %d, ascent: %d, x-advance: %d",
|
||||
height, descent, ascent, max_x_advance);
|
||||
}
|
||||
|
||||
term->cell_width = (int)ceil(term->fextents.max_x_advance);
|
||||
term->cell_height = (int)ceil(term->fextents.height);
|
||||
LOG_INFO("cell width=%d, height=%d", term->cell_width, term->cell_height);
|
||||
|
||||
term->window = wayl_win_init(wayl);
|
||||
if (term->window == NULL)
|
||||
goto err;
|
||||
|
||||
term_set_window_title(term, "foot");
|
||||
|
||||
unsigned width = conf->width;
|
||||
unsigned height = conf->height;
|
||||
|
||||
if (width == -1) {
|
||||
assert(height == -1);
|
||||
width = 80 * term->cell_width;
|
||||
height = 24 * term->cell_height;
|
||||
}
|
||||
|
||||
width = max(width, term->cell_width);
|
||||
height = max(height, term->cell_height);
|
||||
render_resize(term, width, height);
|
||||
|
||||
{
|
||||
int fork_pipe[2];
|
||||
if (pipe2(fork_pipe, O_CLOEXEC) < 0) {
|
||||
LOG_ERRNO("failed to create pipe");
|
||||
goto err;
|
||||
}
|
||||
|
||||
term->slave = fork();
|
||||
switch (term->slave) {
|
||||
case -1:
|
||||
LOG_ERRNO("failed to fork");
|
||||
close(fork_pipe[0]);
|
||||
close(fork_pipe[1]);
|
||||
goto err;
|
||||
|
||||
case 0:
|
||||
/* Child */
|
||||
close(fork_pipe[0]); /* Close read end */
|
||||
|
||||
char **_shell_argv = NULL;
|
||||
char *const *shell_argv = argv;
|
||||
|
||||
if (argc == 0) {
|
||||
if (!tokenize_cmdline(conf->shell, &_shell_argv)) {
|
||||
(void)!write(fork_pipe[1], &errno, sizeof(errno));
|
||||
_exit(0);
|
||||
}
|
||||
shell_argv = _shell_argv;
|
||||
}
|
||||
|
||||
slave_exec(ptmx, shell_argv, fork_pipe[1]);
|
||||
assert(false);
|
||||
break;
|
||||
|
||||
default: {
|
||||
close(fork_pipe[1]); /* Close write end */
|
||||
LOG_DBG("slave has PID %d", term->slave);
|
||||
|
||||
int _errno;
|
||||
static_assert(sizeof(errno) == sizeof(_errno), "errno size mismatch");
|
||||
|
||||
ssize_t ret = read(fork_pipe[0], &_errno, sizeof(_errno));
|
||||
close(fork_pipe[0]);
|
||||
|
||||
if (ret < 0) {
|
||||
LOG_ERRNO("failed to read from pipe");
|
||||
goto err;
|
||||
} else if (ret == sizeof(_errno)) {
|
||||
LOG_ERRNO(
|
||||
"%s: failed to execute", argc == 0 ? conf->shell : argv[0]);
|
||||
goto err;
|
||||
} else
|
||||
LOG_DBG("%s: successfully started", conf->shell);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Read logic requires non-blocking mode */
|
||||
{
|
||||
int fd_flags = fcntl(ptmx, F_GETFL);
|
||||
if (fd_flags == -1) {
|
||||
LOG_ERRNO("failed to set non blocking mode on PTY master");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (fcntl(ptmx, F_SETFL, fd_flags | O_NONBLOCK) == -1) {
|
||||
LOG_ERRNO("failed to set non blocking mode on PTY master");
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (!fdm_add(fdm, ptmx, EPOLLIN, &fdm_ptmx, term)) {
|
||||
LOG_ERR("failed to add ptmx to FDM");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!fdm_add(fdm, flash_fd, EPOLLIN, &fdm_flash, term)) {
|
||||
LOG_ERR("failed to add flash timer FD to FDM");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!fdm_add(fdm, blink_fd, EPOLLIN, &fdm_blink, term)) {
|
||||
LOG_ERR("failed to add blink tiemr FD to FDM");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!fdm_add(fdm, delay_lower_fd, EPOLLIN, &fdm_delayed_render, term) ||
|
||||
!fdm_add(fdm, delay_upper_fd, EPOLLIN, &fdm_delayed_render, term))
|
||||
{
|
||||
LOG_ERR("failed to add delayed rendering timer FDs to FDM");
|
||||
goto err;
|
||||
}
|
||||
|
||||
wayl->term = term;
|
||||
return term;
|
||||
|
||||
err:
|
||||
term_destroy(term);
|
||||
return NULL;
|
||||
|
||||
close_fds:
|
||||
if (ptmx != -1)
|
||||
close(ptmx);
|
||||
if (flash_fd != -1)
|
||||
close(flash_fd);
|
||||
if (blink_fd != -1)
|
||||
close(blink_fd);
|
||||
if (delay_lower_fd != -1)
|
||||
close(delay_lower_fd);
|
||||
if (delay_upper_fd != -1)
|
||||
close(delay_upper_fd);
|
||||
assert(term == NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
term_destroy(struct terminal *term)
|
||||
{
|
||||
if (term == NULL)
|
||||
return 0;
|
||||
|
||||
wayl_win_destroy(term->window);
|
||||
|
||||
if (term->delayed_render_timer.lower_fd != -1) {
|
||||
fdm_del(term->fdm, term->delayed_render_timer.lower_fd);
|
||||
close(term->delayed_render_timer.lower_fd);
|
||||
}
|
||||
|
||||
if (term->delayed_render_timer.upper_fd != -1) {
|
||||
fdm_del(term->fdm, term->delayed_render_timer.upper_fd);
|
||||
close(term->delayed_render_timer.upper_fd);
|
||||
}
|
||||
|
||||
mtx_lock(&term->render.workers.lock);
|
||||
assert(tll_length(term->render.workers.queue) == 0);
|
||||
for (size_t i = 0; i < term->render.workers.count; i++) {
|
||||
sem_post(&term->render.workers.start);
|
||||
tll_push_back(term->render.workers.queue, -2);
|
||||
}
|
||||
cnd_broadcast(&term->render.workers.cond);
|
||||
mtx_unlock(&term->render.workers.lock);
|
||||
free(term->vt.osc.data);
|
||||
for (int row = 0; row < term->normal.num_rows; row++)
|
||||
grid_row_free(term->normal.rows[row]);
|
||||
free(term->normal.rows);
|
||||
for (int row = 0; row < term->alt.num_rows; row++)
|
||||
grid_row_free(term->alt.rows[row]);
|
||||
free(term->alt.rows);
|
||||
|
||||
free(term->window_title);
|
||||
tll_free_and_free(term->window_title_stack, free);
|
||||
|
||||
for (size_t i = 0; i < sizeof(term->fonts) / sizeof(term->fonts[0]); i++)
|
||||
font_destroy(term->fonts[i]);
|
||||
|
||||
free(term->search.buf);
|
||||
|
||||
if (term->flash.fd != -1) {
|
||||
fdm_del(term->fdm, term->flash.fd);
|
||||
close(term->flash.fd);
|
||||
}
|
||||
|
||||
if (term->blink.fd != -1) {
|
||||
fdm_del(term->fdm, term->blink.fd);
|
||||
close(term->blink.fd);
|
||||
}
|
||||
|
||||
if (term->ptmx != -1) {
|
||||
fdm_del(term->fdm, term->ptmx);
|
||||
close(term->ptmx);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < term->render.workers.count; i++)
|
||||
thrd_join(term->render.workers.threads[i], NULL);
|
||||
free(term->render.workers.threads);
|
||||
cnd_destroy(&term->render.workers.cond);
|
||||
mtx_destroy(&term->render.workers.lock);
|
||||
sem_destroy(&term->render.workers.start);
|
||||
sem_destroy(&term->render.workers.done);
|
||||
assert(tll_length(term->render.workers.queue) == 0);
|
||||
tll_free(term->render.workers.queue);
|
||||
|
||||
int ret = EXIT_SUCCESS;
|
||||
|
||||
if (term->slave > 0) {
|
||||
/* Note: we've closed ptmx, so the slave *should* exit... */
|
||||
int status;
|
||||
waitpid(term->slave, &status, 0);
|
||||
|
||||
int child_ret = EXIT_FAILURE;
|
||||
if (WIFEXITED(status)) {
|
||||
child_ret = WEXITSTATUS(status);
|
||||
LOG_DBG("slave exited with code %d", child_ret);
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
child_ret = WTERMSIG(status);
|
||||
LOG_WARN("slave exited with signal %d", child_ret);
|
||||
} else {
|
||||
LOG_WARN("slave exited for unknown reason (status = 0x%08x)", status);
|
||||
}
|
||||
|
||||
ret = child_ret;
|
||||
}
|
||||
|
||||
free(term);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
term_reset(struct terminal *term, bool hard)
|
||||
{
|
||||
|
|
@ -480,7 +1076,7 @@ void
|
|||
term_mouse_down(struct terminal *term, int button, int row, int col,
|
||||
bool shift, bool alt, bool ctrl)
|
||||
{
|
||||
if (term->kbd.shift) {
|
||||
if (term->wl->kbd.shift) {
|
||||
/* "raw" mouse mode */
|
||||
return;
|
||||
}
|
||||
|
|
@ -513,7 +1109,7 @@ void
|
|||
term_mouse_up(struct terminal *term, int button, int row, int col,
|
||||
bool shift, bool alt, bool ctrl)
|
||||
{
|
||||
if (term->kbd.shift) {
|
||||
if (term->wl->kbd.shift) {
|
||||
/* "raw" mouse mode */
|
||||
return;
|
||||
}
|
||||
|
|
@ -551,7 +1147,7 @@ void
|
|||
term_mouse_motion(struct terminal *term, int button, int row, int col,
|
||||
bool shift, bool alt, bool ctrl)
|
||||
{
|
||||
if (term->kbd.shift) {
|
||||
if (term->wl->kbd.shift) {
|
||||
/* "raw" mouse mode */
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
152
terminal.h
152
terminal.h
|
|
@ -8,83 +8,15 @@
|
|||
#include <threads.h>
|
||||
#include <semaphore.h>
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include <primary-selection-unstable-v1.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <xkbcommon/xkbcommon-keysyms.h>
|
||||
|
||||
//#include "config.h"
|
||||
#include "fdm.h"
|
||||
#include "font.h"
|
||||
#include "tllist.h"
|
||||
#include "wayland.h"
|
||||
|
||||
#define likely(c) __builtin_expect(!!(c), 1)
|
||||
#define unlikely(c) __builtin_expect(!!(c), 0)
|
||||
|
||||
struct monitor {
|
||||
struct terminal *term;
|
||||
struct wl_output *output;
|
||||
struct zxdg_output_v1 *xdg;
|
||||
char *name;
|
||||
|
||||
int x;
|
||||
int y;
|
||||
|
||||
int width_mm;
|
||||
int height_mm;
|
||||
|
||||
int width_px;
|
||||
int height_px;
|
||||
|
||||
int scale;
|
||||
float refresh;
|
||||
};
|
||||
|
||||
struct wayland {
|
||||
struct wl_display *display;
|
||||
struct wl_registry *registry;
|
||||
struct wl_compositor *compositor;
|
||||
struct wl_subcompositor *sub_compositor;
|
||||
struct wl_shm *shm;
|
||||
|
||||
struct wl_seat *seat;
|
||||
struct wl_keyboard *keyboard;
|
||||
struct zxdg_output_manager_v1 *xdg_output_manager;
|
||||
|
||||
/* Clipboard */
|
||||
struct wl_data_device_manager *data_device_manager;
|
||||
struct wl_data_device *data_device;
|
||||
struct zwp_primary_selection_device_manager_v1 *primary_selection_device_manager;
|
||||
struct zwp_primary_selection_device_v1 *primary_selection_device;
|
||||
|
||||
/* Cursor */
|
||||
struct {
|
||||
struct wl_pointer *pointer;
|
||||
uint32_t serial;
|
||||
|
||||
struct wl_surface *surface;
|
||||
struct wl_cursor_theme *theme;
|
||||
struct wl_cursor *cursor;
|
||||
int size;
|
||||
char *theme_name;
|
||||
} pointer;
|
||||
|
||||
/* Main window */
|
||||
struct wl_surface *surface;
|
||||
struct xdg_wm_base *shell;
|
||||
struct xdg_surface *xdg_surface;
|
||||
struct xdg_toplevel *xdg_toplevel;
|
||||
|
||||
struct zxdg_decoration_manager_v1 *xdg_decoration_manager;
|
||||
struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration;
|
||||
|
||||
/* Scrollback search */
|
||||
struct wl_surface *search_surface;
|
||||
struct wl_subsurface *search_sub_surface;
|
||||
|
||||
bool have_argb8888;
|
||||
tll(struct monitor) monitors; /* All available outputs */
|
||||
tll(const struct monitor *) on_outputs; /* Outputs we're mapped on */
|
||||
};
|
||||
|
||||
struct rgb { float r, g, b; };
|
||||
|
||||
/*
|
||||
|
|
@ -190,33 +122,6 @@ struct vt {
|
|||
struct attributes saved_attrs;
|
||||
};
|
||||
|
||||
struct kbd {
|
||||
struct xkb_context *xkb;
|
||||
struct xkb_keymap *xkb_keymap;
|
||||
struct xkb_state *xkb_state;
|
||||
struct xkb_compose_table *xkb_compose_table;
|
||||
struct xkb_compose_state *xkb_compose_state;
|
||||
struct {
|
||||
int fd;
|
||||
|
||||
bool dont_re_repeat;
|
||||
int32_t delay;
|
||||
int32_t rate;
|
||||
uint32_t key;
|
||||
} repeat;
|
||||
|
||||
xkb_mod_index_t mod_shift;
|
||||
xkb_mod_index_t mod_alt;
|
||||
xkb_mod_index_t mod_ctrl;
|
||||
xkb_mod_index_t mod_meta;
|
||||
|
||||
/* Enabled modifiers */
|
||||
bool shift;
|
||||
bool alt;
|
||||
bool ctrl;
|
||||
bool meta;
|
||||
};
|
||||
|
||||
enum cursor_keys { CURSOR_KEYS_DONTCARE, CURSOR_KEYS_NORMAL, CURSOR_KEYS_APPLICATION};
|
||||
enum keypad_keys { KEYPAD_DONTCARE, KEYPAD_NUMERICAL, KEYPAD_APPLICATION };
|
||||
enum charset { CHARSET_ASCII, CHARSET_GRAPHIC };
|
||||
|
|
@ -238,23 +143,11 @@ enum mouse_reporting {
|
|||
MOUSE_URXVT, /* ?1015h */
|
||||
};
|
||||
|
||||
struct clipboard {
|
||||
struct wl_data_source *data_source;
|
||||
struct wl_data_offer *data_offer;
|
||||
char *text;
|
||||
uint32_t serial;
|
||||
};
|
||||
|
||||
struct primary {
|
||||
struct zwp_primary_selection_source_v1 *data_source;
|
||||
struct zwp_primary_selection_offer_v1 *data_offer;
|
||||
char *text;
|
||||
uint32_t serial;
|
||||
};
|
||||
|
||||
enum cursor_style { CURSOR_BLOCK, CURSOR_UNDERLINE, CURSOR_BAR };
|
||||
|
||||
struct terminal {
|
||||
struct fdm *fdm;
|
||||
|
||||
pid_t slave;
|
||||
int ptmx;
|
||||
bool quit;
|
||||
|
|
@ -288,7 +181,6 @@ struct terminal {
|
|||
} blink;
|
||||
|
||||
struct vt vt;
|
||||
struct kbd kbd;
|
||||
|
||||
int scale;
|
||||
int width; /* pixels */
|
||||
|
|
@ -314,19 +206,6 @@ struct terminal {
|
|||
uint32_t default_table[256];
|
||||
} colors;
|
||||
|
||||
struct {
|
||||
int col;
|
||||
int row;
|
||||
int button;
|
||||
|
||||
int count;
|
||||
int last_button;
|
||||
struct timeval last_time;
|
||||
|
||||
/* We used a discrete axis event in the current pointer frame */
|
||||
bool have_discrete;
|
||||
} mouse;
|
||||
|
||||
struct coord cursor;
|
||||
struct coord saved_cursor;
|
||||
struct coord alt_saved_cursor;
|
||||
|
|
@ -342,12 +221,9 @@ struct terminal {
|
|||
uint32_t cursor;
|
||||
} cursor_color;
|
||||
|
||||
uint32_t input_serial;
|
||||
struct {
|
||||
struct coord start;
|
||||
struct coord end;
|
||||
struct clipboard clipboard;
|
||||
struct primary primary;
|
||||
} selection;
|
||||
|
||||
bool is_searching;
|
||||
|
|
@ -376,10 +252,11 @@ struct terminal {
|
|||
int max_x_advance;
|
||||
} fextents;
|
||||
|
||||
struct wayland wl;
|
||||
struct wayland *wl;
|
||||
struct wl_window *window;
|
||||
|
||||
struct {
|
||||
int scrollback_lines;
|
||||
struct wl_callback *frame_callback;
|
||||
|
||||
struct {
|
||||
size_t count;
|
||||
|
|
@ -403,8 +280,21 @@ struct terminal {
|
|||
bool was_flashing; /* Flash was active last time we rendered */
|
||||
bool was_searching;
|
||||
} render;
|
||||
|
||||
/* Temporary: for FDM */
|
||||
struct {
|
||||
bool is_armed;
|
||||
int lower_fd;
|
||||
int upper_fd;
|
||||
} delayed_render_timer;
|
||||
};
|
||||
|
||||
struct config;
|
||||
struct terminal *term_init(
|
||||
const struct config *conf, struct fdm *fdm, struct wayland *wayl,
|
||||
int argc, char *const *argv);
|
||||
int term_destroy(struct terminal *term);
|
||||
|
||||
void term_reset(struct terminal *term, bool hard);
|
||||
|
||||
void term_damage_rows(struct terminal *term, int start, int end);
|
||||
|
|
|
|||
776
wayland.c
Normal file
776
wayland.c
Normal file
|
|
@ -0,0 +1,776 @@
|
|||
#include "wayland.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/timerfd.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include <wayland-cursor.h>
|
||||
#include <xdg-shell.h>
|
||||
#include <xkbcommon/xkbcommon-compose.h>
|
||||
|
||||
#include <xdg-output-unstable-v1.h>
|
||||
#include <xdg-decoration-unstable-v1.h>
|
||||
|
||||
#define LOG_MODULE "wayland"
|
||||
#define LOG_ENABLE_DBG 0
|
||||
#include "log.h"
|
||||
|
||||
#include "tllist.h"
|
||||
#include "terminal.h"
|
||||
#include "input.h"
|
||||
#include "render.h"
|
||||
#include "selection.h"
|
||||
|
||||
#define min(x, y) ((x) < (y) ? (x) : (y))
|
||||
#define max(x, y) ((x) > (y) ? (x) : (y))
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
struct terminal *term = mon->wayl->term;
|
||||
if (term != NULL) {
|
||||
render_resize(term, term->width / term->scale, term->height / term->scale);
|
||||
wayl_reload_cursor_theme(mon->wayl, term);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
||||
static struct zxdg_output_v1_listener xdg_output_listener = {
|
||||
.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,
|
||||
};
|
||||
|
||||
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) {
|
||||
wayl->compositor = wl_registry_bind(
|
||||
wayl->registry, name, &wl_compositor_interface, 4);
|
||||
}
|
||||
|
||||
else if (strcmp(interface, wl_subcompositor_interface.name) == 0) {
|
||||
wayl->sub_compositor = wl_registry_bind(
|
||||
wayl->registry, name, &wl_subcompositor_interface, 1);
|
||||
}
|
||||
|
||||
else if (strcmp(interface, wl_shm_interface.name) == 0) {
|
||||
wayl->shm = wl_registry_bind(
|
||||
wayl->registry, name, &wl_shm_interface, 1);
|
||||
wl_shm_add_listener(wayl->shm, &shm_listener, wayl);
|
||||
wl_display_roundtrip(wayl->display);
|
||||
}
|
||||
|
||||
else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
|
||||
wayl->shell = wl_registry_bind(
|
||||
wayl->registry, name, &xdg_wm_base_interface, 1);
|
||||
xdg_wm_base_add_listener(wayl->shell, &xdg_wm_base_listener, wayl);
|
||||
}
|
||||
|
||||
else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0)
|
||||
wayl->xdg_decoration_manager = wl_registry_bind(
|
||||
wayl->registry, name, &zxdg_decoration_manager_v1_interface, 1);
|
||||
|
||||
else if (strcmp(interface, wl_seat_interface.name) == 0) {
|
||||
wayl->seat = wl_registry_bind(
|
||||
wayl->registry, name, &wl_seat_interface, 5);
|
||||
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) {
|
||||
wayl->xdg_output_manager = wl_registry_bind(
|
||||
wayl->registry, name, &zxdg_output_manager_v1_interface, min(version, 2));
|
||||
}
|
||||
|
||||
else if (strcmp(interface, wl_output_interface.name) == 0) {
|
||||
struct wl_output *output = wl_registry_bind(
|
||||
wayl->registry, name, &wl_output_interface, 3);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
wl_display_roundtrip(wayl->display);
|
||||
}
|
||||
|
||||
else if (strcmp(interface, wl_data_device_manager_interface.name) == 0) {
|
||||
wayl->data_device_manager = wl_registry_bind(
|
||||
wayl->registry, name, &wl_data_device_manager_interface, 1);
|
||||
}
|
||||
|
||||
else if (strcmp(interface, zwp_primary_selection_device_manager_v1_interface.name) == 0) {
|
||||
wayl->primary_selection_device_manager = wl_registry_bind(
|
||||
wayl->registry, name, &zwp_primary_selection_device_manager_v1_interface, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
surface_enter(void *data, struct wl_surface *wl_surface,
|
||||
struct wl_output *wl_output)
|
||||
{
|
||||
struct wayland *wayl = data;
|
||||
struct terminal *term = wayl_terminal_from_surface(wayl, wl_surface);
|
||||
|
||||
tll_foreach(wayl->monitors, it) {
|
||||
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 */
|
||||
render_resize(term, term->width / term->scale, term->height / term->scale);
|
||||
wayl_reload_cursor_theme(wayl, term);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_ERR("mapped on unknown output");
|
||||
}
|
||||
|
||||
static void
|
||||
surface_leave(void *data, struct wl_surface *wl_surface,
|
||||
struct wl_output *wl_output)
|
||||
{
|
||||
struct wayland *wayl = data;
|
||||
struct terminal *term = wayl_terminal_from_surface(wayl, wl_surface);
|
||||
|
||||
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 */
|
||||
render_resize(term, term->width / term->scale, term->height / term->scale);
|
||||
wayl_reload_cursor_theme(wayl, term);
|
||||
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)
|
||||
{
|
||||
LOG_DBG("xdg-toplevel: configure: %dx%d", width, height);
|
||||
|
||||
if (width <= 0 || height <= 0)
|
||||
return;
|
||||
|
||||
struct wayland *wayl = data;
|
||||
struct terminal *term = wayl_terminal_from_xdg_toplevel(wayl, xdg_toplevel);
|
||||
render_resize(term, width, height);
|
||||
}
|
||||
|
||||
static void
|
||||
xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel)
|
||||
{
|
||||
struct wayland *wayl = data;
|
||||
struct terminal *term = wayl_terminal_from_xdg_toplevel(wayl, xdg_toplevel);
|
||||
LOG_DBG("xdg-toplevel: close");
|
||||
|
||||
term->quit = true;
|
||||
wl_display_roundtrip(wayl->display);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
//LOG_DBG("xdg-surface: configure");
|
||||
xdg_surface_ack_configure(xdg_surface, serial);
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
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) {
|
||||
LOG_ERR("disconnected from Wayland");
|
||||
return false;
|
||||
}
|
||||
|
||||
return event_count != -1 && !wayl->term->quit;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (errno == EAGAIN)
|
||||
return true;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
struct wayland *
|
||||
wayl_init(struct fdm *fdm)
|
||||
{
|
||||
struct wayland *wayl = calloc(1, sizeof(*wayl));
|
||||
wayl->fdm = fdm;
|
||||
|
||||
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;
|
||||
}
|
||||
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->have_argb8888) {
|
||||
LOG_ERR("compositor does not support ARGB surfaces");
|
||||
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;
|
||||
}
|
||||
if (wayl->primary_selection_device_manager == NULL)
|
||||
LOG_WARN("no primary selection available");
|
||||
|
||||
tll_foreach(wayl->monitors, it) {
|
||||
LOG_INFO("%s: %dx%d+%dx%d (scale=%d, refresh=%.2fHz)",
|
||||
it->item.name, it->item.width_px, it->item.height_px,
|
||||
it->item.x, it->item.y, it->item.scale, it->item.refresh);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
wayl->kbd.repeat.fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK);
|
||||
if (wayl->kbd.repeat.fd == -1) {
|
||||
LOG_ERRNO("failed to create keyboard repeat timer FD");
|
||||
goto out;
|
||||
}
|
||||
|
||||
int wl_fd = wl_display_get_fd(wayl->display);
|
||||
int fd_flags = fcntl(wl_fd, F_GETFL);
|
||||
if (fd_flags == -1) {
|
||||
LOG_ERRNO("failed to set non blocking mode on Wayland display connection");
|
||||
goto out;
|
||||
}
|
||||
if (fcntl(wl_fd, F_SETFL, fd_flags | O_NONBLOCK) == -1) {
|
||||
LOG_ERRNO("failed to set non blocking mode on Wayland display connection");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!fdm_add(fdm, wl_fd, EPOLLIN, &fdm_wayl, wayl)) {
|
||||
LOG_ERR("failed to register Wayland connection with the FDM");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!fdm_add(fdm, wayl->kbd.repeat.fd, EPOLLIN, &fdm_repeat, wayl)) {
|
||||
LOG_ERR("failed to register keyboard repeat timer with the FDM");
|
||||
goto out;
|
||||
}
|
||||
|
||||
//wl_display_dispatch_pending(wayl->display);
|
||||
//wl_display_flush(wayl->display);
|
||||
return wayl;
|
||||
|
||||
out:
|
||||
if (wayl != NULL)
|
||||
wayl_destroy(wayl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
wayl_destroy(struct wayland *wayl)
|
||||
{
|
||||
if (wayl == NULL)
|
||||
return;
|
||||
|
||||
if (wayl->kbd.repeat.fd != 0) {
|
||||
fdm_del(wayl->fdm, wayl->kbd.repeat.fd);
|
||||
close(wayl->kbd.repeat.fd);
|
||||
}
|
||||
|
||||
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);
|
||||
tll_remove(wayl->monitors, it);
|
||||
}
|
||||
|
||||
if (wayl->xdg_output_manager != NULL)
|
||||
zxdg_output_manager_v1_destroy(wayl->xdg_output_manager);
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
if (wayl->display != NULL) {
|
||||
fdm_del(wayl->fdm, wl_display_get_fd(wayl->display));
|
||||
wl_display_disconnect(wayl->display);
|
||||
}
|
||||
|
||||
free(wayl);
|
||||
}
|
||||
|
||||
struct wl_window *
|
||||
wayl_win_init(struct wayland *wayl)
|
||||
{
|
||||
struct wl_window *win = calloc(1, sizeof(*win));
|
||||
|
||||
win->surface = wl_compositor_create_surface(wayl->compositor);
|
||||
if (win->surface == NULL) {
|
||||
LOG_ERR("failed to create wayland surface");
|
||||
goto out;
|
||||
}
|
||||
|
||||
wl_surface_add_listener(win->surface, &surface_listener, wayl);
|
||||
|
||||
win->xdg_surface = xdg_wm_base_get_xdg_surface(wayl->shell, win->surface);
|
||||
xdg_surface_add_listener(win->xdg_surface, &xdg_surface_listener, wayl);
|
||||
|
||||
win->xdg_toplevel = xdg_surface_get_toplevel(win->xdg_surface);
|
||||
xdg_toplevel_add_listener(win->xdg_toplevel, &xdg_toplevel_listener, wayl);
|
||||
|
||||
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(
|
||||
win->xdg_toplevel_decoration, &xdg_toplevel_decoration_listener, wayl);
|
||||
|
||||
/* 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);
|
||||
wl_display_roundtrip(wayl->display);
|
||||
|
||||
return win;
|
||||
|
||||
out:
|
||||
if (win != NULL)
|
||||
wayl_win_destroy(win);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
wayl_win_destroy(struct wl_window *win)
|
||||
{
|
||||
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);
|
||||
free(win);
|
||||
}
|
||||
|
||||
bool
|
||||
wayl_reload_cursor_theme(struct wayland *wayl, 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;
|
||||
}
|
||||
|
||||
wayl->pointer.cursor = wl_cursor_theme_get_cursor(
|
||||
wayl->pointer.theme, "left_ptr");
|
||||
assert(wayl->pointer.cursor != NULL);
|
||||
wayl_update_cursor_surface(wayl, term);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
wayl_update_cursor_surface(struct wayland *wayl, struct terminal *term)
|
||||
{
|
||||
if (wayl->pointer.cursor == NULL)
|
||||
return;
|
||||
|
||||
const int scale = term->scale;
|
||||
wl_surface_set_buffer_scale(wayl->pointer.surface, 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);
|
||||
|
||||
wl_surface_commit(wayl->pointer.surface);
|
||||
}
|
||||
|
||||
|
||||
struct terminal *
|
||||
wayl_terminal_from_surface(struct wayland *wayl, struct wl_surface *surface)
|
||||
{
|
||||
assert(surface == wayl->term->window->surface);
|
||||
return wayl->term;
|
||||
}
|
||||
|
||||
struct terminal *
|
||||
wayl_terminal_from_xdg_toplevel(struct wayland *wayl,
|
||||
struct xdg_toplevel *toplevel)
|
||||
{
|
||||
assert(toplevel == wayl->term->window->xdg_toplevel);
|
||||
return wayl->term;
|
||||
}
|
||||
169
wayland.h
Normal file
169
wayland.h
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include <primary-selection-unstable-v1.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
|
||||
#include "fdm.h"
|
||||
#include "tllist.h"
|
||||
|
||||
struct monitor {
|
||||
struct wayland *wayl;
|
||||
struct wl_output *output;
|
||||
struct zxdg_output_v1 *xdg;
|
||||
char *name;
|
||||
|
||||
int x;
|
||||
int y;
|
||||
|
||||
int width_mm;
|
||||
int height_mm;
|
||||
|
||||
int width_px;
|
||||
int height_px;
|
||||
|
||||
int scale;
|
||||
float refresh;
|
||||
};
|
||||
|
||||
struct kbd {
|
||||
struct xkb_context *xkb;
|
||||
struct xkb_keymap *xkb_keymap;
|
||||
struct xkb_state *xkb_state;
|
||||
struct xkb_compose_table *xkb_compose_table;
|
||||
struct xkb_compose_state *xkb_compose_state;
|
||||
struct {
|
||||
int fd;
|
||||
|
||||
bool dont_re_repeat;
|
||||
int32_t delay;
|
||||
int32_t rate;
|
||||
uint32_t key;
|
||||
} repeat;
|
||||
|
||||
xkb_mod_index_t mod_shift;
|
||||
xkb_mod_index_t mod_alt;
|
||||
xkb_mod_index_t mod_ctrl;
|
||||
xkb_mod_index_t mod_meta;
|
||||
|
||||
/* Enabled modifiers */
|
||||
bool shift;
|
||||
bool alt;
|
||||
bool ctrl;
|
||||
bool meta;
|
||||
};
|
||||
|
||||
struct wl_clipboard {
|
||||
struct wl_data_source *data_source;
|
||||
struct wl_data_offer *data_offer;
|
||||
char *text;
|
||||
uint32_t serial;
|
||||
};
|
||||
|
||||
struct wl_primary {
|
||||
struct zwp_primary_selection_source_v1 *data_source;
|
||||
struct zwp_primary_selection_offer_v1 *data_offer;
|
||||
char *text;
|
||||
uint32_t serial;
|
||||
};
|
||||
|
||||
struct wl_window {
|
||||
struct wl_surface *surface;
|
||||
struct xdg_surface *xdg_surface;
|
||||
struct xdg_toplevel *xdg_toplevel;
|
||||
|
||||
struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration;
|
||||
|
||||
/* Scrollback search */
|
||||
struct wl_surface *search_surface;
|
||||
struct wl_subsurface *search_sub_surface;
|
||||
|
||||
struct wl_callback *frame_callback;
|
||||
|
||||
tll(const struct monitor *) on_outputs; /* Outputs we're mapped on */
|
||||
};
|
||||
|
||||
struct terminal;
|
||||
struct wayland {
|
||||
struct fdm *fdm;
|
||||
struct wl_display *display;
|
||||
struct wl_registry *registry;
|
||||
struct wl_compositor *compositor;
|
||||
struct wl_subcompositor *sub_compositor;
|
||||
struct wl_shm *shm;
|
||||
|
||||
struct wl_seat *seat;
|
||||
struct wl_keyboard *keyboard;
|
||||
struct zxdg_output_manager_v1 *xdg_output_manager;
|
||||
|
||||
struct xdg_wm_base *shell;
|
||||
struct zxdg_decoration_manager_v1 *xdg_decoration_manager;
|
||||
|
||||
/* Keyboard */
|
||||
struct kbd kbd;
|
||||
|
||||
/* Clipboard */
|
||||
uint32_t input_serial;
|
||||
struct wl_data_device_manager *data_device_manager;
|
||||
struct wl_data_device *data_device;
|
||||
struct zwp_primary_selection_device_manager_v1 *primary_selection_device_manager;
|
||||
struct zwp_primary_selection_device_v1 *primary_selection_device;
|
||||
|
||||
struct wl_clipboard clipboard;
|
||||
struct wl_primary primary;
|
||||
|
||||
/* Cursor */
|
||||
struct {
|
||||
struct wl_pointer *pointer;
|
||||
uint32_t serial;
|
||||
|
||||
struct wl_surface *surface;
|
||||
struct wl_cursor_theme *theme;
|
||||
struct wl_cursor *cursor;
|
||||
int size;
|
||||
char *theme_name;
|
||||
} pointer;
|
||||
|
||||
struct {
|
||||
int col;
|
||||
int row;
|
||||
int button;
|
||||
|
||||
int count;
|
||||
int last_button;
|
||||
struct timeval last_time;
|
||||
|
||||
/* We used a discrete axis event in the current pointer frame */
|
||||
bool have_discrete;
|
||||
} mouse;
|
||||
|
||||
bool have_argb8888;
|
||||
tll(struct monitor) monitors; /* All available outputs */
|
||||
|
||||
/* TODO: turn into a list to support multiple windows */
|
||||
struct terminal *term;
|
||||
struct terminal *focused;
|
||||
struct terminal *moused;
|
||||
};
|
||||
|
||||
struct wayland *wayl_init(struct fdm *fdm);
|
||||
void wayl_destroy(struct wayland *wayl);
|
||||
|
||||
struct terminal *wayl_terminal_from_surface(
|
||||
struct wayland *wayl, struct wl_surface *surface);
|
||||
struct terminal *wayl_terminal_from_xdg_surface(
|
||||
struct wayland *wayl, struct xdg_surface *surface);
|
||||
struct terminal *wayl_terminal_from_xdg_toplevel(
|
||||
struct wayland *wayl, struct xdg_toplevel *toplevel);
|
||||
|
||||
/* TODO: pass something other than 'term'? Need scale... */
|
||||
bool wayl_reload_cursor_theme(struct wayland *wayl, struct terminal *term);
|
||||
void wayl_update_cursor_surface(struct wayland *wayl, struct terminal *term);
|
||||
|
||||
struct wl_window *wayl_win_init(struct wayland *wayl);
|
||||
void wayl_win_destroy(struct wl_window *win);
|
||||
Loading…
Add table
Add a link
Reference in a new issue