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

159 lines
3.5 KiB
C

// 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 <time.h>
#include <xkbcommon/xkbcommon.h>
#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);
}
}
}