228 lines
4.5 KiB
C
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;
|
|
}
|