chore(linux): Implement Linux side of caps-lock action processing

This commit is contained in:
Eberhard Beilharz 2021-07-08 18:29:22 +02:00
parent dabfea57ab
commit cfeb9e820e
No known key found for this signature in database
GPG key ID: 64A39A9E98B53105
3 changed files with 157 additions and 115 deletions

View file

@ -58,6 +58,11 @@ PKG_CHECK_MODULES(GTK, [
gtk+-3.0 >= 2.4
])
# Check X11
PKG_CHECK_MODULES(X11, [
x11 >= 1.6
])
# check atkspi 2.0
#PKG_CHECK_MODULES(ATSPI, [
# atspi-2 >= 2.0

View file

@ -22,6 +22,7 @@ AM_CFLAGS = \
@IBUS_CFLAGS@ \
@GTK_CFLAGS@ \
@JSON_GLIB_CFLAGS@ \
@X11_CFLAGS@ \
@KEYMAN_PROC_CFLAGS@ \
-DPKGDATADIR=\"$(pkgdatadir)\" \
$(NULL)
@ -29,6 +30,7 @@ AM_CFLAGS = \
AM_LDFLAGS = \
@IBUS_LIBS@ \
@GTK_LIBS@ \
@X11_LIBS@ \
@JSON_GLIB_LIBS@ \
@KEYMAN_PROC_LIBS@ \
$(NULL)
@ -73,6 +75,7 @@ ibus_engine_keyman_SOURCES = \
ibus_engine_keyman_CFLAGS = \
@IBUS_CFLAGS@ \
@GTK_CFLAGS@ \
@X11_CFLAGS@ \
@JSON_GLIB_CFLAGS@ \
@KEYMAN_PROC_CFLAGS@ \
-DPKGDATADIR=\"$(pkgdatadir)\" \
@ -81,6 +84,7 @@ ibus_engine_keyman_CFLAGS = \
ibus_engine_keyman_LDFLAGS = \
@GTK_LIBS@ \
@IBUS_LIBS@ \
@X11_LIBS@ \
@JSON_GLIB_LIBS@ \
@KEYMAN_PROC_LIBS@ \
$(NULL)

View file

