From d2758a873a848fc4113a4087d53446f73c6543cb Mon Sep 17 00:00:00 2001 From: Robert Tari Date: Tue, 7 Sep 2021 01:25:17 +0200 Subject: Split X11 and Lomiri backends - CMakeLists.txt: Add separate dependencies for the X11 backend - src/keyboard.c: Fork into keyboard-x11.c and keyboard-lomiri.c - src/CMakeLists.txt: Build backend libraries - src/keyboard-lomiri.c: Add some mock defaults for Lomiri - src/service.c: Load appropriate backend during runtime - data/org.ayatana.indicator.keyboard: Move icon to leftmost position --- CMakeLists.txt | 3 +- data/org.ayatana.indicator.keyboard | 2 +- src/CMakeLists.txt | 40 ++--- src/keyboard-lomiri.c | 93 +++++++++++ src/keyboard-x11.c | 319 ++++++++++++++++++++++++++++++++++++ src/keyboard.c | 319 ------------------------------------ src/keyboard.h | 2 + src/languages.h | 2 + src/service.c | 56 ++++++- 9 files changed, 490 insertions(+), 346 deletions(-) create mode 100644 src/keyboard-lomiri.c create mode 100644 src/keyboard-x11.c delete mode 100644 src/keyboard.c diff --git a/CMakeLists.txt b/CMakeLists.txt index a9053180..de69c452 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,7 +39,8 @@ add_definitions (-DGETTEXT_PACKAGE="${GETTEXT_PACKAGE}" -DLOCALEDIR="${CMAKE_INS find_package (PkgConfig REQUIRED) include (CheckIncludeFile) include (FindPkgConfig) -pkg_check_modules(SERVICE_DEPS REQUIRED glib-2.0>=2.36 gio-2.0>=2.36 x11>=1.6.7 libxklavier>=5.4 libayatana-common>=0.9.3) +pkg_check_modules(SERVICE_DEPS REQUIRED glib-2.0>=2.36 gio-2.0>=2.36 libayatana-common>=0.9.3) +pkg_check_modules(X11_DEPS REQUIRED x11>=1.6.7 libxklavier>=5.4) include_directories (SYSTEM ${SERVICE_DEPS_INCLUDE_DIRS}) # custom targets diff --git a/data/org.ayatana.indicator.keyboard b/data/org.ayatana.indicator.keyboard index 0af4084a..30e235d3 100644 --- a/data/org.ayatana.indicator.keyboard +++ b/data/org.ayatana.indicator.keyboard @@ -5,7 +5,7 @@ Position=-10 [phone] ObjectPath=/org/ayatana/indicator/keyboard/phone -Position=25 +Position=1000 [desktop] ObjectPath=/org/ayatana/indicator/keyboard/desktop diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6efa4677..e4ca3439 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,28 +1,30 @@ -cmake_minimum_required(VERSION 2.8.12) -set (SERVICE_LIB "ayatanaindicatorkeyboardservice") -set (SERVICE_EXEC "ayatana-indicator-keyboard-service") +cmake_minimum_required(VERSION 3.13) -add_definitions(-DG_LOG_DOMAIN="ayatana-indicator-keyboard") +# libayatana-keyboard-backend-x11.so -# handwritten sources -set(SERVICE_MANUAL_SOURCES keyboard.c service.c) +add_library("ayatana-keyboard-x11" SHARED keyboard-x11.c) +target_link_libraries("ayatana-keyboard-x11" ${SERVICE_DEPS_LIBRARIES} ${X11_DEPS_LIBRARIES}) +set_target_properties("ayatana-keyboard-x11" PROPERTIES VERSION 0.0.0 SOVERSION 0) +install(TARGETS "ayatana-keyboard-x11" DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}) -# generated sources -set(SERVICE_GENERATED_SOURCES) +# libayatana-keyboard-backend-lomiri.so -# add the bin dir to our include path so the code can find the generated header files -include_directories(${CMAKE_CURRENT_BINARY_DIR}) +add_library("ayatana-keyboard-lomiri" SHARED keyboard-lomiri.c) +target_link_libraries("ayatana-keyboard-lomiri" ${SERVICE_DEPS_LIBRARIES}) +set_target_properties("ayatana-keyboard-lomiri" PROPERTIES VERSION 0.0.0 SOVERSION 0) +install(TARGETS "ayatana-keyboard-lomiri" DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}) -# add warnings/coverage info on handwritten files but not the autogenerated ones... -set_source_files_properties(${SERVICE_MANUAL_SOURCES} PROPERTIES COMPILE_FLAGS "${C_WARNING_ARGS} -g -std=c99") +# libayatanaindicatorkeyboardservice.a -# the service library for tests to link against (basically, everything except main()) -add_library(${SERVICE_LIB} STATIC ${SERVICE_MANUAL_SOURCES} ${SERVICE_GENERATED_SOURCES}) +add_definitions(-DG_LOG_DOMAIN="ayatana-indicator-keyboard") +set_source_files_properties(service.c PROPERTIES COMPILE_FLAGS "-std=c99") +add_library("ayatanaindicatorkeyboardservice" STATIC service.c) include_directories(${CMAKE_SOURCE_DIR}) link_directories(${SERVICE_DEPS_LIBRARY_DIRS}) -# the executable: lib + main() -add_executable (${SERVICE_EXEC} main.c) -set_source_files_properties(${SERVICE_SOURCES} main.c PROPERTIES COMPILE_FLAGS "${C_WARNING_ARGS} -std=c99") -target_link_libraries (${SERVICE_EXEC} ${SERVICE_LIB} ${SERVICE_DEPS_LIBRARIES}) -install (TARGETS ${SERVICE_EXEC} RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}) +# ayatana-indicator-keyboard-service + +add_executable("ayatana-indicator-keyboard-service" main.c) +set_source_files_properties(service.c main.c PROPERTIES COMPILE_FLAGS "-std=c99") +target_link_libraries("ayatana-indicator-keyboard-service" "ayatanaindicatorkeyboardservice" "${SERVICE_DEPS_LIBRARIES} -ldl") +install(TARGETS "ayatana-indicator-keyboard-service" RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}") diff --git a/src/keyboard-lomiri.c b/src/keyboard-lomiri.c new file mode 100644 index 00000000..0ae0b4a2 --- /dev/null +++ b/src/keyboard-lomiri.c @@ -0,0 +1,93 @@ +/* + * Copyright 2021 Robert Tari + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include +#include "languages.h" +#include "keyboard.h" + +enum +{ + LAYOUT_CHANGED, + CONFIG_CHANGED, + LAST_SIGNAL +}; + +static guint m_lSignals[LAST_SIGNAL]; + +struct _KeyboardPrivate +{ + guint nLayout; +}; + +typedef KeyboardPrivate priv_t; + +G_DEFINE_TYPE_WITH_PRIVATE(Keyboard, keyboard, G_TYPE_OBJECT) + +void keyboard_AddSource(Keyboard *pKeyboard) +{ + return; +} + +guint keyboard_GetNumLayouts(Keyboard *pKeyboard) +{ + return 1; +} + +void keyboard_GetLayout(Keyboard *pKeyboard, gint nLayout, gchar **pLanguage, gchar **pDescription) +{ + if (nLayout == -1) + { + nLayout = pKeyboard->pPrivate->nLayout; + } + + *pLanguage = g_strdup("En"); + + if (pDescription != NULL) + { + *pDescription = g_strdup("English"); + } +} + +void keyboard_SetLayout(Keyboard *pKeyboard, gint nLayout) +{ + g_signal_emit(pKeyboard, m_lSignals[LAYOUT_CHANGED], 0); +} + +static void onDispose(GObject *pObject) +{ + G_OBJECT_CLASS(keyboard_parent_class)->dispose(pObject); +} + +static void keyboard_class_init(KeyboardClass *klass) +{ + GObjectClass *pClass = G_OBJECT_CLASS(klass); + pClass->dispose = onDispose; + m_lSignals[LAYOUT_CHANGED] = g_signal_new(KEYBOARD_LAYOUT_CHANGED, G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); + m_lSignals[CONFIG_CHANGED] = g_signal_new(KEYBOARD_CONFIG_CHANGED, G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); +} + +Keyboard* keyboard_new() +{ + GObject *pObject = g_object_new(G_TYPE_KEYBOARD, NULL); + + return G_KEYBOARD(pObject); +} + +static void keyboard_init(Keyboard *self) +{ + self->pPrivate = keyboard_get_instance_private(self); + self->pPrivate->nLayout = 0; +} diff --git a/src/keyboard-x11.c b/src/keyboard-x11.c new file mode 100644 index 00000000..11c6d93f --- /dev/null +++ b/src/keyboard-x11.c @@ -0,0 +1,319 @@ +/* + * Copyright 2021 Robert Tari + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include +#include +#include "languages.h" +#include "keyboard.h" + +enum +{ + LAYOUT_CHANGED, + CONFIG_CHANGED, + LAST_SIGNAL +}; + +static guint m_lSignals[LAST_SIGNAL]; + +struct _KeyboardPrivate +{ + GPollFD cPollFD; + GSourceFuncs cSourceFuncs; + XklEngine *pEngine; + GHashTable *lLayouts; + Display *pDisplay; + guint nLayout; + gint nXkbEventType; + XklConfigRec *pConfigRec; +}; + +typedef KeyboardPrivate priv_t; + +G_DEFINE_TYPE_WITH_PRIVATE(Keyboard, keyboard, G_TYPE_OBJECT) + +typedef struct _Layout +{ + gchar *sId; + gchar *sLanguage; + gchar *sDescription; + +} Layout; + +typedef struct _LayoutParser +{ + const gchar *sLayout; + const gchar *sLanguage; + Keyboard *pKeyboard; + +} LayoutParser; + +typedef struct _Source +{ + GSource cSource; + Keyboard *pKeyboard; + +} Source; + +static gboolean onCheckEvent(Display *pDisplay, XEvent *pEvent, XPointer pData) +{ + gint *pXkbEventType = (gint*)pData; + + if (pEvent->type == *pXkbEventType) + { + XkbEvent *pXkbEvent = (XkbEvent*)pEvent; + + if (pXkbEvent->any.xkb_type == XkbStateNotify || pXkbEvent->any.xkb_type == XkbNamesNotify) + { + return TRUE; + } + } + + return FALSE; +} + +static gboolean onCheckSource(GSource *pSource) +{ + Keyboard *pKeyboard = ((Source*)pSource)->pKeyboard; + XEvent cEvent; + gboolean bEvent = XCheckIfEvent(pKeyboard->pPrivate->pDisplay, &cEvent, onCheckEvent, (XPointer)&pKeyboard->pPrivate->nXkbEventType); + + if (bEvent) + { + XklConfigRec *pConfigRec = xkl_config_rec_new(); + xkl_config_rec_get_from_server(pConfigRec, pKeyboard->pPrivate->pEngine); + gboolean bConfigChanged = FALSE; + gboolean bLayoutChanged = FALSE; + + if (!xkl_config_rec_equals(pKeyboard->pPrivate->pConfigRec, pConfigRec)) + { + gboolean bBadSignal = FALSE; + + if (g_strv_length(pKeyboard->pPrivate->pConfigRec->layouts) > g_strv_length(pConfigRec->layouts)) + { + // Pairing a Bluetooth device does this sometimes + if (pConfigRec->variants[0] == NULL) + { + bBadSignal = TRUE; + } + else + { + xkl_engine_lock_group(pKeyboard->pPrivate->pEngine, 0); + pKeyboard->pPrivate->nLayout = 0; + bLayoutChanged = TRUE; + } + } + + if (!bBadSignal) + { + xkl_config_rec_get_from_server(pKeyboard->pPrivate->pConfigRec, pKeyboard->pPrivate->pEngine); + bConfigChanged = TRUE; + } + } + + g_object_unref(pConfigRec); + pConfigRec = NULL; + + if (((XkbEvent*)&cEvent)->any.xkb_type == XkbStateNotify && !bConfigChanged && ((XkbEvent*)&cEvent)->state.group != pKeyboard->pPrivate->nLayout && ((XkbEvent*)&cEvent)->state.group < g_strv_length(pKeyboard->pPrivate->pConfigRec->layouts)) + { + pKeyboard->pPrivate->nLayout = ((XkbEvent*)&cEvent)->state.group; + bLayoutChanged = TRUE; + } + + if (bLayoutChanged) + { + g_signal_emit(pKeyboard, m_lSignals[LAYOUT_CHANGED], 0); + } + + if (bConfigChanged) + { + g_signal_emit(pKeyboard, m_lSignals[CONFIG_CHANGED], 0); + } + } + + return FALSE; +} + +static void freeLayout(gpointer pData) +{ + Layout *pLayout = pData; + + g_return_if_fail(pLayout != NULL); + + g_free(pLayout->sId); + g_free(pLayout->sLanguage); + g_free(pLayout->sDescription); + g_slice_free(Layout, pLayout); +} + +static void onParseLayouts(XklConfigRegistry *pRegistry, const XklConfigItem * pItem, gpointer pData) +{ + LayoutParser *pLayoutParser = (LayoutParser*)pData; + Layout *pLayout = g_slice_new0(Layout); + + if (pLayoutParser->sLayout) + { + pLayout->sId = g_strjoin("+", pLayoutParser->sLayout, pItem->name, NULL); + pLayout->sLanguage = g_strdup(lookupLanguage(pLayout->sId)); + pLayout->sDescription = g_strdup(pItem->description); + } + else + { + pLayout->sId = g_strdup(pItem->name); + pLayout->sLanguage = g_strdup(lookupLanguage(pLayout->sId)); + pLayout->sDescription = g_strdup(pItem->description); + } + + g_hash_table_replace(pLayoutParser->pKeyboard->pPrivate->lLayouts, pLayout->sId, pLayout); + + if (pLayoutParser->sLayout == NULL) + { + LayoutParser cLayoutParser; + cLayoutParser.sLayout = pItem->name; + cLayoutParser.pKeyboard = pLayoutParser->pKeyboard; + cLayoutParser.sLanguage = lookupLanguage(cLayoutParser.sLayout); + + xkl_config_registry_foreach_layout_variant(pRegistry, pItem->name, onParseLayouts, &cLayoutParser); + } +} + +void keyboard_AddSource(Keyboard *pKeyboard) +{ + XkbQueryExtension(pKeyboard->pPrivate->pDisplay, 0, &pKeyboard->pPrivate->nXkbEventType, 0, 0, 0); + XkbSelectEventDetails(pKeyboard->pPrivate->pDisplay, XkbUseCoreKbd, XkbStateNotify, XkbAllStateComponentsMask, XkbGroupStateMask); + + pKeyboard->pPrivate->cPollFD.fd = ConnectionNumber(pKeyboard->pPrivate->pDisplay); + pKeyboard->pPrivate->cPollFD.events = G_IO_IN | G_IO_HUP | G_IO_ERR; + pKeyboard->pPrivate->cPollFD.revents = 0; + pKeyboard->pPrivate->cSourceFuncs.prepare = NULL; + pKeyboard->pPrivate->cSourceFuncs.check = onCheckSource; + pKeyboard->pPrivate->cSourceFuncs.dispatch = NULL; + pKeyboard->pPrivate->cSourceFuncs.finalize = NULL; + + GSource *pSource = g_source_new(&pKeyboard->pPrivate->cSourceFuncs, sizeof(Source)); + ((Source*)pSource)->pKeyboard = pKeyboard; + g_source_add_poll(pSource, &pKeyboard->pPrivate->cPollFD); + g_source_attach(pSource, NULL); +} + +guint keyboard_GetNumLayouts(Keyboard *pKeyboard) +{ + return g_strv_length(pKeyboard->pPrivate->pConfigRec->layouts); +} + +void keyboard_GetLayout(Keyboard *pKeyboard, gint nLayout, gchar **pLanguage, gchar **pDescription) +{ + if (nLayout == -1) + { + nLayout = pKeyboard->pPrivate->nLayout; + } + + gchar *sLayout = pKeyboard->pPrivate->pConfigRec->layouts[nLayout]; + gchar *sVariant = pKeyboard->pPrivate->pConfigRec->variants[nLayout]; + gchar *sId; + + if (sVariant && strlen(sVariant)) + { + sId = g_strconcat(sLayout, "+", sVariant, NULL); + } + else + { + sId = g_strdup(sLayout); + } + + const Layout *pLayout; + g_hash_table_lookup_extended(pKeyboard->pPrivate->lLayouts, sId, NULL, (gpointer*)&pLayout); + + if (pLanguage != NULL) + { + *pLanguage = g_strdup(pLayout->sLanguage); + } + + if (pDescription != NULL) + { + *pDescription = g_strdup(pLayout->sDescription); + } + + g_free(sId); +} + +void keyboard_SetLayout(Keyboard *pKeyboard, gint nLayout) +{ + xkl_engine_lock_group(pKeyboard->pPrivate->pEngine, nLayout); + pKeyboard->pPrivate->nLayout = nLayout; + g_signal_emit(pKeyboard, m_lSignals[LAYOUT_CHANGED], 0); +} + +static void onDispose(GObject *pObject) +{ + Keyboard *self = G_KEYBOARD(pObject); + + if (self->pPrivate->lLayouts) + { + g_hash_table_destroy(self->pPrivate->lLayouts); + } + + if (self->pPrivate->pConfigRec) + { + g_object_unref(self->pPrivate->pConfigRec); + self->pPrivate->pConfigRec = NULL; + } + + G_OBJECT_CLASS(keyboard_parent_class)->dispose(pObject); +} + +static void keyboard_class_init(KeyboardClass *klass) +{ + GObjectClass *pClass = G_OBJECT_CLASS(klass); + pClass->dispose = onDispose; + m_lSignals[LAYOUT_CHANGED] = g_signal_new(KEYBOARD_LAYOUT_CHANGED, G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); + m_lSignals[CONFIG_CHANGED] = g_signal_new(KEYBOARD_CONFIG_CHANGED, G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); +} + +Keyboard* keyboard_new() +{ + GObject *pObject = g_object_new(G_TYPE_KEYBOARD, NULL); + + return G_KEYBOARD(pObject); +} + +static void keyboard_init(Keyboard *self) +{ + self->pPrivate = keyboard_get_instance_private(self); + self->pPrivate->pDisplay = XOpenDisplay(NULL); + + g_assert(self->pPrivate->pDisplay); + + self->pPrivate->pEngine = xkl_engine_get_instance(self->pPrivate->pDisplay); + + g_assert(self->pPrivate->pEngine); + + self->pPrivate->lLayouts = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, freeLayout); + XklConfigRegistry *pRegistry = xkl_config_registry_get_instance(self->pPrivate->pEngine); + xkl_config_registry_load(pRegistry, TRUE); + + LayoutParser cLayoutParser; + cLayoutParser.sLayout = NULL; + cLayoutParser.pKeyboard = self; + cLayoutParser.sLanguage = NULL; + xkl_config_registry_foreach_layout(pRegistry, onParseLayouts, &cLayoutParser); + + xkl_engine_start_listen(self->pPrivate->pEngine, XKLL_TRACK_KEYBOARD_STATE); + + self->pPrivate->pConfigRec = xkl_config_rec_new(); + xkl_config_rec_get_from_server(self->pPrivate->pConfigRec, self->pPrivate->pEngine); + XklState *pState = xkl_engine_get_current_state(self->pPrivate->pEngine); + self->pPrivate->nLayout = pState->group; +} diff --git a/src/keyboard.c b/src/keyboard.c deleted file mode 100644 index 11c6d93f..00000000 --- a/src/keyboard.c +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Copyright 2021 Robert Tari - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#include -#include -#include "languages.h" -#include "keyboard.h" - -enum -{ - LAYOUT_CHANGED, - CONFIG_CHANGED, - LAST_SIGNAL -}; - -static guint m_lSignals[LAST_SIGNAL]; - -struct _KeyboardPrivate -{ - GPollFD cPollFD; - GSourceFuncs cSourceFuncs; - XklEngine *pEngine; - GHashTable *lLayouts; - Display *pDisplay; - guint nLayout; - gint nXkbEventType; - XklConfigRec *pConfigRec; -}; - -typedef KeyboardPrivate priv_t; - -G_DEFINE_TYPE_WITH_PRIVATE(Keyboard, keyboard, G_TYPE_OBJECT) - -typedef struct _Layout -{ - gchar *sId; - gchar *sLanguage; - gchar *sDescription; - -} Layout; - -typedef struct _LayoutParser -{ - const gchar *sLayout; - const gchar *sLanguage; - Keyboard *pKeyboard; - -} LayoutParser; - -typedef struct _Source -{ - GSource cSource; - Keyboard *pKeyboard; - -} Source; - -static gboolean onCheckEvent(Display *pDisplay, XEvent *pEvent, XPointer pData) -{ - gint *pXkbEventType = (gint*)pData; - - if (pEvent->type == *pXkbEventType) - { - XkbEvent *pXkbEvent = (XkbEvent*)pEvent; - - if (pXkbEvent->any.xkb_type == XkbStateNotify || pXkbEvent->any.xkb_type == XkbNamesNotify) - { - return TRUE; - } - } - - return FALSE; -} - -static gboolean onCheckSource(GSource *pSource) -{ - Keyboard *pKeyboard = ((Source*)pSource)->pKeyboard; - XEvent cEvent; - gboolean bEvent = XCheckIfEvent(pKeyboard->pPrivate->pDisplay, &cEvent, onCheckEvent, (XPointer)&pKeyboard->pPrivate->nXkbEventType); - - if (bEvent) - { - XklConfigRec *pConfigRec = xkl_config_rec_new(); - xkl_config_rec_get_from_server(pConfigRec, pKeyboard->pPrivate->pEngine); - gboolean bConfigChanged = FALSE; - gboolean bLayoutChanged = FALSE; - - if (!xkl_config_rec_equals(pKeyboard->pPrivate->pConfigRec, pConfigRec)) - { - gboolean bBadSignal = FALSE; - - if (g_strv_length(pKeyboard->pPrivate->pConfigRec->layouts) > g_strv_length(pConfigRec->layouts)) - { - // Pairing a Bluetooth device does this sometimes - if (pConfigRec->variants[0] == NULL) - { - bBadSignal = TRUE; - } - else - { - xkl_engine_lock_group(pKeyboard->pPrivate->pEngine, 0); - pKeyboard->pPrivate->nLayout = 0; - bLayoutChanged = TRUE; - } - } - - if (!bBadSignal) - { - xkl_config_rec_get_from_server(pKeyboard->pPrivate->pConfigRec, pKeyboard->pPrivate->pEngine); - bConfigChanged = TRUE; - } - } - - g_object_unref(pConfigRec); - pConfigRec = NULL; - - if (((XkbEvent*)&cEvent)->any.xkb_type == XkbStateNotify && !bConfigChanged && ((XkbEvent*)&cEvent)->state.group != pKeyboard->pPrivate->nLayout && ((XkbEvent*)&cEvent)->state.group < g_strv_length(pKeyboard->pPrivate->pConfigRec->layouts)) - { - pKeyboard->pPrivate->nLayout = ((XkbEvent*)&cEvent)->state.group; - bLayoutChanged = TRUE; - } - - if (bLayoutChanged) - { - g_signal_emit(pKeyboard, m_lSignals[LAYOUT_CHANGED], 0); - } - - if (bConfigChanged) - { - g_signal_emit(pKeyboard, m_lSignals[CONFIG_CHANGED], 0); - } - } - - return FALSE; -} - -static void freeLayout(gpointer pData) -{ - Layout *pLayout = pData; - - g_return_if_fail(pLayout != NULL); - - g_free(pLayout->sId); - g_free(pLayout->sLanguage); - g_free(pLayout->sDescription); - g_slice_free(Layout, pLayout); -} - -static void onParseLayouts(XklConfigRegistry *pRegistry, const XklConfigItem * pItem, gpointer pData) -{ - LayoutParser *pLayoutParser = (LayoutParser*)pData; - Layout *pLayout = g_slice_new0(Layout); - - if (pLayoutParser->sLayout) - { - pLayout->sId = g_strjoin("+", pLayoutParser->sLayout, pItem->name, NULL); - pLayout->sLanguage = g_strdup(lookupLanguage(pLayout->sId)); - pLayout->sDescription = g_strdup(pItem->description); - } - else - { - pLayout->sId = g_strdup(pItem->name); - pLayout->sLanguage = g_strdup(lookupLanguage(pLayout->sId)); - pLayout->sDescription = g_strdup(pItem->description); - } - - g_hash_table_replace(pLayoutParser->pKeyboard->pPrivate->lLayouts, pLayout->sId, pLayout); - - if (pLayoutParser->sLayout == NULL) - { - LayoutParser cLayoutParser; - cLayoutParser.sLayout = pItem->name; - cLayoutParser.pKeyboard = pLayoutParser->pKeyboard; - cLayoutParser.sLanguage = lookupLanguage(cLayoutParser.sLayout); - - xkl_config_registry_foreach_layout_variant(pRegistry, pItem->name, onParseLayouts, &cLayoutParser); - } -} - -void keyboard_AddSource(Keyboard *pKeyboard) -{ - XkbQueryExtension(pKeyboard->pPrivate->pDisplay, 0, &pKeyboard->pPrivate->nXkbEventType, 0, 0, 0); - XkbSelectEventDetails(pKeyboard->pPrivate->pDisplay, XkbUseCoreKbd, XkbStateNotify, XkbAllStateComponentsMask, XkbGroupStateMask); - - pKeyboard->pPrivate->cPollFD.fd = ConnectionNumber(pKeyboard->pPrivate->pDisplay); - pKeyboard->pPrivate->cPollFD.events = G_IO_IN | G_IO_HUP | G_IO_ERR; - pKeyboard->pPrivate->cPollFD.revents = 0; - pKeyboard->pPrivate->cSourceFuncs.prepare = NULL; - pKeyboard->pPrivate->cSourceFuncs.check = onCheckSource; - pKeyboard->pPrivate->cSourceFuncs.dispatch = NULL; - pKeyboard->pPrivate->cSourceFuncs.finalize = NULL; - - GSource *pSource = g_source_new(&pKeyboard->pPrivate->cSourceFuncs, sizeof(Source)); - ((Source*)pSource)->pKeyboard = pKeyboard; - g_source_add_poll(pSource, &pKeyboard->pPrivate->cPollFD); - g_source_attach(pSource, NULL); -} - -guint keyboard_GetNumLayouts(Keyboard *pKeyboard) -{ - return g_strv_length(pKeyboard->pPrivate->pConfigRec->layouts); -} - -void keyboard_GetLayout(Keyboard *pKeyboard, gint nLayout, gchar **pLanguage, gchar **pDescription) -{ - if (nLayout == -1) - { - nLayout = pKeyboard->pPrivate->nLayout; - } - - gchar *sLayout = pKeyboard->pPrivate->pConfigRec->layouts[nLayout]; - gchar *sVariant = pKeyboard->pPrivate->pConfigRec->variants[nLayout]; - gchar *sId; - - if (sVariant && strlen(sVariant)) - { - sId = g_strconcat(sLayout, "+", sVariant, NULL); - } - else - { - sId = g_strdup(sLayout); - } - - const Layout *pLayout; - g_hash_table_lookup_extended(pKeyboard->pPrivate->lLayouts, sId, NULL, (gpointer*)&pLayout); - - if (pLanguage != NULL) - { - *pLanguage = g_strdup(pLayout->sLanguage); - } - - if (pDescription != NULL) - { - *pDescription = g_strdup(pLayout->sDescription); - } - - g_free(sId); -} - -void keyboard_SetLayout(Keyboard *pKeyboard, gint nLayout) -{ - xkl_engine_lock_group(pKeyboard->pPrivate->pEngine, nLayout); - pKeyboard->pPrivate->nLayout = nLayout; - g_signal_emit(pKeyboard, m_lSignals[LAYOUT_CHANGED], 0); -} - -static void onDispose(GObject *pObject) -{ - Keyboard *self = G_KEYBOARD(pObject); - - if (self->pPrivate->lLayouts) - { - g_hash_table_destroy(self->pPrivate->lLayouts); - } - - if (self->pPrivate->pConfigRec) - { - g_object_unref(self->pPrivate->pConfigRec); - self->pPrivate->pConfigRec = NULL; - } - - G_OBJECT_CLASS(keyboard_parent_class)->dispose(pObject); -} - -static void keyboard_class_init(KeyboardClass *klass) -{ - GObjectClass *pClass = G_OBJECT_CLASS(klass); - pClass->dispose = onDispose; - m_lSignals[LAYOUT_CHANGED] = g_signal_new(KEYBOARD_LAYOUT_CHANGED, G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); - m_lSignals[CONFIG_CHANGED] = g_signal_new(KEYBOARD_CONFIG_CHANGED, G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); -} - -Keyboard* keyboard_new() -{ - GObject *pObject = g_object_new(G_TYPE_KEYBOARD, NULL); - - return G_KEYBOARD(pObject); -} - -static void keyboard_init(Keyboard *self) -{ - self->pPrivate = keyboard_get_instance_private(self); - self->pPrivate->pDisplay = XOpenDisplay(NULL); - - g_assert(self->pPrivate->pDisplay); - - self->pPrivate->pEngine = xkl_engine_get_instance(self->pPrivate->pDisplay); - - g_assert(self->pPrivate->pEngine); - - self->pPrivate->lLayouts = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, freeLayout); - XklConfigRegistry *pRegistry = xkl_config_registry_get_instance(self->pPrivate->pEngine); - xkl_config_registry_load(pRegistry, TRUE); - - LayoutParser cLayoutParser; - cLayoutParser.sLayout = NULL; - cLayoutParser.pKeyboard = self; - cLayoutParser.sLanguage = NULL; - xkl_config_registry_foreach_layout(pRegistry, onParseLayouts, &cLayoutParser); - - xkl_engine_start_listen(self->pPrivate->pEngine, XKLL_TRACK_KEYBOARD_STATE); - - self->pPrivate->pConfigRec = xkl_config_rec_new(); - xkl_config_rec_get_from_server(self->pPrivate->pConfigRec, self->pPrivate->pEngine); - XklState *pState = xkl_engine_get_current_state(self->pPrivate->pEngine); - self->pPrivate->nLayout = pState->group; -} diff --git a/src/keyboard.h b/src/keyboard.h index 24c4aa19..20ae9dac 100644 --- a/src/keyboard.h +++ b/src/keyboard.h @@ -17,6 +17,8 @@ #ifndef __KEYBOARD_H__ #define __KEYBOARD_H__ +#include + G_BEGIN_DECLS #define KEYBOARD_LAYOUT_CHANGED "layout-changed" diff --git a/src/languages.h b/src/languages.h index bd121000..f3c1a7ea 100644 --- a/src/languages.h +++ b/src/languages.h @@ -14,6 +14,8 @@ * with this program. If not, see . */ +#include + gchar *LANGUAGES[] = { "Ap", //APL diff --git a/src/service.c b/src/service.c index afeed80f..ac94f0b5 100644 --- a/src/service.c +++ b/src/service.c @@ -17,12 +17,19 @@ #include #include #include +#include #include "service.h" #define BUS_NAME "org.ayatana.indicator.keyboard" #define BUS_PATH "/org/ayatana/indicator/keyboard" static guint m_nSignal = 0; +static void *m_pLibHandle = NULL; +static Keyboard* (*m_fnKeyboardNew)(); +static void (*m_fnKeyboardAddSource)(Keyboard *pKeyboard); +static guint (*m_fnKeyboardGetNumLayouts)(Keyboard *pKeyboard); +static void (*m_fnKeyboardGetLayout)(Keyboard *pKeyboard, gint nLayout, gchar **pLanguage, gchar **pDescription); +static void (*m_fnKeyboardSetLayout)(Keyboard *pKeyboard, gint nLayout); enum { @@ -81,7 +88,7 @@ static GVariant* createHeaderState(IndicatorKeyboardService *self) g_variant_builder_add(&cBuilder, "{sv}", "visible", g_variant_new_boolean(TRUE)); gchar *sLanguage; - keyboard_GetLayout(self->pPrivate->pKeyboard, -1, &sLanguage, NULL); + m_fnKeyboardGetLayout(self->pPrivate->pKeyboard, -1, &sLanguage, NULL); gchar *sIcon = g_strconcat("ayatana-indicator-keyboard-", sLanguage, NULL); g_free(sLanguage); @@ -110,13 +117,13 @@ static GMenuModel* createDesktopLayoutSection(IndicatorKeyboardService *self, in { self->pPrivate->pLayoutSection = g_menu_new(); - guint nLayouts = keyboard_GetNumLayouts(self->pPrivate->pKeyboard); + guint nLayouts = m_fnKeyboardGetNumLayouts(self->pPrivate->pKeyboard); for (guint nLayout = 0; nLayout < nLayouts; nLayout++) { gchar *sLanguage; gchar *sDescription; - keyboard_GetLayout(self->pPrivate->pKeyboard, nLayout, &sLanguage, &sDescription); + m_fnKeyboardGetLayout(self->pPrivate->pKeyboard, nLayout, &sLanguage, &sDescription); GMenuItem *pItem = g_menu_item_new(sDescription, NULL); g_free(sDescription); g_menu_item_set_action_and_target_value(pItem, "indicator.layout", g_variant_new_byte(nLayout)); @@ -251,7 +258,7 @@ static void onLayoutSelected(GSimpleAction *pAction, GVariant *pVariant, gpointe { IndicatorKeyboardService *self = INDICATOR_KEYBOARD_SERVICE(pData); const guint8 nLayout = g_variant_get_byte(pVariant); - keyboard_SetLayout(self->pPrivate->pKeyboard, nLayout); + m_fnKeyboardSetLayout(self->pPrivate->pKeyboard, nLayout); } static void onSettings(GSimpleAction *pAction, GVariant *pVariant, gpointer pUserData) @@ -387,14 +394,51 @@ static void onDispose(GObject *pObject) g_clear_object (&self->pPrivate->pActionGroup); g_clear_object (&self->pPrivate->pConnection); + if (m_pLibHandle) + { + dlclose(m_pLibHandle); + m_pLibHandle = NULL; + } + G_OBJECT_CLASS(indicator_keyboard_service_parent_class)->dispose(pObject); } static void indicator_keyboard_service_init(IndicatorKeyboardService *self) { + gchar *sLib = "libayatana-keyboard-x11.so"; + GFile *pFile = g_file_new_for_path("/usr/lib/maliit/plugins/liblomiri-keyboard-plugin.so"); + gboolean bExists = g_file_query_exists(pFile, NULL); + g_object_unref(pFile); + + if (bExists) + { + sLib = "libayatana-keyboard-lomiri.so"; + } + + m_pLibHandle = dlopen(sLib, RTLD_NOW); + + if (!m_pLibHandle) + { + g_error("%s", dlerror()); + } + + m_fnKeyboardNew = dlsym(m_pLibHandle, "keyboard_new"); + + gchar *sError = dlerror(); + + if (sError != NULL) + { + g_error("%s", sError); + } + + m_fnKeyboardAddSource = dlsym(m_pLibHandle, "keyboard_AddSource"); + m_fnKeyboardGetNumLayouts = dlsym(m_pLibHandle, "keyboard_GetNumLayouts"); + m_fnKeyboardGetLayout = dlsym(m_pLibHandle, "keyboard_GetLayout"); + m_fnKeyboardSetLayout = dlsym(m_pLibHandle, "keyboard_SetLayout"); + self->pPrivate = indicator_keyboard_service_get_instance_private(self); self->pPrivate->pCancellable = g_cancellable_new(); - self->pPrivate->pKeyboard = keyboard_new(); + self->pPrivate->pKeyboard = m_fnKeyboardNew(); g_signal_connect(self->pPrivate->pKeyboard, KEYBOARD_LAYOUT_CHANGED, G_CALLBACK(onLayoutChanged), self); g_signal_connect(self->pPrivate->pKeyboard, KEYBOARD_CONFIG_CHANGED, G_CALLBACK(onConfigChanged), self); initActions(self); @@ -424,5 +468,5 @@ IndicatorKeyboardService *indicator_keyboard_service_new() void indicator_keyboard_service_AddKeyboardSource(IndicatorKeyboardService *self) { - keyboard_AddSource(self->pPrivate->pKeyboard); + m_fnKeyboardAddSource(self->pPrivate->pKeyboard); } -- cgit v1.2.3