// SPDX-License-Identifier: GPL-3.0-only /* * Management of keys that can be latched or locked. * * Copyright (c) 2024, Richard Acayan. All rights reserved. */ #include #include #include "ufkbd.h" static int mod_key_id(xkb_keysym_t sym) { if (sym == XKB_KEY_Shift_L || sym == XKB_KEY_Shift_R) return UFKBD_MOD_SHIFT; else if (sym == XKB_KEY_Control_L || sym == XKB_KEY_Control_R) return UFKBD_MOD_CTRL; else if (sym == XKB_KEY_Alt_L || sym == XKB_KEY_Alt_R) return UFKBD_MOD_ALT; return -1; } static inline enum ufkbd_mod_state get_mod_state(const struct ufkbd_ctx *ctx, int id) { int shift = id % 16 * 2; return (ctx->mods[id / 16] >> shift) & 0x3; } static inline void set_mod_state(struct ufkbd_ctx *ctx, int id, enum ufkbd_mod_state state) { int shift = id % 16 * 2; ctx->mods[id / 16] = (ctx->mods[id / 16] & ~(0x3 << shift)) | state << shift; } bool ufkbd_is_modifier(struct ufkbd_press *press) { xkb_keysym_t sym; int part, mod; part = press->part; sym = press->key->keysyms[part]; mod = mod_key_id(sym); return mod != -1; } enum ufkbd_mod_state ufkbd_modifier_get_state(const struct ufkbd_ctx *ctx, const struct ufkbd_key *key, int part) { xkb_keysym_t sym; int mod; sym = key->keysyms[part]; mod = mod_key_id(sym); if (mod == -1) return UFKBD_MOD_RELEASED; return get_mod_state(ctx, mod); } void ufkbd_modifier_press(struct ufkbd_ctx *ctx, struct ufkbd_press *press) { xkb_keysym_t sym; enum ufkbd_mod_state curr; int part, mod; part = press->part; sym = press->key->keysyms[part]; // If this fails, the key is not a modifier key. mod = mod_key_id(sym); if (mod == -1) return; curr = get_mod_state(ctx, mod); if (curr == UFKBD_MOD_RELEASED) { set_mod_state(ctx, mod, UFKBD_MOD_PRESSED); ctx->drv->send_mod(ctx->drv_data, mod, true); } } void ufkbd_modifier_cancel(struct ufkbd_ctx *ctx, struct ufkbd_press *press) { xkb_keysym_t sym; enum ufkbd_mod_state curr; int part, mod; part = press->part; sym = press->key->keysyms[part]; mod = mod_key_id(sym); if (mod == -1) return; curr = get_mod_state(ctx, mod); if (curr == UFKBD_MOD_PRESSED) { set_mod_state(ctx, mod, UFKBD_MOD_RELEASED); ctx->drv->send_mod(ctx->drv_data, mod, false); } } void ufkbd_modifier_release(struct ufkbd_ctx *ctx, struct ufkbd_press *press) { struct timespec now = { .tv_sec = 0, .tv_nsec = 0, }; enum ufkbd_mod_state curr; xkb_keysym_t sym; int part, mod; part = press->part; sym = press->key->keysyms[part]; mod = mod_key_id(sym); if (mod == -1) return; curr = get_mod_state(ctx, mod); if (curr == UFKBD_MOD_PRESSED) { clock_gettime(CLOCK_REALTIME, &now); if (now.tv_sec > press->repeat.tv_sec || (now.tv_sec == press->repeat.tv_sec && now.tv_nsec > press->repeat.tv_nsec)) set_mod_state(ctx, mod, UFKBD_MOD_LOCKED); else set_mod_state(ctx, mod, UFKBD_MOD_LATCHED); } else if (curr == UFKBD_MOD_LATCHED || curr == UFKBD_MOD_LOCKED) { set_mod_state(ctx, mod, UFKBD_MOD_RELEASED); ctx->drv->send_mod(ctx->drv_data, mod, false); } } void ufkbd_modifier_unlatch_all(struct ufkbd_ctx *ctx) { enum ufkbd_mod_state curr; int i; for (i = 0; i < UFKBD_MOD_MAX; i++) { curr = get_mod_state(ctx, i); if (curr == UFKBD_MOD_LATCHED) { set_mod_state(ctx, i, UFKBD_MOD_RELEASED); ctx->drv->send_mod(ctx->drv_data, i, false); } else if (curr == UFKBD_MOD_PRESSED) { // This modifier key should go up on first release. set_mod_state(ctx, i, UFKBD_MOD_LOCKED); } } }