wip: initial input handling

This commit is contained in:
Daniel Eklöf 2019-06-19 10:04:47 +02:00
parent 3bd77bceb1
commit 71dde121e6
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
9 changed files with 484 additions and 51 deletions

101
csi.c
View file

@ -107,51 +107,124 @@ csi_dispatch(struct terminal *term, uint8_t final)
if (term->vt.intermediates.idx == 0) {
switch (final) {
case 'c':
write(term->ptmx, "\033[?6c", 5);
return true;
return write(term->ptmx, "\033[?6c", 5) == 5;
case 'm':
return csi_sgr(term);
case 'J': {
assert(term->vt.params.idx == 0);
int start = grid_cursor_linear(&term->grid, term->grid.cursor.row, 0);
int end = term->grid.cols * term->grid.rows;
/* Erase screen */
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);
return true;
break;
}
case 'K': {
assert(term->vt.params.idx == 0);
int start = term->grid.linear_cursor;
int end = grid_cursor_linear(
&term->grid, term->grid.cursor.row, term->grid.cols - 1);
LOG_DBG("K: %d -> %d", start, end);
/* Erase line */
int param = 0;
if (term->vt.params.idx >= 0)
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);
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': {
int count = term->vt.params.idx > 0 ? term->vt.params.v[0].value : 1;
grid_cursor_right(&term->grid, count);
return true;
break;
}
case 'D': {
int count = term->vt.params.idx > 0 ? term->vt.params.v[0].value : 1;
grid_cursor_left(&term->grid, count);
return true;
break;
}
default:
LOG_ERR("CSI: unimplemented final: %c", final);
abort();
}
return true;
} else {
LOG_ERR("CSI: unimplemented: intermediates: %.*s",
(int)term->vt.intermediates.idx,
term->vt.intermediates.data);
//abort();
return true;
}

48
grid.c
View file

@ -53,42 +53,26 @@ void
grid_cursor_left(struct grid *grid, int count)
{
int move_amount = min(grid->cursor.col, count);
int new_linear = grid->linear_cursor - 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;
grid_cursor_to(grid, grid->cursor.row, grid->cursor.col - move_amount);
}
void
grid_cursor_right(struct grid *grid, int count)
{
int move_amount = min(grid->cols - grid->cursor.col - 1, count);
int new_linear = grid->linear_cursor + 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;
grid_cursor_to(grid, grid->cursor.row, grid->cursor.col + move_amount);
}
void
grid_cursor_up(struct grid *grid, int count)
{
int move_amount = min(grid->cursor.row, count);
grid_cursor_to(grid, grid->cursor.row - move_amount, grid->cursor.col);
}
void
grid_cursor_down(struct grid *grid, int count)
{
int move_amount = min(grid->rows - grid->cursor.row - 1, count);
grid_cursor_to(grid, grid->cursor.row + move_amount, grid->cursor.col);
}

2
grid.h
View file

@ -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_left(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);

173
input.c Normal file
View 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
View 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
View file

@ -23,6 +23,7 @@
#include "slave.h"
#include "terminal.h"
#include "vt.h"
#include "input.h"
static const uint32_t default_foreground = 0xffffffff;
static const uint32_t default_background = 0x000000ff;
@ -33,6 +34,8 @@ struct wayland {
struct wl_compositor *compositor;
struct wl_surface *surface;
struct wl_shm *shm;
struct wl_seat *seat;
struct wl_keyboard *keyboard;
struct xdg_wm_base *shell;
struct xdg_surface *xdg_surface;
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,
};
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
handle_global(void *data, struct wl_registry *registry,
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);
}
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
else if (strcmp(interface, wl_output_interface.name) == 0) {
struct wl_output *output = wl_registry_bind(
@ -370,6 +405,77 @@ static const struct wl_registry_listener registry_listener = {
.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
main(int argc, const char *const *argv)
{
@ -377,6 +483,12 @@ main(int argc, const char *const *argv)
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 = {
.quit = false,
.term = {
@ -386,9 +498,22 @@ main(int argc, const char *const *argv)
},
.grid = {.foreground = default_foreground,
.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";
c.font = font_from_name(font_name);
if (c.font == NULL)
@ -459,8 +584,8 @@ main(int argc, const char *const *argv)
wl_display_dispatch_pending(c.wl.display);
pid_t pid = fork();
switch (pid) {
c.term.slave = fork();
switch (c.term.slave) {
case -1:
LOG_ERRNO("failed to fork");
goto out;
@ -472,7 +597,7 @@ main(int argc, const char *const *argv)
break;
default:
LOG_DBG("slave has PID %d", pid);
LOG_DBG("slave has PID %d", c.term.slave);
break;
}
@ -480,6 +605,7 @@ main(int argc, const char *const *argv)
struct pollfd fds[] = {
{.fd = wl_display_get_fd(c.wl.display), .events = POLLIN},
{.fd = c.term.ptmx, .events = POLLIN},
{.fd = c.term.kbd.repeat.pipe_read_fd, .events = POLLIN},
};
wl_display_flush(c.wl.display);
@ -517,9 +643,29 @@ main(int argc, const char *const *argv)
ret = EXIT_SUCCESS;
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:
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();
if (c.wl.xdg_toplevel != NULL)
xdg_toplevel_destroy(c.wl.xdg_toplevel);
@ -546,6 +692,12 @@ out:
if (c.term.ptmx != -1)
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();
return ret;
}

View file

@ -20,6 +20,7 @@ add_project_arguments(
cc = meson.get_compiler('c')
math = cc.find_library('m')
threads = dependency('threads')
fontconfig = dependency('fontconfig')
cairo = dependency('cairo')
cairo_ft = dependency('cairo-ft')
@ -60,6 +61,7 @@ executable(
'csi.c', 'csi.h',
'font.c', 'font.h',
'grid.c', 'grid.h',
'input.c', 'input.h',
'log.c', 'log.h',
'main.c',
'osc.c', 'osc.h',
@ -69,5 +71,5 @@ executable(
'tllist.h',
'vt.c', 'vt.h',
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)

View file

@ -4,6 +4,11 @@
#include <stdbool.h>
#include <stddef.h>
#include <threads.h>
#include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-keysyms.h>
struct attributes {
bool bold;
bool italic;
@ -73,8 +78,29 @@ struct vt {
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 {
pid_t slave;
int ptmx;
struct vt vt;
struct grid grid;
struct kbd kbd;
};

14
vt.c
View file

@ -157,6 +157,10 @@ action(struct terminal *term, enum action action, uint8_t c)
case ACTION_EXECUTE:
LOG_DBG("execute: 0x%02x", c);
switch (c) {
case '\n':
grid_cursor_down(&term->grid, 1);
break;
case '\r':
grid_cursor_left(&term->grid, term->grid.cursor.col);
break;
@ -164,6 +168,14 @@ action(struct terminal *term, enum action action, uint8_t c)
case '\b':
grid_cursor_left(&term->grid, 1);
break;
case '\x07':
LOG_WARN("BELL");
break;
default:
LOG_ERR("execute: unimplemented: %c", c);
return false;
}
return true;
@ -189,7 +201,7 @@ action(struct terminal *term, enum action action, uint8_t c)
cell->c[term->vt.utf8.idx] = '\0';
memset(&term->vt.utf8, 0, sizeof(term->vt.utf8));
} else {
//LOG_DBG("print: ASCII: %c", c);
LOG_DBG("print: ASCII: %c", c);
cell->c[0] = c;
cell->c[1] = '\0';
}