// SPDX-License-Identifier: GPL-3.0-only /* * Copyright (c) 2024, Richard Acayan. All rights reserved. */ #include #include #include #include #include #include #include #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; }