unfettered-keyboard/input.c
2024-04-15 22:11:20 -04:00

228 lines
4.5 KiB
C

// SPDX-License-Identifier: GPL-3.0-only
/*
* Copyright (c) 2024, Richard Acayan. All rights reserved.
*/
#include <errno.h>
#include <math.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <xkbcommon/xkbcommon.h>
#include "graphics.h"
#include "input.h"
#include "layout.h"
#include "modifier.h"
#include "ufkbd.h"
// Simple function to add nanoseconds to a time struct.
static inline void time_add(struct timespec *time, long ns)
{
time->tv_nsec += ns;
if (time->tv_nsec >= 1000000000) {
time->tv_sec += time->tv_nsec / 1000000000;
time->tv_nsec = time->tv_nsec % 1000000000;
}
}
static enum ufkbd_part part_calc(struct ufkbd_ctx *ctx,
struct ufkbd_key *key,
int x1, int y1, int x2, int y2)
{
double angle;
int dx, dy;
int r_square;
int idx;
dx = x2 - x1;
dy = y2 - y1;
r_square = dx * dx + dy * dy;
if (r_square < ctx->dist_squared)
return UFKBD_PART_CENTER;
angle = atan2(dy, dx);
if (angle < 0)
angle += M_PI * 2;
idx = floor(angle * 8 / M_PI);
return key->lut[idx];
}
int ufkbd_input_press(struct ufkbd_ctx *ctx, int id)
{
struct ufkbd_press *press;
int x1, y1, x2, y2;
int ret;
if (id >= UFKBD_PRESS_MAX)
return -EINVAL;
press = &ctx->presses[id];
// Only reset the press if it is new.
if (press->key == NULL) {
press->x1 = press->x2;
press->y1 = press->y2;
}
// Failure to find a key results in ignoring the press.
press->key = ufkbd_layout_search(ctx->layout, press->x1, press->y1,
&x1, &y1, &x2, &y2);
press->part = UFKBD_PART_CENTER;
if (press->key != NULL) {
ufkbd_modifier_press(ctx, press);
press->key->presses[press->part]++;
ctx->n_presses++;
press->xl = x1;
press->yt = y1;
press->xr = x2;
press->yb = y2;
ufkbd_graphics_draw_key(ctx, press->key, x1, y1, x2, y2);
}
// The keypress may function partially if this fails.
ret = clock_gettime(CLOCK_REALTIME, &press->repeat);
if (ret == -1)
return 0;
time_add(&press->repeat, ctx->repeat_delay_ns);
return 0;
}
int ufkbd_input_position(struct ufkbd_ctx *ctx, int id, int x, int y)
{
struct ufkbd_press *press;
enum ufkbd_part part;
if (id >= UFKBD_PRESS_MAX)
return -EINVAL;
press = &ctx->presses[id];
press->x2 = x;
press->y2 = y;
// We only want to do this if a valid key is being pressed.
if (press->key != NULL) {
part = part_calc(ctx, press->key,
press->x1, press->y1,
press->x2, press->y2);
if (press->part != part) {
press->key->presses[press->part]--;
press->key->presses[part]++;
ufkbd_graphics_draw_key(ctx, press->key,
press->xl, press->yt,
press->xr, press->yb);
ufkbd_modifier_cancel(ctx, press);
press->part = part;
ufkbd_modifier_press(ctx, press);
}
}
return 0;
}
int ufkbd_input_release(struct ufkbd_ctx *ctx, int id)
{
struct ufkbd_press *press;
int part, code;
int ret = 0;
if (ctx->drv->send_key == NULL)
return -ENOTSUP;
if (ctx->presses[id].part >= UFKBD_PART_MAX)
return -EINVAL;
press = &ctx->presses[id];
if (press->key == NULL)
return 0;
part = press->part;
code = press->key->keyids[part];
/*
* If the key is a modifier, a state change is needed, and the key
* should only be sent to the driver when the modifier is released.
* Otherwise, the key should be sent to the driver and any latched
* modifiers should be released.
*/
if (ufkbd_is_modifier(press)) {
ufkbd_modifier_release(ctx, press);
} else if (code != -1) {
ret = ctx->drv->send_key(ctx->drv_data, code, false);
ufkbd_modifier_unlatch_all(ctx);
}
press->key->presses[part]--;
ctx->n_presses--;
ufkbd_graphics_draw_key(ctx, press->key,
press->xl, press->yt,
press->xr, press->yb);
press->key = NULL;
return ret;
}
int ufkbd_input_repeat(struct ufkbd_ctx *ctx, int id)
{
struct ufkbd_press *press;
enum ufkbd_part part;
int code;
press = &ctx->presses[id];
// Modifier keys are not repeated.
if (ufkbd_is_modifier(press))
return 0;
time_add(&press->repeat, ctx->repeat_interval_ns);
part = press->part;
code = press->key->keyids[part];
return ctx->drv->send_key(ctx->drv_data, code, true);
}
int ufkbd_input_repeat_held(struct ufkbd_ctx *ctx)
{
struct ufkbd_press *press;
struct timespec time;
size_t i;
int ret;
ret = clock_gettime(CLOCK_REALTIME, &time);
if (ret == -1)
return 0;
for (i = 0; i < UFKBD_PRESS_MAX; i++) {
press = &ctx->presses[i];
if (press->key == NULL)
continue;
if (time.tv_sec > press->repeat.tv_sec
|| (time.tv_sec == press->repeat.tv_sec
&& time.tv_nsec > press->repeat.tv_nsec)) {
ufkbd_input_repeat(ctx, i);
}
}
return 0;
}