mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-05 04:06:08 -05:00
wip: initial input handling
This commit is contained in:
parent
3bd77bceb1
commit
71dde121e6
9 changed files with 484 additions and 51 deletions
101
csi.c
101
csi.c
|
|
@ -107,51 +107,124 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
||||||
if (term->vt.intermediates.idx == 0) {
|
if (term->vt.intermediates.idx == 0) {
|
||||||
switch (final) {
|
switch (final) {
|
||||||
case 'c':
|
case 'c':
|
||||||
write(term->ptmx, "\033[?6c", 5);
|
return write(term->ptmx, "\033[?6c", 5) == 5;
|
||||||
return true;
|
|
||||||
|
|
||||||
case 'm':
|
case 'm':
|
||||||
return csi_sgr(term);
|
return csi_sgr(term);
|
||||||
|
|
||||||
case 'J': {
|
case 'J': {
|
||||||
assert(term->vt.params.idx == 0);
|
/* Erase screen */
|
||||||
int start = grid_cursor_linear(&term->grid, term->grid.cursor.row, 0);
|
|
||||||
int end = term->grid.cols * term->grid.rows;
|
int param = 0;
|
||||||
|
if (term->vt.params.idx >= 1)
|
||||||
|
param = term->vt.params.v[0].value;
|
||||||
|
|
||||||
|
int start = -1;
|
||||||
|
int end = -1;
|
||||||
|
switch (param) {
|
||||||
|
case 0:
|
||||||
|
/* From cursor to end of screen */
|
||||||
|
start = term->grid.linear_cursor;
|
||||||
|
end = term->grid.cols * term->grid.rows;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
/* From start of screen to cursor */
|
||||||
|
start = 0;
|
||||||
|
end = term->grid.linear_cursor;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
/* Erase entire screen */
|
||||||
|
start = 0;
|
||||||
|
end = term->grid.cols * term->grid.rows;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
LOG_ERR("CSI: J: invalid argument: %d", param);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
grid_erase(&term->grid, start, end);
|
grid_erase(&term->grid, start, end);
|
||||||
return true;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'K': {
|
case 'K': {
|
||||||
assert(term->vt.params.idx == 0);
|
/* Erase line */
|
||||||
int start = term->grid.linear_cursor;
|
|
||||||
int end = grid_cursor_linear(
|
int param = 0;
|
||||||
&term->grid, term->grid.cursor.row, term->grid.cols - 1);
|
if (term->vt.params.idx >= 0)
|
||||||
LOG_DBG("K: %d -> %d", start, end);
|
param = term->vt.params.v[0].value;
|
||||||
|
|
||||||
|
int start = -1;
|
||||||
|
int end = -1;
|
||||||
|
switch (param) {
|
||||||
|
case 0:
|
||||||
|
/* From cursor to end of line */
|
||||||
|
start = term->grid.linear_cursor;
|
||||||
|
end = grid_cursor_linear(
|
||||||
|
&term->grid, term->grid.cursor.row, term->grid.cols - 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
/* From start of line to cursor */
|
||||||
|
start = grid_cursor_linear(
|
||||||
|
&term->grid, term->grid.cursor.row, 0);
|
||||||
|
end = term->grid.linear_cursor;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
/* Entire line */
|
||||||
|
start = grid_cursor_linear(
|
||||||
|
&term->grid, term->grid.cursor.row, 0);
|
||||||
|
end = grid_cursor_linear(
|
||||||
|
&term->grid, term->grid.cursor.row, term->grid.cols - 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
LOG_ERR("CSI: K: invalid argument: %d", param);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
grid_erase(&term->grid, start, end);
|
grid_erase(&term->grid, start, end);
|
||||||
return true;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'A': {
|
||||||
|
int count = term->vt.params.idx > 0 ? term->vt.params.v[0].value : 1;
|
||||||
|
grid_cursor_up(&term->grid, count);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'B': {
|
||||||
|
int count = term->vt.params.idx > 0 ? term->vt.params.v[0].value : 1;
|
||||||
|
grid_cursor_up(&term->grid, count);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'C': {
|
case 'C': {
|
||||||
int count = term->vt.params.idx > 0 ? term->vt.params.v[0].value : 1;
|
int count = term->vt.params.idx > 0 ? term->vt.params.v[0].value : 1;
|
||||||
grid_cursor_right(&term->grid, count);
|
grid_cursor_right(&term->grid, count);
|
||||||
return true;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'D': {
|
case 'D': {
|
||||||
int count = term->vt.params.idx > 0 ? term->vt.params.v[0].value : 1;
|
int count = term->vt.params.idx > 0 ? term->vt.params.v[0].value : 1;
|
||||||
grid_cursor_left(&term->grid, count);
|
grid_cursor_left(&term->grid, count);
|
||||||
return true;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
LOG_ERR("CSI: unimplemented final: %c", final);
|
LOG_ERR("CSI: unimplemented final: %c", final);
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
LOG_ERR("CSI: unimplemented: intermediates: %.*s",
|
LOG_ERR("CSI: unimplemented: intermediates: %.*s",
|
||||||
(int)term->vt.intermediates.idx,
|
(int)term->vt.intermediates.idx,
|
||||||
term->vt.intermediates.data);
|
term->vt.intermediates.data);
|
||||||
|
|
||||||
//abort();
|
//abort();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
48
grid.c
48
grid.c
|
|
@ -53,42 +53,26 @@ void
|
||||||
grid_cursor_left(struct grid *grid, int count)
|
grid_cursor_left(struct grid *grid, int count)
|
||||||
{
|
{
|
||||||
int move_amount = min(grid->cursor.col, count);
|
int move_amount = min(grid->cursor.col, count);
|
||||||
int new_linear = grid->linear_cursor - move_amount;
|
grid_cursor_to(grid, grid->cursor.row, grid->cursor.col - move_amount);
|
||||||
int new_col = grid->cursor.col - move_amount;
|
|
||||||
|
|
||||||
assert(new_linear >= 0);
|
|
||||||
assert(new_linear < grid->rows * grid->cols);
|
|
||||||
assert(new_col >= 0);
|
|
||||||
assert(new_col < grid->cols);
|
|
||||||
|
|
||||||
grid->cells[grid->linear_cursor].dirty = true;
|
|
||||||
grid->cells[new_linear].dirty = true;
|
|
||||||
|
|
||||||
grid->linear_cursor = new_linear;
|
|
||||||
grid->cursor.col = new_col;
|
|
||||||
|
|
||||||
grid->dirty = true;
|
|
||||||
grid->print_needs_wrap = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
grid_cursor_right(struct grid *grid, int count)
|
grid_cursor_right(struct grid *grid, int count)
|
||||||
{
|
{
|
||||||
int move_amount = min(grid->cols - grid->cursor.col - 1, count);
|
int move_amount = min(grid->cols - grid->cursor.col - 1, count);
|
||||||
int new_linear = grid->linear_cursor + move_amount;
|
grid_cursor_to(grid, grid->cursor.row, grid->cursor.col + move_amount);
|
||||||
int new_col = grid->cursor.col + move_amount;
|
}
|
||||||
|
|
||||||
assert(new_linear >= 0);
|
void
|
||||||
assert(new_linear < grid->rows * grid->cols);
|
grid_cursor_up(struct grid *grid, int count)
|
||||||
assert(new_col >= 0);
|
{
|
||||||
assert(new_col < grid->cols);
|
int move_amount = min(grid->cursor.row, count);
|
||||||
|
grid_cursor_to(grid, grid->cursor.row - move_amount, grid->cursor.col);
|
||||||
grid->cells[grid->linear_cursor].dirty = true;
|
}
|
||||||
grid->cells[new_linear].dirty = true;
|
|
||||||
|
void
|
||||||
grid->linear_cursor = new_linear;
|
grid_cursor_down(struct grid *grid, int count)
|
||||||
grid->cursor.col = new_col;
|
{
|
||||||
|
int move_amount = min(grid->rows - grid->cursor.row - 1, count);
|
||||||
grid->dirty = true;
|
grid_cursor_to(grid, grid->cursor.row + move_amount, grid->cursor.col);
|
||||||
grid->print_needs_wrap = false;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
grid.h
2
grid.h
|
|
@ -7,6 +7,8 @@ void grid_erase(struct grid *grid, int start, int end);
|
||||||
void grid_cursor_to(struct grid *grid, int row, int col);
|
void grid_cursor_to(struct grid *grid, int row, int col);
|
||||||
void grid_cursor_left(struct grid *grid, int count);
|
void grid_cursor_left(struct grid *grid, int count);
|
||||||
void grid_cursor_right(struct grid *grid, int count);
|
void grid_cursor_right(struct grid *grid, int count);
|
||||||
|
void grid_cursor_up(struct grid *grid, int count);
|
||||||
|
void grid_cursor_down(struct grid *grid, int count);
|
||||||
|
|
||||||
int grid_cursor_linear(const struct grid *grid, int row, int col);
|
int grid_cursor_linear(const struct grid *grid, int row, int col);
|
||||||
|
|
||||||
|
|
|
||||||
173
input.c
Normal file
173
input.c
Normal file
|
|
@ -0,0 +1,173 @@
|
||||||
|
#include "input.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <threads.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
|
#include <xkbcommon/xkbcommon.h>
|
||||||
|
#include <xkbcommon/xkbcommon-keysyms.h>
|
||||||
|
|
||||||
|
#define LOG_MODULE "input"
|
||||||
|
#define LOG_ENABLE_DBG 1
|
||||||
|
#include "log.h"
|
||||||
|
#include "terminal.h"
|
||||||
|
|
||||||
|
static void
|
||||||
|
keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
|
||||||
|
uint32_t format, int32_t fd, uint32_t size)
|
||||||
|
{
|
||||||
|
struct terminal *term = data;
|
||||||
|
|
||||||
|
char *map_str = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||||
|
|
||||||
|
/* TODO: free old context + keymap */
|
||||||
|
|
||||||
|
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,
|
||||||
|
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||||
|
|
||||||
|
/* TODO: initialize in enter? */
|
||||||
|
term->kbd.xkb_state = xkb_state_new(term->kbd.xkb_keymap);
|
||||||
|
|
||||||
|
munmap(map_str, size);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
|
||||||
|
struct wl_surface *surface, struct wl_array *keys)
|
||||||
|
{
|
||||||
|
LOG_DBG("enter");
|
||||||
|
#if 0
|
||||||
|
uint32_t *key;
|
||||||
|
wl_array_for_each(key, keys)
|
||||||
|
xkb_state_update_key(xkb_state, *key, 1);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
|
||||||
|
struct wl_surface *surface)
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
struct terminal *term = data;
|
||||||
|
term->status = EXIT;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
|
||||||
|
uint32_t time, uint32_t key, uint32_t state)
|
||||||
|
{
|
||||||
|
static bool mod_masks_initialized = false;
|
||||||
|
static xkb_mod_mask_t ctrl = -1;
|
||||||
|
static xkb_mod_mask_t alt = -1;
|
||||||
|
//static xkb_mod_mask_t shift = -1;
|
||||||
|
|
||||||
|
struct terminal *term = data;
|
||||||
|
|
||||||
|
if (!mod_masks_initialized) {
|
||||||
|
mod_masks_initialized = true;
|
||||||
|
ctrl = 1 << xkb_keymap_mod_get_index(term->kbd.xkb_keymap, "Control");
|
||||||
|
alt = 1 << xkb_keymap_mod_get_index(term->kbd.xkb_keymap, "Mod1");
|
||||||
|
//shift = 1 << xkb_keymap_mod_get_index(term->kbd.xkb_keymap, "Shift");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == XKB_KEY_UP) {
|
||||||
|
mtx_lock(&term->kbd.repeat.mutex);
|
||||||
|
if (term->kbd.repeat.key == key) {
|
||||||
|
if (term->kbd.repeat.cmd != REPEAT_EXIT) {
|
||||||
|
term->kbd.repeat.cmd = REPEAT_STOP;
|
||||||
|
cnd_signal(&term->kbd.repeat.cond);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mtx_unlock(&term->kbd.repeat.mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
key += 8;
|
||||||
|
xkb_keysym_t sym = xkb_state_key_get_one_sym(term->kbd.xkb_state, key);
|
||||||
|
|
||||||
|
xkb_mod_mask_t mods = xkb_state_serialize_mods(
|
||||||
|
term->kbd.xkb_state, XKB_STATE_MODS_EFFECTIVE);
|
||||||
|
xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods(term->kbd.xkb_state, key);
|
||||||
|
xkb_mod_mask_t significant = ctrl | alt /*| shift*/;
|
||||||
|
xkb_mod_mask_t effective_mods = mods & ~consumed & significant;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
for (size_t i = 0; i < 32; i++) {
|
||||||
|
if (mods & (1 << i)) {
|
||||||
|
LOG_DBG("%s", xkb_keymap_mod_get_name(term->kbd.xkb_keymap, i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
LOG_DBG("sym=%u, mod=0x%08x, consumed=0x%08x, significant=0x%08x, "
|
||||||
|
"effective=0x%08x",
|
||||||
|
sym, mods, consumed, significant, effective_mods);
|
||||||
|
|
||||||
|
if (sym == XKB_KEY_c && effective_mods == ctrl) {
|
||||||
|
kill(term->slave, SIGINT);
|
||||||
|
|
||||||
|
} else if (effective_mods == 0) {
|
||||||
|
char buf[128] = {0};
|
||||||
|
int count = xkb_state_key_get_utf8(term->kbd.xkb_state, key, buf, sizeof(buf));
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
write(term->ptmx, buf, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
mtx_lock(&term->kbd.repeat.mutex);
|
||||||
|
if (!term->kbd.repeat.dont_re_repeat) {
|
||||||
|
if (term->kbd.repeat.cmd != REPEAT_EXIT) {
|
||||||
|
term->kbd.repeat.cmd = REPEAT_START;
|
||||||
|
term->kbd.repeat.key = key - 8;
|
||||||
|
cnd_signal(&term->kbd.repeat.cond);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mtx_unlock(&term->kbd.repeat.mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
|
||||||
|
uint32_t mods_depressed, uint32_t mods_latched,
|
||||||
|
uint32_t mods_locked, uint32_t group)
|
||||||
|
{
|
||||||
|
struct terminal *term = 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard,
|
||||||
|
int32_t rate, int32_t delay)
|
||||||
|
{
|
||||||
|
struct terminal *term = data;
|
||||||
|
LOG_DBG("keyboard repeat: rate=%d, delay=%d", rate, delay);
|
||||||
|
term->kbd.repeat.rate = rate;
|
||||||
|
term->kbd.repeat.delay = delay;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct wl_keyboard_listener keyboard_listener = {
|
||||||
|
.keymap = &keyboard_keymap,
|
||||||
|
.enter = &keyboard_enter,
|
||||||
|
.leave = &keyboard_leave,
|
||||||
|
.key = &keyboard_key,
|
||||||
|
.modifiers = &keyboard_modifiers,
|
||||||
|
.repeat_info = &keyboard_repeat_info,
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
input_repeat(struct terminal *term, uint32_t key)
|
||||||
|
{
|
||||||
|
keyboard_key(term, NULL, 0, 0, key, XKB_KEY_DOWN);
|
||||||
|
}
|
||||||
9
input.h
Normal file
9
input.h
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <wayland-client.h>
|
||||||
|
|
||||||
|
#include "terminal.h"
|
||||||
|
|
||||||
|
extern const struct wl_keyboard_listener keyboard_listener;
|
||||||
|
|
||||||
|
void input_repeat(struct terminal *term, uint32_t key);
|
||||||
158
main.c
158
main.c
|
|
@ -23,6 +23,7 @@
|
||||||
#include "slave.h"
|
#include "slave.h"
|
||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
#include "vt.h"
|
#include "vt.h"
|
||||||
|
#include "input.h"
|
||||||
|
|
||||||
static const uint32_t default_foreground = 0xffffffff;
|
static const uint32_t default_foreground = 0xffffffff;
|
||||||
static const uint32_t default_background = 0x000000ff;
|
static const uint32_t default_background = 0x000000ff;
|
||||||
|
|
@ -33,6 +34,8 @@ struct wayland {
|
||||||
struct wl_compositor *compositor;
|
struct wl_compositor *compositor;
|
||||||
struct wl_surface *surface;
|
struct wl_surface *surface;
|
||||||
struct wl_shm *shm;
|
struct wl_shm *shm;
|
||||||
|
struct wl_seat *seat;
|
||||||
|
struct wl_keyboard *keyboard;
|
||||||
struct xdg_wm_base *shell;
|
struct xdg_wm_base *shell;
|
||||||
struct xdg_surface *xdg_surface;
|
struct xdg_surface *xdg_surface;
|
||||||
struct xdg_toplevel *xdg_toplevel;
|
struct xdg_toplevel *xdg_toplevel;
|
||||||
|
|
@ -248,6 +251,32 @@ static const struct xdg_wm_base_listener xdg_wm_base_listener = {
|
||||||
.ping = &xdg_wm_base_ping,
|
.ping = &xdg_wm_base_ping,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
|
||||||
|
enum wl_seat_capability caps)
|
||||||
|
{
|
||||||
|
struct context *c = data;
|
||||||
|
|
||||||
|
if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (c->wl.keyboard != NULL)
|
||||||
|
wl_keyboard_release(c->wl.keyboard);
|
||||||
|
|
||||||
|
c->wl.keyboard = wl_seat_get_keyboard(wl_seat);
|
||||||
|
wl_keyboard_add_listener(c->wl.keyboard, &keyboard_listener, &c->term);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
static void
|
||||||
handle_global(void *data, struct wl_registry *registry,
|
handle_global(void *data, struct wl_registry *registry,
|
||||||
uint32_t name, const char *interface, uint32_t version)
|
uint32_t name, const char *interface, uint32_t version)
|
||||||
|
|
@ -272,6 +301,12 @@ handle_global(void *data, struct wl_registry *registry,
|
||||||
xdg_wm_base_add_listener(c->wl.shell, &xdg_wm_base_listener, c);
|
xdg_wm_base_add_listener(c->wl.shell, &xdg_wm_base_listener, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else if (strcmp(interface, wl_seat_interface.name) == 0) {
|
||||||
|
c->wl.seat = wl_registry_bind(c->wl.registry, name, &wl_seat_interface, 4);
|
||||||
|
wl_seat_add_listener(c->wl.seat, &seat_listener, c);
|
||||||
|
wl_display_roundtrip(c->wl.display);
|
||||||
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
else if (strcmp(interface, wl_output_interface.name) == 0) {
|
else if (strcmp(interface, wl_output_interface.name) == 0) {
|
||||||
struct wl_output *output = wl_registry_bind(
|
struct wl_output *output = wl_registry_bind(
|
||||||
|
|
@ -370,6 +405,77 @@ static const struct wl_registry_listener registry_listener = {
|
||||||
.global_remove = &handle_global_remove,
|
.global_remove = &handle_global_remove,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
keyboard_repeater(void *arg)
|
||||||
|
{
|
||||||
|
struct terminal *term = arg;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
LOG_DBG("repeater: waiting for start");
|
||||||
|
|
||||||
|
mtx_lock(&term->kbd.repeat.mutex);
|
||||||
|
while (term->kbd.repeat.cmd == REPEAT_STOP)
|
||||||
|
cnd_wait(&term->kbd.repeat.cond, &term->kbd.repeat.mutex);
|
||||||
|
|
||||||
|
if (term->kbd.repeat.cmd == REPEAT_EXIT) {
|
||||||
|
mtx_unlock(&term->kbd.repeat.mutex);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
restart:
|
||||||
|
|
||||||
|
LOG_DBG("repeater: started");
|
||||||
|
assert(term->kbd.repeat.cmd == REPEAT_START);
|
||||||
|
|
||||||
|
const long rate_delay = 1000000000 / term->kbd.repeat.rate;
|
||||||
|
long delay = term->kbd.repeat.delay * 1000000;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
assert(term->kbd.repeat.rate > 0);
|
||||||
|
|
||||||
|
struct timespec timeout;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &timeout);
|
||||||
|
|
||||||
|
timeout.tv_nsec += delay;
|
||||||
|
if (timeout.tv_nsec >= 1000000000) {
|
||||||
|
timeout.tv_sec += timeout.tv_nsec / 1000000000;
|
||||||
|
timeout.tv_nsec %= 1000000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = cnd_timedwait(&term->kbd.repeat.cond, &term->kbd.repeat.mutex, &timeout);
|
||||||
|
if (ret == thrd_success) {
|
||||||
|
if (term->kbd.repeat.cmd == REPEAT_START)
|
||||||
|
goto restart;
|
||||||
|
else if (term->kbd.repeat.cmd == REPEAT_STOP) {
|
||||||
|
mtx_unlock(&term->kbd.repeat.mutex);
|
||||||
|
break;
|
||||||
|
} else if (term->kbd.repeat.cmd == REPEAT_EXIT) {
|
||||||
|
mtx_unlock(&term->kbd.repeat.mutex);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(ret == thrd_timedout);
|
||||||
|
assert(term->kbd.repeat.cmd == REPEAT_START);
|
||||||
|
LOG_DBG("repeater: repeat: %u", term->kbd.repeat.key);
|
||||||
|
|
||||||
|
if (write(term->kbd.repeat.pipe_write_fd, &term->kbd.repeat.key,
|
||||||
|
sizeof(term->kbd.repeat.key)) != sizeof(term->kbd.repeat.key))
|
||||||
|
{
|
||||||
|
LOG_ERRNO("faile to write repeat key to repeat pipe");
|
||||||
|
mtx_unlock(&term->kbd.repeat.mutex);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
delay = rate_delay;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(false);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, const char *const *argv)
|
main(int argc, const char *const *argv)
|
||||||
{
|
{
|
||||||
|
|
@ -377,6 +483,12 @@ main(int argc, const char *const *argv)
|
||||||
|
|
||||||
setlocale(LC_ALL, "");
|
setlocale(LC_ALL, "");
|
||||||
|
|
||||||
|
int repeat_pipe_fds[2] = {-1, -1};
|
||||||
|
if (pipe2(repeat_pipe_fds, O_CLOEXEC) == -1) {
|
||||||
|
LOG_ERRNO("failed to create pipe for repeater thread");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
struct context c = {
|
struct context c = {
|
||||||
.quit = false,
|
.quit = false,
|
||||||
.term = {
|
.term = {
|
||||||
|
|
@ -386,9 +498,22 @@ main(int argc, const char *const *argv)
|
||||||
},
|
},
|
||||||
.grid = {.foreground = default_foreground,
|
.grid = {.foreground = default_foreground,
|
||||||
.background = default_background},
|
.background = default_background},
|
||||||
|
.kbd = {
|
||||||
|
.repeat = {
|
||||||
|
.pipe_read_fd = repeat_pipe_fds[0],
|
||||||
|
.pipe_write_fd = repeat_pipe_fds[1],
|
||||||
|
.cmd = REPEAT_STOP,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mtx_init(&c.term.kbd.repeat.mutex, mtx_plain);
|
||||||
|
cnd_init(&c.term.kbd.repeat.cond);
|
||||||
|
|
||||||
|
thrd_t keyboard_repeater_id;
|
||||||
|
thrd_create(&keyboard_repeater_id, &keyboard_repeater, &c.term);
|
||||||
|
|
||||||
const char *font_name = "Dina:pixelsize=12";
|
const char *font_name = "Dina:pixelsize=12";
|
||||||
c.font = font_from_name(font_name);
|
c.font = font_from_name(font_name);
|
||||||
if (c.font == NULL)
|
if (c.font == NULL)
|
||||||
|
|
@ -459,8 +584,8 @@ main(int argc, const char *const *argv)
|
||||||
|
|
||||||
wl_display_dispatch_pending(c.wl.display);
|
wl_display_dispatch_pending(c.wl.display);
|
||||||
|
|
||||||
pid_t pid = fork();
|
c.term.slave = fork();
|
||||||
switch (pid) {
|
switch (c.term.slave) {
|
||||||
case -1:
|
case -1:
|
||||||
LOG_ERRNO("failed to fork");
|
LOG_ERRNO("failed to fork");
|
||||||
goto out;
|
goto out;
|
||||||
|
|
@ -472,7 +597,7 @@ main(int argc, const char *const *argv)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
LOG_DBG("slave has PID %d", pid);
|
LOG_DBG("slave has PID %d", c.term.slave);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -480,6 +605,7 @@ main(int argc, const char *const *argv)
|
||||||
struct pollfd fds[] = {
|
struct pollfd fds[] = {
|
||||||
{.fd = wl_display_get_fd(c.wl.display), .events = POLLIN},
|
{.fd = wl_display_get_fd(c.wl.display), .events = POLLIN},
|
||||||
{.fd = c.term.ptmx, .events = POLLIN},
|
{.fd = c.term.ptmx, .events = POLLIN},
|
||||||
|
{.fd = c.term.kbd.repeat.pipe_read_fd, .events = POLLIN},
|
||||||
};
|
};
|
||||||
|
|
||||||
wl_display_flush(c.wl.display);
|
wl_display_flush(c.wl.display);
|
||||||
|
|
@ -517,9 +643,29 @@ main(int argc, const char *const *argv)
|
||||||
ret = EXIT_SUCCESS;
|
ret = EXIT_SUCCESS;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fds[2].revents & POLLIN) {
|
||||||
|
uint32_t key;
|
||||||
|
if (read(c.term.kbd.repeat.pipe_read_fd, &key, sizeof(key)) != sizeof(key)) {
|
||||||
|
LOG_ERRNO("failed to read repeat key from repeat pipe");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
c.term.kbd.repeat.dont_re_repeat = true;
|
||||||
|
input_repeat(&c.term, key);
|
||||||
|
c.term.kbd.repeat.dont_re_repeat = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fds[2].revents & POLLHUP)
|
||||||
|
LOG_ERR("keyboard repeat handling thread died");
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
mtx_lock(&c.term.kbd.repeat.mutex);
|
||||||
|
c.term.kbd.repeat.cmd = REPEAT_EXIT;
|
||||||
|
cnd_signal(&c.term.kbd.repeat.cond);
|
||||||
|
mtx_unlock(&c.term.kbd.repeat.mutex);
|
||||||
|
|
||||||
shm_fini();
|
shm_fini();
|
||||||
if (c.wl.xdg_toplevel != NULL)
|
if (c.wl.xdg_toplevel != NULL)
|
||||||
xdg_toplevel_destroy(c.wl.xdg_toplevel);
|
xdg_toplevel_destroy(c.wl.xdg_toplevel);
|
||||||
|
|
@ -546,6 +692,12 @@ out:
|
||||||
if (c.term.ptmx != -1)
|
if (c.term.ptmx != -1)
|
||||||
close(c.term.ptmx);
|
close(c.term.ptmx);
|
||||||
|
|
||||||
|
thrd_join(keyboard_repeater_id, NULL);
|
||||||
|
cnd_destroy(&c.term.kbd.repeat.cond);
|
||||||
|
mtx_destroy(&c.term.kbd.repeat.mutex);
|
||||||
|
close(c.term.kbd.repeat.pipe_read_fd);
|
||||||
|
close(c.term.kbd.repeat.pipe_write_fd);
|
||||||
|
|
||||||
cairo_debug_reset_static_data();
|
cairo_debug_reset_static_data();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ add_project_arguments(
|
||||||
cc = meson.get_compiler('c')
|
cc = meson.get_compiler('c')
|
||||||
math = cc.find_library('m')
|
math = cc.find_library('m')
|
||||||
|
|
||||||
|
threads = dependency('threads')
|
||||||
fontconfig = dependency('fontconfig')
|
fontconfig = dependency('fontconfig')
|
||||||
cairo = dependency('cairo')
|
cairo = dependency('cairo')
|
||||||
cairo_ft = dependency('cairo-ft')
|
cairo_ft = dependency('cairo-ft')
|
||||||
|
|
@ -60,6 +61,7 @@ executable(
|
||||||
'csi.c', 'csi.h',
|
'csi.c', 'csi.h',
|
||||||
'font.c', 'font.h',
|
'font.c', 'font.h',
|
||||||
'grid.c', 'grid.h',
|
'grid.c', 'grid.h',
|
||||||
|
'input.c', 'input.h',
|
||||||
'log.c', 'log.h',
|
'log.c', 'log.h',
|
||||||
'main.c',
|
'main.c',
|
||||||
'osc.c', 'osc.h',
|
'osc.c', 'osc.h',
|
||||||
|
|
@ -69,5 +71,5 @@ executable(
|
||||||
'tllist.h',
|
'tllist.h',
|
||||||
'vt.c', 'vt.h',
|
'vt.c', 'vt.h',
|
||||||
wl_proto_src + wl_proto_headers,
|
wl_proto_src + wl_proto_headers,
|
||||||
dependencies: [math, cairo, cairo_ft, fontconfig, wayland_client, wayland_cursor, xkb],
|
dependencies: [threads, math, cairo, cairo_ft, fontconfig, wayland_client, wayland_cursor, xkb],
|
||||||
install: true)
|
install: true)
|
||||||
|
|
|
||||||
26
terminal.h
26
terminal.h
|
|
@ -4,6 +4,11 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <threads.h>
|
||||||
|
|
||||||
|
#include <xkbcommon/xkbcommon.h>
|
||||||
|
#include <xkbcommon/xkbcommon-keysyms.h>
|
||||||
|
|
||||||
struct attributes {
|
struct attributes {
|
||||||
bool bold;
|
bool bold;
|
||||||
bool italic;
|
bool italic;
|
||||||
|
|
@ -73,8 +78,29 @@ struct vt {
|
||||||
bool dim;
|
bool dim;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct kbd {
|
||||||
|
struct xkb_context *xkb;
|
||||||
|
struct xkb_keymap *xkb_keymap;
|
||||||
|
struct xkb_state *xkb_state;
|
||||||
|
struct {
|
||||||
|
mtx_t mutex;
|
||||||
|
cnd_t cond;
|
||||||
|
int trigger;
|
||||||
|
int pipe_read_fd;
|
||||||
|
int pipe_write_fd;
|
||||||
|
enum {REPEAT_STOP, REPEAT_START, REPEAT_EXIT} cmd;
|
||||||
|
|
||||||
|
bool dont_re_repeat;
|
||||||
|
int32_t delay;
|
||||||
|
int32_t rate;
|
||||||
|
uint32_t key;
|
||||||
|
} repeat;
|
||||||
|
};
|
||||||
|
|
||||||
struct terminal {
|
struct terminal {
|
||||||
|
pid_t slave;
|
||||||
int ptmx;
|
int ptmx;
|
||||||
struct vt vt;
|
struct vt vt;
|
||||||
struct grid grid;
|
struct grid grid;
|
||||||
|
struct kbd kbd;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
14
vt.c
14
vt.c
|
|
@ -157,6 +157,10 @@ action(struct terminal *term, enum action action, uint8_t c)
|
||||||
case ACTION_EXECUTE:
|
case ACTION_EXECUTE:
|
||||||
LOG_DBG("execute: 0x%02x", c);
|
LOG_DBG("execute: 0x%02x", c);
|
||||||
switch (c) {
|
switch (c) {
|
||||||
|
case '\n':
|
||||||
|
grid_cursor_down(&term->grid, 1);
|
||||||
|
break;
|
||||||
|
|
||||||
case '\r':
|
case '\r':
|
||||||
grid_cursor_left(&term->grid, term->grid.cursor.col);
|
grid_cursor_left(&term->grid, term->grid.cursor.col);
|
||||||
break;
|
break;
|
||||||
|
|
@ -164,6 +168,14 @@ action(struct terminal *term, enum action action, uint8_t c)
|
||||||
case '\b':
|
case '\b':
|
||||||
grid_cursor_left(&term->grid, 1);
|
grid_cursor_left(&term->grid, 1);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case '\x07':
|
||||||
|
LOG_WARN("BELL");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
LOG_ERR("execute: unimplemented: %c", c);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -189,7 +201,7 @@ action(struct terminal *term, enum action action, uint8_t c)
|
||||||
cell->c[term->vt.utf8.idx] = '\0';
|
cell->c[term->vt.utf8.idx] = '\0';
|
||||||
memset(&term->vt.utf8, 0, sizeof(term->vt.utf8));
|
memset(&term->vt.utf8, 0, sizeof(term->vt.utf8));
|
||||||
} else {
|
} else {
|
||||||
//LOG_DBG("print: ASCII: %c", c);
|
LOG_DBG("print: ASCII: %c", c);
|
||||||
cell->c[0] = c;
|
cell->c[0] = c;
|
||||||
cell->c[1] = '\0';
|
cell->c[1] = '\0';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue