159 lines
3.5 KiB
C
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);
|
|
}
|
|
}
|
|
}
|