diff --git a/.gitignore b/.gitignore index 1a4c153947..832df984e1 100644 --- a/.gitignore +++ b/.gitignore @@ -229,6 +229,7 @@ # VS Code .vscode + .DS_Store *.kpj.user diff --git a/linux/ibus-kmfl/src/Makefile.am b/linux/ibus-kmfl/src/Makefile.am index df1e959e8f..d52a1aae08 100644 --- a/linux/ibus-kmfl/src/Makefile.am +++ b/linux/ibus-kmfl/src/Makefile.am @@ -53,6 +53,8 @@ ibus_engine_kmfl_SOURCES = \ main.c \ engine.c \ engine.h \ + keyman-service.c \ + keyman-service.h \ $(NULL) ibus_engine_kmfl_CFLAGS = \ @IBUS_CFLAGS@ \ diff --git a/linux/ibus-kmfl/src/engine.c b/linux/ibus-kmfl/src/engine.c index 5b825359d7..f7e01b73a2 100644 --- a/linux/ibus-kmfl/src/engine.c +++ b/linux/ibus-kmfl/src/engine.c @@ -29,6 +29,7 @@ #include #include "kmflutil.h" +#include "keyman-service.h" #include "engine.h" typedef struct _IBusKMFLEngine IBusKMFLEngine; @@ -196,6 +197,7 @@ ibus_kmfl_engine_constructor (GType type, engine_name = ibus_engine_get_name ((IBusEngine *) kmfl); g_assert (engine_name); + g_message("DAR: ibus_kmfl_engine_constructor %s", engine_name); if (im_table == NULL) { im_table = g_hash_table_new_full (g_str_hash, @@ -231,10 +233,11 @@ static void ibus_kmfl_engine_destroy (IBusKMFLEngine *kmfl) { const gchar *engine_name; - g_debug("DAR: ibus_kmfl_engine_destroy"); + g_debug("DAR: ibus_kmfl_engine_destroy"); engine_name = ibus_engine_get_name ((IBusEngine *) kmfl); g_assert (engine_name); + g_message("DAR: ibus_kmfl_engine_destroy %s", engine_name); if (kmfl->prop_list) { g_debug("DAR: unref kmfl->prop_list"); @@ -402,17 +405,39 @@ ibus_kmfl_engine_reset (IBusEngine *engine) static void ibus_kmfl_engine_enable (IBusEngine *engine) { + const gchar *engine_name; IBusKMFLEngine *kmfl = (IBusKMFLEngine *) engine; + KInputMethod *im; + engine_name = ibus_engine_get_name (engine); + g_assert (engine_name); + g_message("WDG: ibus_kmfl_engine_enable %s", engine_name); + im = (KInputMethod *) g_hash_table_lookup (im_table, engine_name); + // own dbus name com.Keyman + // expose properties LDMLFile and Name + KeymanService *service = km_service_get_default(); + //const gchar *ldmlfile = ""; + km_service_set_ldmlfile (service, im->keyboard_ldmlfile); + km_service_set_name (service, im->keyboard_name); parent_class->enable (engine); } static void ibus_kmfl_engine_disable (IBusEngine *engine) { + const gchar *engine_name; IBusKMFLEngine *kmfl = (IBusKMFLEngine *) engine; + engine_name = ibus_engine_get_name (engine); + g_assert (engine_name); + g_message("WDG: ibus_kmfl_engine_disable %s", engine_name); ibus_kmfl_engine_focus_out (engine); + // stop owning dbus name com.Keyman + KeymanService *service = km_service_get_default(); + km_service_set_ldmlfile (service, ""); + km_service_set_name (service, "None"); + // g_clear_object(&service); + parent_class->disable (engine); } diff --git a/linux/ibus-kmfl/src/keyman-service.c b/linux/ibus-kmfl/src/keyman-service.c new file mode 100644 index 0000000000..c768430677 --- /dev/null +++ b/linux/ibus-kmfl/src/keyman-service.c @@ -0,0 +1,379 @@ +/* vim:set et sts=4: */ + +/* + * KMFL Input Method for IBUS (The Input Bus) + * + * Copyright (C) 2018 SIL International + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * + */ + + /* + This file is based on work from Mousetweaks + Copyright © 2007-2010 Gerd Kohlberger +*/ + +#include + +#include "keyman-service.h" + +struct _KeymanServicePrivate +{ + guint owner_id; + GDBusNodeInfo *ispec; + + gchar * name; + gchar * ldmlfile; +}; + +enum +{ + PROP_0, + PROP_NAME, + PROP_LDMLFILE +}; + +static const gchar introspection_xml[] = + "" + " " + " " + " " + " " + ""; + +static void km_service_bus_acquired (GDBusConnection *connection, + const gchar *name, + gpointer data); + +G_DEFINE_TYPE (KeymanService, km_service, G_TYPE_OBJECT) + +static void +km_service_init (KeymanService *service) +{ + KeymanServicePrivate *priv; + GError *error = NULL; + + g_message("WDG: km_service_init"); + + service->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (service, + KM_TYPE_SERVICE, + KeymanServicePrivate); + + priv->name = g_strdup ("None"); + priv->ldmlfile = g_strdup (""); + priv->owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, + KEYMAN_DBUS_NAME, + G_BUS_NAME_OWNER_FLAGS_NONE, + km_service_bus_acquired, + NULL, NULL, + service, NULL); + priv->ispec = g_dbus_node_info_new_for_xml (introspection_xml, &error); + if (error) + { + g_warning ("%s\n", error->message); + g_error_free (error); + } +} + +static void +km_service_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + KeymanService *service = KM_SERVICE (object); + + switch (prop_id) + { + case PROP_NAME: + if (service->priv->name) + { + g_free(service->priv->name); + } + service->priv->name = g_value_dup_string (value); + break; + case PROP_LDMLFILE: + if (service->priv->ldmlfile) + { + g_free(service->priv->ldmlfile); + } + service->priv->ldmlfile = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +km_service_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + KeymanService *service = KM_SERVICE (object); + + switch (prop_id) + { + case PROP_NAME: + g_value_set_string (value, service->priv->name); + break; + case PROP_LDMLFILE: + g_value_set_string (value, service->priv->ldmlfile); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +km_service_dispose (GObject *object) +{ + g_message("WDG: km_service_dispose"); + KeymanServicePrivate *priv = KM_SERVICE (object)->priv; + + if (priv->owner_id) + { + g_bus_unown_name (priv->owner_id); + priv->owner_id = 0; + } + + if (priv->ispec) + { + g_dbus_node_info_unref (priv->ispec); + priv->ispec = NULL; + } + + if (priv->name) + { + g_free(priv->name); + } + + if (priv->ldmlfile) + { + g_free(priv->ldmlfile); + } + + G_OBJECT_CLASS (km_service_parent_class)->dispose (object); +} + +static void +km_service_class_init (KeymanServiceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = km_service_get_property; + object_class->set_property = km_service_set_property; + object_class->dispose = km_service_dispose; + + g_object_class_install_property (object_class, + PROP_NAME, + g_param_spec_string ("name", + "Name", + "Name of the currently active Keyman keyboard", + "None", + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, + PROP_LDMLFILE, + g_param_spec_string ("ldmlfile", + "LDML file", + "On screen keyboard file for the currently active Keyman keyboard", + "", + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_type_class_add_private (klass, sizeof (KeymanServicePrivate)); +} + +static GVariant * +handle_get_property (GDBusConnection *connection, + const gchar *sender, + const gchar *path, + const gchar *interface_name, + const gchar *property, + GError **error, + KeymanService *service) +{ + GVariant *ret = NULL; + + if (g_strcmp0 (property, "Name") == 0) + { + ret = g_variant_new_string (service->priv->name); + } + else if (g_strcmp0 (property, "LDMLFile") == 0) + { + ret = g_variant_new_string (service->priv->ldmlfile); + } + return ret; +} + +static gboolean +handle_set_property (GDBusConnection *connection, + const gchar *sender, + const gchar *path, + const gchar *interface, + const gchar *property, + GVariant *value, + GError **error, + KeymanService *service) +{ + gsize sz; + if (g_strcmp0 (property, "Name") == 0) + { + km_service_set_name (service, g_variant_dup_string (value, &sz)); + } + else if (g_strcmp0 (property, "LDMLFile") == 0) + { + km_service_set_ldmlfile (service, g_variant_dup_string (value, &sz)); + } + return TRUE; +} + +static const GDBusInterfaceVTable interface_vtable = +{ + (GDBusInterfaceMethodCallFunc) NULL, + (GDBusInterfaceGetPropertyFunc) handle_get_property, + (GDBusInterfaceSetPropertyFunc) handle_set_property +}; + +static void +emit_property_changed (GObject *object, + GParamSpec *pspec, + GDBusConnection *connection) +{ + KeymanService *service = KM_SERVICE (object); + GError *error = NULL; + GVariantBuilder builder, inv_builder; + GVariant *prop_v; + + g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY); + g_variant_builder_init (&inv_builder, G_VARIANT_TYPE ("as")); + + if (g_strcmp0 (pspec->name, "name") == 0) + { + g_variant_builder_add (&builder, "{sv}", "Name", + g_variant_new_string (service->priv->name)); + } + else if (g_strcmp0 (pspec->name, "ldmlfile") == 0) + { + g_variant_builder_add (&builder, "{sv}", "LDMLFile", + g_variant_new_string (service->priv->ldmlfile)); + } + + prop_v = g_variant_new ("(sa{sv}as)", + KEYMAN_DBUS_IFACE, + &builder, &inv_builder); + + if (!g_dbus_connection_emit_signal (connection, NULL, + KEYMAN_DBUS_PATH, + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + prop_v, &error)) + { + g_warning ("%s\n", error->message); + g_error_free (error); + } +} + +static void +km_service_bus_acquired (GDBusConnection *connection, + const gchar *name, + gpointer data) +{ + KeymanService *service = data; + + if (service->priv->ispec) + { + GError *error = NULL; + + g_dbus_connection_register_object (connection, + KEYMAN_DBUS_PATH, + service->priv->ispec->interfaces[0], + &interface_vtable, + service, NULL, &error); + if (error) + { + g_warning ("%s", error->message); + g_error_free (error); + } + + g_signal_connect (service, "notify", + G_CALLBACK (emit_property_changed), connection); + } +} + +KeymanService * +km_service_get_default (void) +{ + static KeymanService *service = NULL; + + // if (service) + // { + // g_free(service); + // service = g_object_new (KM_TYPE_SERVICE, NULL); + // g_object_add_weak_pointer (G_OBJECT (service), (gpointer *) &service); + // } + if (!service) + { + service = g_object_new (KM_TYPE_SERVICE, NULL); + g_object_add_weak_pointer (G_OBJECT (service), (gpointer *) &service); + } + return service; +} + +void +km_service_set_name (KeymanService *service, + const gchar *name) +{ + g_return_if_fail (KM_IS_SERVICE (service)); + + if (g_strcmp0(name,service->priv->name) != 0) + { + g_free(service->priv->name); + service->priv->name = g_strdup(name); + g_object_notify (G_OBJECT (service), "name"); + } +} + +// const gchar * +// km_service_get_name (KeymanService *service) +// { +// g_return_val_if_fail (KM_IS_SERVICE (service), -1); + +// return service->priv->name; +// } + +void +km_service_set_ldmlfile (KeymanService *service, + const gchar *xml) +{ + g_return_if_fail (KM_IS_SERVICE (service)); + + if (g_strcmp0(xml,service->priv->ldmlfile) != 0) + { + g_free(service->priv->ldmlfile); + service->priv->ldmlfile = g_strdup(xml); + g_object_notify (G_OBJECT (service), "ldmlfile"); + } +} + +// const gchar * +// km_service_get_ldmlfile (KeymanService *service) +// { +// g_return_val_if_fail (KM_IS_SERVICE (service), -1); + +// return service->priv->ldmlfile; +// } diff --git a/linux/ibus-kmfl/src/keyman-service.h b/linux/ibus-kmfl/src/keyman-service.h new file mode 100644 index 0000000000..0ce5237b77 --- /dev/null +++ b/linux/ibus-kmfl/src/keyman-service.h @@ -0,0 +1,69 @@ +/* vim:set et sts=4: */ + +/* + * KMFL Input Method for IBUS (The Input Bus) + * + * Copyright (C) 2018 SIL International + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * + */ + + /* + This file is based on work from Mousetweaks + Copyright © 2007-2010 Gerd Kohlberger +*/ + +#ifndef __KM_SERVICE_H__ +#define __KM_SERVICE_H__ + +#include + +G_BEGIN_DECLS + +#define KEYMAN_DBUS_NAME "com.Keyman" +#define KEYMAN_DBUS_IFACE "com.Keyman" +#define KEYMAN_DBUS_PATH "/com/Keyman/IBus" + +#define KM_TYPE_SERVICE (km_service_get_type ()) +#define KM_SERVICE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), KM_TYPE_SERVICE, KeymanService)) +#define KM_IS_SERVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), KM_TYPE_SERVICE)) + +typedef GObjectClass KeymanServiceClass; +typedef struct _KeymanService KeymanService; +typedef struct _KeymanServicePrivate KeymanServicePrivate; + +struct _KeymanService +{ + GObject parent; + KeymanServicePrivate *priv; +}; + +GType km_service_get_type (void) G_GNUC_CONST; + +KeymanService * km_service_get_default (void); + +// const gchar * km_service_get_ldmlfile (KeymanService *service); +void km_service_set_ldmlfile (KeymanService *service, + const gchar *xml); + +// const gchar * km_service_get_name (KeymanService *service); +void km_service_set_name (KeymanService *service, + const gchar *name); + + +G_END_DECLS + +#endif /* __KM_SERVICE_H__ */ diff --git a/linux/ibus-kmfl/src/kmflutil.c b/linux/ibus-kmfl/src/kmflutil.c index 9af72adef6..983ce98662 100644 --- a/linux/ibus-kmfl/src/kmflutil.c +++ b/linux/ibus-kmfl/src/kmflutil.c @@ -146,11 +146,46 @@ gchar * kmfl_get_icon_file(KInputMethod * im) return full_path_to_icon_file; } +gchar * kmfl_get_ldml_file(KInputMethod * im) +{ + gchar * full_path_to_ldml_file=NULL, *p, *filename; + struct stat filestat; + + if(g_strcmp0(im->keyboard_visualkeyboard, "") == 0) + { + g_debug("WDG: no kvk(s) so no ldml file"); + return g_strdup(""); + } + p=rindex(im->keyboard_visualkeyboard,'.'); + if (p==NULL) + { + g_debug("WDG: couldn't find . in vk filename %s", im->keyboard_visualkeyboard); + return g_strdup(""); + } + if (strncmp(p, ".kvk", 4) != 0) // sometimes kmn have kvks as the "visual keyboard" + { + g_debug("WDG: visual keyboard is not a kvk. Filename: %s", im->keyboard_visualkeyboard); + return g_strdup(""); + } + filename = g_strndup(im->keyboard_visualkeyboard, p-(im->keyboard_visualkeyboard)); + full_path_to_ldml_file=g_strdup_printf("%s/%s.ldml", get_dirname(im->keyboard_filename), filename); + g_free(filename); + // don't forget to stat for the file before returning + stat(full_path_to_ldml_file, &filestat); + + if (!S_ISREG(filestat.st_mode)) { + g_debug("WDG: couldn't find ldml file %s", full_path_to_ldml_file); + g_free(full_path_to_ldml_file); + full_path_to_ldml_file=g_strdup(""); + } + return full_path_to_ldml_file; +} + void kmfl_get_keyboard_info(KInputMethod * im) { char buf[1024]; KMSI * p_kmsi; - + im->keyboard_name = g_strdup(kmfl_keyboard_name(im->keyboard_number)); p_kmsi = kmfl_make_keyboard_instance(NULL); kmfl_attach_keyboard(p_kmsi, im->keyboard_number); @@ -176,16 +211,25 @@ void kmfl_get_keyboard_info(KInputMethod * im) else im->keyboard_license=g_strdup(""); + *buf='\0'; + kmfl_get_header(p_kmsi,SS_VISUALKEYBOARD,buf,sizeof(buf) - 1); + im->keyboard_visualkeyboard=g_strdup(buf); + + *buf='\0'; + kmfl_get_header(p_kmsi,SS_KEYBOARDVERSION,buf,sizeof(buf) - 1); + im->keyboard_keyboardversion=g_strdup(buf); + *buf='\0'; kmfl_get_header(p_kmsi,SS_LAYOUT,buf,sizeof(buf) - 1); if (*buf != '\0') im->keyboard_layout=g_strdup(buf); else im->keyboard_layout=g_strdup("us"); - + kmfl_detach_keyboard(p_kmsi); kmfl_delete_keyboard_instance(p_kmsi); im->keyboard_icon_filename = kmfl_get_icon_file(im); + im->keyboard_ldmlfile = kmfl_get_ldml_file(im); } void kmfl_free_keyboard_info(KInputMethod * im) @@ -200,6 +244,9 @@ void kmfl_free_keyboard_info(KInputMethod * im) g_free(im->keyboard_icon_filename); g_free(im->keyboard_layout); g_free(im->keyboard_license); + g_free(im->keyboard_visualkeyboard); + g_free(im->keyboard_keyboardversion); + g_free(im->keyboard_ldmlfile); } diff --git a/linux/ibus-kmfl/src/kmflutil.h b/linux/ibus-kmfl/src/kmflutil.h index 3937aa8aa2..1358d48b8e 100644 --- a/linux/ibus-kmfl/src/kmflutil.h +++ b/linux/ibus-kmfl/src/kmflutil.h @@ -83,6 +83,9 @@ typedef struct gchar * keyboard_description; gchar * keyboard_layout; gchar * keyboard_license; + gchar * keyboard_visualkeyboard; + gchar * keyboard_keyboardversion; + gchar * keyboard_ldmlfile; } KInputMethod; diff --git a/linux/ibus-kmfl/src/main.c b/linux/ibus-kmfl/src/main.c index 106ebe1f05..273423b825 100644 --- a/linux/ibus-kmfl/src/main.c +++ b/linux/ibus-kmfl/src/main.c @@ -27,6 +27,7 @@ #include #include "kmflutil.h" #include "engine.h" +#include "keyman-service.h" static IBusBus *bus = NULL; static IBusFactory *factory = NULL; @@ -50,6 +51,9 @@ ibus_disconnected_cb (IBusBus *bus, gpointer user_data) { g_debug ("bus disconnected"); + KeymanService *service = km_service_get_default(); + g_clear_object(&service); + ibus_quit (); } @@ -88,6 +92,7 @@ start_component (void) } g_object_unref (component); + km_service_get_default(); // initialise dbus service ibus_main (); } diff --git a/linux/libkmfl/src/kmfl_interpreter.c b/linux/libkmfl/src/kmfl_interpreter.c index 57e8aa69f0..a881f90bec 100644 --- a/linux/libkmfl/src/kmfl_interpreter.c +++ b/linux/libkmfl/src/kmfl_interpreter.c @@ -80,7 +80,6 @@ int kmfl_interpret(KMSI *p_kmsi, UINT key, UINT state) XKEYBOARD *p_kbd; XGROUP *p_group1; ITEM keysym; - int matched; p_kmsi->noutput_queue=0; @@ -250,7 +249,7 @@ int match_rule(KMSI *p_kmsi, XRULE *rp, ITEM *any_index, int usekeys) { UINT k, m, n, nmax, rulelen, nhistory, index; - ITEM *pr, *ph, *ps, mask; + ITEM *pr, *ph, *ps; rulelen = rp->ilen; pr = p_kmsi->strings+rp->lhs; @@ -351,7 +350,7 @@ int process_rule(KMSI *p_kmsi, XRULE *rp, ITEM *any_index, int usekeys) XGROUP *gp; UINT i, k, m, n, nout, itp, index; ITEM *p, *pr, *ps, output[MAX_OUTPUT+1], history[MAX_HISTORY], *it; - int erase, result, retCode=1, nhistory; + int erase, retCode=1, nhistory; DBGMSG(1, "DAR - libkmfl - process_rule\n"); pr = p_kmsi->strings+rp->rhs; // Pointer to start of output rule @@ -739,7 +738,7 @@ int kmfl_get_header(KMSI *p_kmsi,int hdrID,char *buf,int buflen) if(!p_kmsi) return -1; - if(hdrID < 0 || hdrID > SS_AUTHOR) return -2; + if(hdrID < 0 || hdrID > SS_KEYBOARDVERSION) return -2; p32 = (UTF32 *)store_content(p_kmsi,hdrID); p8 = (UTF8 *)buf; diff --git a/linux/libkmfl/src/kmfl_load_keyboard.c b/linux/libkmfl/src/kmfl_load_keyboard.c index 5656c573f6..0d29526677 100644 --- a/linux/libkmfl/src/kmfl_load_keyboard.c +++ b/linux/libkmfl/src/kmfl_load_keyboard.c @@ -461,7 +461,7 @@ const char *kmfl_icon_file(int keyboard_number) if(stores[SS_BITMAP].len >= 0) { p32 = strings + stores[SS_BITMAP].items; - p8 = icon_name; + p8 = (UTF8 *)icon_name; IConvertUTF32toUTF8((const UTF32**)&p32,p32+stores[SS_BITMAP].len,&p8,p8+255); *p8 = 0; }