@ -27,6 +27,12 @@
#include <gdk/gdk.h>
#include <gio/gio.h>
#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#include <X11/XKBlib.h>
#endif
#include <keyman/keyboardprocessor.h>
#include "keymanutil.h"
@ -41,6 +47,7 @@
#define KEYMAN_RCTRL 97
#define KEYMAN_RALT 100
#define KEYMAN_CAPS 0x3a
#define KEYMAN_CAPS_KEYSYM IBUS_KEY_Caps_Lock
typedef struct _IBusKeymanEngine IBusKeymanEngine;
typedef struct _IBusKeymanEngineClass IBusKeymanEngineClass;
@ -63,6 +70,9 @@ struct _IBusKeymanEngine {
IBusLookupTable *table;
IBusProperty *status_prop;
IBusPropList *prop_list;
#ifdef GDK_WINDOWING_X11
Display *xdisplay;
#endif
};
struct _IBusKeymanEngineClass {
@ -238,32 +248,34 @@ static void reset_context(IBusEngine *engine)
}
static void
ibus_keyman_engine_init (IBusKeymanEngine *keyman)
{
keyman->status_prop = ibus_property_new ("status",
PROP_TYPE_NORMAL,
NULL,
NULL,
NULL,
TRUE,
FALSE,
0,
NULL);
g_object_ref_sink(keyman->status_prop);
keyman->prop_list = ibus_prop_list_new ();
g_object_ref_sink(keyman->prop_list);
ibus_prop_list_append (keyman->prop_list, keyman->status_prop);
ibus_keyman_engine_init(IBusKeymanEngine *keyman) {
gdk_init(NULL, NULL);
keyman->status_prop = ibus_property_new("status", PROP_TYPE_NORMAL, NULL, NULL, NULL, TRUE, FALSE, 0, NULL);
g_object_ref_sink(keyman->status_prop);
keyman->prop_list = ibus_prop_list_new();
g_object_ref_sink(keyman->prop_list);
ibus_prop_list_append(keyman->prop_list, keyman->status_prop);
keyman->table = ibus_lookup_table_new (9, 0, TRUE, TRUE);
g_object_ref_sink(keyman->table);
keyman->state = NULL;
keyman->table = ibus_lookup_table_new(9, 0, TRUE, TRUE);
g_object_ref_sink(keyman->table);
keyman->state = NULL;
#ifdef GDK_WINDOWING_X11
keyman->xdisplay = NULL;
if (GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
keyman->xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
} else
#endif
{
g_error("unsupported GDK backend");
}
}
static GObject*
ibus_keyman_engine_constructor (GType type,
guint n_construct_params,
GObjectConstructParam *construct_params)
{
ibus_keyman_engine_constructor(
GType type,
guint n_construct_params,
GObjectConstructParam *construct_params
) {
IBusKeymanEngine *keyman;
IBusEngine *engine;
IBusText *text;
@ -584,7 +596,8 @@ static gboolean ok_for_single_backspace(const km_kbp_action_item *action_items,
return TRUE;
}
static gboolean process_unicode_char_action(
static gboolean
process_unicode_char_action(
IBusKeymanEngine *keyman,
const km_kbp_action_item *action_item
) {
@ -691,7 +704,8 @@ process_backspace_action(
return TRUE;
}
static gboolean process_persist_action(
static gboolean
process_persist_action(
IBusKeymanEngine *keyman,
const km_kbp_action_item *action_item
) {
@ -735,6 +749,23 @@ static gboolean process_invalidate_context_action(IBusEngine *engine) {
return TRUE;
}
static gboolean
process_capslock_action(
IBusKeymanEngine *keyman,
const km_kbp_action_item *action_item
) {
g_message("**** %s caps-lock", action_item->capsLock ? "Enable" : "Disable");
#ifdef GDK_WINDOWING_X11
if (keyman->xdisplay) {
XkbLockModifiers(keyman->xdisplay, XkbUseCoreKbd, LockMask, action_item->capsLock ? LockMask : 0);
XSync(keyman->xdisplay, False);
}
#endif
return TRUE;
}
static gboolean process_end_action(IBusKeymanEngine *keyman) {
keyman->firstsurrogate = 0;
if (keyman->char_buffer != NULL) {
@ -750,7 +781,8 @@ static gboolean process_end_action(IBusKeymanEngine *keyman) {
return TRUE;
}
static gboolean process_actions(
static gboolean
process_actions(
IBusEngine *engine,
const km_kbp_action_item *action_items,
size_t num_action_items
@ -786,6 +818,10 @@ static gboolean process_actions(
g_message("INVALIDATE_CONTEXT action %d/%d", i + 1, (int)num_action_items);
continue_with_next_action = process_invalidate_context_action(engine);
break;
case KM_KBP_IT_CAPSLOCK:
g_message("CAPSLOCK action %d/%d", i + 1, (int)num_action_items);
continue_with_next_action = process_capslock_action(keyman, &action_items[i]);
break;
case KM_KBP_IT_END:
g_message("END action %d/%d", i + 1, (int)num_action_items);
continue_with_next_action = process_end_action(keyman);
@ -800,103 +836,100 @@ static gboolean process_actions(
}
static gboolean
ibus_keyman_engine_process_key_event (IBusEngine *engine,
guint keyval,
guint keycode,
guint state)
{
IBusKeymanEngine *keyman = (IBusKeymanEngine *) engine;
ibus_keyman_engine_process_key_event(
IBusEngine *engine,
guint keyval,
guint keycode,
guint state
) {
IBusKeymanEngine *keyman = (IBusKeymanEngine *)engine;
g_message("DAR: ibus_keyman_engine_process_key_event - keyval=%02i, keycode=%02i, state=%02x", keyval, keycode, state);
gboolean isKeyDown = !(state & IBUS_RELEASE_MASK);
gboolean isKeyDown = !!(state & IBUS_RELEASE_MASK);
g_message("-----------------------------------------------------------------------------------------------------------------");
g_message(
"DAR: ibus_keyman_engine_process_key_event - keyval=0x%02x keycode=0x%02x, state=0x%02x, isKeyDown=%d", keyval, keycode,
state, isKeyDown);
// REVIEW: why don't we handle these keys?
switch (keycode) {
case KEYMAN_LCTRL:
keyman->lctrl_pressed = isKeyDown;
break;
case KEYMAN_RCTRL:
keyman->rctrl_pressed = isKeyDown;
break;
case KEYMAN_LALT:
keyman->lalt_pressed = isKeyDown;
break;
case KEYMAN_RALT:
keyman->ralt_pressed = isKeyDown;
break;
// REVIEW: why don't we handle these keys?
switch (keycode) {
case KEYMAN_LCTRL:
keyman->lctrl_pressed = isKeyDown;
break;
case KEYMAN_RCTRL:
keyman->rctrl_pressed = isKeyDown;
break;
case KEYMAN_LALT:
keyman->lalt_pressed = isKeyDown;
break;
case KEYMAN_RALT:
keyman->ralt_pressed = isKeyDown;
break;
}
if (keycode < 0 || keycode > 255) {
g_warning("keycode %d out of range", keycode);
return FALSE;
}
if (keycode_to_vk[keycode] == 0) { // key we don't handle
return FALSE;
}
// keyman modifiers are different from X11/ibus
uint16_t km_mod_state = 0;
if (state & IBUS_SHIFT_MASK) {
km_mod_state |= KM_KBP_MODIFIER_SHIFT;
}
if (state & IBUS_MOD5_MASK) {
km_mod_state |= KM_KBP_MODIFIER_RALT;
g_message("modstate KM_KBP_MODIFIER_RALT from IBUS_MOD5_MASK");
}
if (state & IBUS_MOD1_MASK) {
if (keyman->ralt_pressed) {
km_mod_state |= KM_KBP_MODIFIER_RALT;
g_message("modstate KM_KBP_MODIFIER_RALT from ralt_pressed");
}
if (keyman->lalt_pressed) {
km_mod_state |= KM_KBP_MODIFIER_LALT;
g_message("modstate KM_KBP_MODIFIER_LALT from lalt_pressed");
}
}
if (state & IBUS_CONTROL_MASK) {
if (keyman->rctrl_pressed) {
km_mod_state |= KM_KBP_MODIFIER_RCTRL;
g_message("modstate KM_KBP_MODIFIER_RCTRL from rctrl_pressed");
}
if (keyman->lctrl_pressed) {
km_mod_state |= KM_KBP_MODIFIER_LCTRL;
g_message("modstate KM_KBP_MODIFIER_LCTRL from lctrl_pressed");
}
}
if (state & IBUS_LOCK_MASK) {
km_mod_state |= KM_KBP_MODIFIER_CAPS;
}
g_message("before process key event");
km_kbp_context *context = km_kbp_state_context(keyman->state);
g_free(get_current_context_text(context));
g_message("DAR: ibus_keyman_engine_process_key_event - km_mod_state=0x%x", km_mod_state);
km_kbp_status event_status = km_kbp_process_event(keyman->state, keycode_to_vk[keycode], km_mod_state, isKeyDown);
context = km_kbp_state_context(keyman->state);
g_message("after process key event");
g_free(get_current_context_text(context));
if (keycode < 0 || keycode > 255) {
g_warning("keycode %d out of range", keycode);
return FALSE;
}
// km_kbp_state_action_items to get action items
size_t num_action_items;
g_free(keyman->char_buffer);
keyman->char_buffer = NULL;
const km_kbp_action_item *action_items = km_kbp_state_action_items(keyman->state, &num_action_items);
if (keycode_to_vk[keycode] == 0) { // key we don't handle
return FALSE;
}
if (!process_actions(engine, action_items, num_action_items))
return FALSE;
// keyman modifiers are different from X11/ibus
uint16_t km_mod_state = 0;
if (state & IBUS_SHIFT_MASK)
{
km_mod_state |= KM_KBP_MODIFIER_SHIFT;
}
if (state & IBUS_MOD5_MASK)
{
km_mod_state |= KM_KBP_MODIFIER_RALT;
g_message("modstate KM_KBP_MODIFIER_RALT from IBUS_MOD5_MASK");
}
if (state & IBUS_MOD1_MASK)
{
if (keyman->ralt_pressed) {
km_mod_state |= KM_KBP_MODIFIER_RALT;
g_message("modstate KM_KBP_MODIFIER_RALT from ralt_pressed");
}
if (keyman->lalt_pressed) {
km_mod_state |= KM_KBP_MODIFIER_LALT;
g_message("modstate KM_KBP_MODIFIER_LALT from lalt_pressed");
}
}
if (state & IBUS_CONTROL_MASK)
{
if (keyman->rctrl_pressed) {
km_mod_state |= KM_KBP_MODIFIER_RCTRL;
g_message("modstate KM_KBP_MODIFIER_RCTRL from rctrl_pressed");
}
if (keyman->lctrl_pressed) {
km_mod_state |= KM_KBP_MODIFIER_LCTRL;
g_message("modstate KM_KBP_MODIFIER_LCTRL from lctrl_pressed");
}
}
if (state & IBUS_LOCK_MASK)
{
km_mod_state |= KM_KBP_MODIFIER_CAPS;
}
g_message("before process key event");
km_kbp_context *context = km_kbp_state_context(keyman->state);
g_free(get_current_context_text(context));
g_message("DAR: ibus_keyman_engine_process_key_event - km_mod_state=%x", km_mod_state);
km_kbp_status event_status =
km_kbp_process_event(keyman->state, keycode_to_vk[keycode], km_mod_state, isKeyDown);
context = km_kbp_state_context(keyman->state);
g_message("after process key event");
g_free(get_current_context_text(context));
// km_kbp_state_action_items to get action items
size_t num_action_items;
g_free(keyman->char_buffer);
keyman->char_buffer = NULL;
const km_kbp_action_item *action_items = km_kbp_state_action_items(keyman->state,
&num_action_items);
if (!process_actions(engine, action_items, num_action_items))
return FALSE;
context = km_kbp_state_context(keyman->state);
g_message("after processing all actions");
g_free(get_current_context_text(context));
return TRUE;
context = km_kbp_state_context(keyman->state);
g_message("after processing all actions");
g_free(get_current_context_text(context));
return TRUE;
}
static void