From 4b7f244062841ed33c3aa4641b9a096c996b049e Mon Sep 17 00:00:00 2001 From: Daiki Ueno Date: Fri, 11 Jun 2010 17:56:32 +0900 Subject: [PATCH] Add a standalone application. --- Makefile.am | 2 +- configure.ac | 5 + eek/eek-keyboard.c | 16 ++ eek/eek-layout.c | 34 ++- eek/eek-layout.h | 18 +- eek/eek-xkb-layout.c | 52 +++- eek/eek-xkl-layout.c | 72 +++++- eek/eek-xkl-layout.h | 8 +- examples/eek-clutter-xkb-test.c | 11 +- src/Makefile.am | 21 ++ src/eekboard.c | 416 ++++++++++++++++++++++++++++++++ 11 files changed, 632 insertions(+), 23 deletions(-) create mode 100644 src/Makefile.am create mode 100644 src/eekboard.c diff --git a/Makefile.am b/Makefile.am index 85f73808..823eef73 100644 --- a/Makefile.am +++ b/Makefile.am @@ -17,4 +17,4 @@ # 02110-1301 USA ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = eek examples tests docs +SUBDIRS = eek src examples tests docs diff --git a/configure.ac b/configure.ac index eb6910d3..8e3a9645 100644 --- a/configure.ac +++ b/configure.ac @@ -34,12 +34,16 @@ PKG_CHECK_MODULES([PANGO], [pango], , [AC_MSG_ERROR([Pango not found])]) PKG_CHECK_MODULES([CLUTTER], [clutter-1.0], , [AC_MSG_ERROR([Clutter not found])]) +PKG_CHECK_MODULES([CLUTTER_GTK], [clutter-gtk-0.10], , + [AC_MSG_ERROR([clutter-gtk not found])]) PKG_CHECK_MODULES([GTK2], [gtk+-2.0 gdk-2.0], , [AC_MSG_ERROR([GTK2 not found])]) PKG_CHECK_MODULES([XKB], [x11], , [AC_MSG_ERROR([XKB support not found])]) PKG_CHECK_MODULES([LIBXKLAVIER], [libxklavier x11], , [AC_MSG_ERROR([Libxklavier not found])]) +PKG_CHECK_MODULES([LIBFAKEKEY], [libfakekey], , + [AC_MSG_ERROR([libfakekey not found])]) GTK_DOC_CHECK([1.14],[--flavour no-tmpl]) @@ -51,6 +55,7 @@ AC_CHECK_PROGS([PYTHON], [python]) AC_CONFIG_HEADERS([eek/config.h]) AC_CONFIG_FILES([Makefile eek/Makefile +src/Makefile examples/Makefile tests/Makefile docs/Makefile diff --git a/eek/eek-keyboard.c b/eek/eek-keyboard.c index 7f5a3d32..6eabc720 100644 --- a/eek/eek-keyboard.c +++ b/eek/eek-keyboard.c @@ -154,6 +154,18 @@ eek_keyboard_real_create_section (EekKeyboard *self) return section; } +static void +on_group_changed (EekLayout *layout, + gint new_group, + gpointer user_data) +{ + EekKeyboard *keyboard = user_data; + gint group, level; + + eek_keyboard_get_keysym_index (keyboard, &group, &level); + eek_keyboard_set_keysym_index (keyboard, new_group, level); +} + static void eek_keyboard_real_set_layout (EekKeyboard *self, EekLayout *layout) @@ -163,6 +175,8 @@ eek_keyboard_real_set_layout (EekKeyboard *self, g_return_if_fail (EEK_IS_LAYOUT(layout)); priv->layout = layout; g_object_ref_sink (priv->layout); + g_signal_connect (priv->layout, "group_changed", + G_CALLBACK(on_group_changed), self); } static void @@ -173,6 +187,8 @@ eek_keyboard_real_realize (EekKeyboard *self) g_return_if_fail (priv->layout); g_return_if_fail (!priv->is_realized); EEK_LAYOUT_GET_IFACE(priv->layout)->apply (priv->layout, self); + /* apply the initial group setting */ + on_group_changed (priv->layout, eek_layout_get_group (priv->layout), self); priv->is_realized = TRUE; } diff --git a/eek/eek-layout.c b/eek/eek-layout.c index f09f08f9..568599df 100644 --- a/eek/eek-layout.c +++ b/eek/eek-layout.c @@ -33,13 +33,39 @@ #include "eek-layout.h" #include "eek-keyboard.h" +enum { + GROUP_CHANGED, + CHANGED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0, }; + static void eek_layout_base_init (gpointer gobject_class) { static gboolean is_initialized = FALSE; if (!is_initialized) { - /* TODO: signals */ + signals[GROUP_CHANGED] = + g_signal_new ("group-changed", + G_TYPE_FROM_INTERFACE(gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(EekLayoutIface, group_changed), + NULL, + NULL, + g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, 1, + G_TYPE_INT); + signals[CHANGED] = + g_signal_new ("changed", + G_TYPE_FROM_INTERFACE(gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(EekLayoutIface, changed), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); is_initialized = TRUE; } } @@ -69,3 +95,9 @@ eek_layout_apply (EekLayout *layout, EEK_LAYOUT_GET_IFACE(layout)->apply (layout, keyboard); } +gint +eek_layout_get_group (EekLayout *layout) +{ + g_return_val_if_fail (EEK_IS_LAYOUT(layout), -1); + return EEK_LAYOUT_GET_IFACE(layout)->get_group (layout); +} diff --git a/eek/eek-layout.h b/eek/eek-layout.h index 11f645dd..63a2a8ab 100644 --- a/eek/eek-layout.h +++ b/eek/eek-layout.h @@ -39,13 +39,21 @@ struct _EekLayoutIface /*< private >*/ GTypeInterface parent_iface; - void (*apply) (EekLayout *self, - EekKeyboard *keyboard); + /*< public >*/ + void (* apply) (EekLayout *self, + EekKeyboard *keyboard); + gint (* get_group) (EekLayout *self); + + /* signals */ + void (* group_changed) (EekLayout *self, + gint group); + void (* changed) (EekLayout *self); }; -GType eek_layout_get_type (void) G_GNUC_CONST; -void eek_layout_apply (EekLayout *layout, - EekKeyboard *keyboard); +GType eek_layout_get_type (void) G_GNUC_CONST; +void eek_layout_apply (EekLayout *layout, + EekKeyboard *keyboard); +gint eek_layout_get_group (EekLayout *layout); G_END_DECLS #endif /* EEK_LAYOUT_H */ diff --git a/eek/eek-xkb-layout.c b/eek/eek-xkb-layout.c index c56093a6..5d7b1309 100644 --- a/eek/eek-xkb-layout.c +++ b/eek/eek-xkb-layout.c @@ -27,14 +27,15 @@ * elements using XKB. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - #include #include #include #include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + #include "eek-xkb-layout.h" #include "eek-keyboard.h" #include "eek-section.h" @@ -308,19 +309,41 @@ eek_xkb_layout_real_apply (EekLayout *layout, EekKeyboard *keyboard) create_keyboard (EEK_XKB_LAYOUT(layout), keyboard); } +static gint +compare_component_names (gchar *name0, gchar *name1) +{ + if (name0 && name1) + return g_strcmp0 (name0, name1); + else if (!name0 && name1) + return -1; + else if (name0 && !name1) + return 1; + else + return 0; +} + static void eek_xkb_layout_real_set_names (EekXkbLayout *self, XkbComponentNamesRec *names) { EekXkbLayoutPrivate *priv = EEK_XKB_LAYOUT_GET_PRIVATE (self); + gboolean is_changed; g_return_if_fail (priv); + is_changed = + compare_component_names (names->keycodes, priv->names.keycodes) != 0; g_free (priv->names.keycodes); priv->names.keycodes = g_strdup (names->keycodes); + is_changed = + compare_component_names (names->geometry, priv->names.geometry) != 0; g_free (priv->names.geometry); priv->names.geometry = g_strdup (names->geometry); + is_changed = + compare_component_names (names->symbols, priv->names.symbols) != 0; g_free (priv->names.symbols); priv->names.symbols = g_strdup (names->symbols); get_keyboard (self); + if (is_changed) + g_signal_emit_by_name (self, "changed"); } static void @@ -574,11 +597,18 @@ void eek_xkb_layout_set_keycodes (EekXkbLayout *layout, const gchar *keycodes) { EekXkbLayoutPrivate *priv = EEK_XKB_LAYOUT_GET_PRIVATE (layout); + gboolean is_changed = TRUE; g_return_if_fail (priv); + if (keycodes && priv->names.keycodes) + is_changed = g_strcmp0 (keycodes, priv->names.keycodes) != 0; + else if (keycodes == NULL && priv->names.keycodes == NULL) + is_changed = FALSE; g_free (priv->names.keycodes); priv->names.keycodes = g_strdup (keycodes); get_keyboard (layout); + if (is_changed) + g_signal_emit_by_name (layout, "changed"); } /** @@ -592,11 +622,18 @@ void eek_xkb_layout_set_geometry (EekXkbLayout *layout, const gchar *geometry) { EekXkbLayoutPrivate *priv = EEK_XKB_LAYOUT_GET_PRIVATE (layout); + gboolean is_changed = TRUE; g_return_if_fail (priv); + if (geometry && priv->names.geometry) + is_changed = g_strcmp0 (geometry, priv->names.geometry) != 0; + else if (geometry == NULL && priv->names.geometry == NULL) + is_changed = FALSE; g_free (priv->names.geometry); priv->names.geometry = g_strdup (geometry); get_keyboard (layout); + if (is_changed) + g_signal_emit_by_name (layout, "changed"); } /** @@ -610,11 +647,18 @@ void eek_xkb_layout_set_symbols (EekXkbLayout *layout, const gchar *symbols) { EekXkbLayoutPrivate *priv = EEK_XKB_LAYOUT_GET_PRIVATE (layout); + gboolean is_changed = TRUE; g_return_if_fail (priv); + if (symbols && priv->names.symbols) + is_changed = g_strcmp0 (symbols, priv->names.symbols) != 0; + else if (symbols == NULL && priv->names.symbols == NULL) + is_changed = FALSE; g_free (priv->names.symbols); priv->names.symbols = g_strdup (symbols); get_keyboard (layout); + if (is_changed) + g_signal_emit_by_name (layout, "changed"); } /** diff --git a/eek/eek-xkl-layout.c b/eek/eek-xkl-layout.c index 7bf91969..f0e3dcd6 100644 --- a/eek/eek-xkl-layout.c +++ b/eek/eek-xkl-layout.c @@ -38,7 +38,12 @@ #define noKBDRAW_DEBUG -G_DEFINE_TYPE (EekXklLayout, eek_xkl_layout, EEK_TYPE_XKB_LAYOUT); +static void eek_layout_iface_init (EekLayoutIface *iface); +static EekLayoutIface *parent_layout_iface; + +G_DEFINE_TYPE_WITH_CODE (EekXklLayout, eek_xkl_layout, EEK_TYPE_XKB_LAYOUT, + G_IMPLEMENT_INTERFACE(EEK_TYPE_LAYOUT, + eek_layout_iface_init)); #define EEK_XKL_LAYOUT_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EEK_TYPE_XKL_LAYOUT, EekXklLayoutPrivate)) @@ -72,6 +77,26 @@ extern void xkl_xkb_config_native_cleanup (XklEngine * engine, static void get_xkb_component_names (EekXklLayout *layout); +static gint +eek_xkl_layout_real_get_group (EekLayout *self) +{ + EekXklLayoutPrivate *priv = EEK_XKL_LAYOUT_GET_PRIVATE (self); + XklState *state; + + state = xkl_engine_get_current_state (priv->engine); + g_return_val_if_fail (state, -1); + return state->group; +} + +static void +eek_layout_iface_init (EekLayoutIface *iface) +{ + parent_layout_iface = g_type_interface_peek_parent (iface); + if (!parent_layout_iface) + parent_layout_iface = g_type_default_interface_peek (EEK_TYPE_LAYOUT); + iface->get_group = eek_xkl_layout_real_get_group; +} + static void eek_xkl_layout_finalize (GObject *object) { @@ -145,12 +170,12 @@ eek_xkl_layout_get_property (GObject *object, static void eek_xkl_layout_class_init (EekXklLayoutClass *klass) { - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GParamSpec *pspec; + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; g_type_class_add_private (gobject_class, sizeof (EekXklLayoutPrivate)); - gobject_class->finalize = eek_xkl_layout_finalize; + gobject_class->finalize = eek_xkl_layout_finalize; gobject_class->set_property = eek_xkl_layout_set_property; gobject_class->get_property = eek_xkl_layout_get_property; @@ -176,6 +201,19 @@ eek_xkl_layout_class_init (EekXklLayoutClass *klass) g_object_class_install_property (gobject_class, PROP_OPTIONS, pspec); } +static void +on_state_changed (XklEngine *xklengine, + XklEngineStateChange type, + gint value, + gboolean restore, + gpointer user_data) +{ + EekLayout *layout = user_data; + + if (type == GROUP_CHANGED) + g_signal_emit_by_name (layout, "group_changed", value); +} + static void eek_xkl_layout_init (EekXklLayout *self) { @@ -189,18 +227,37 @@ eek_xkl_layout_init (EekXklLayout *self) g_return_if_fail (display); priv->engine = xkl_engine_get_instance (display); + g_signal_connect (priv->engine, "X-state-changed", + G_CALLBACK(on_state_changed), self); xkl_config_rec_get_from_server (&priv->config, priv->engine); get_xkb_component_names (self); + xkl_engine_start_listen (priv->engine, XKLL_TRACK_KEYBOARD_STATE); } EekLayout * -eek_xkl_layout_new (gchar **layouts, - gchar **variants, - gchar **options) +eek_xkl_layout_new (void) { return g_object_new (EEK_TYPE_XKL_LAYOUT, NULL); } +void +eek_xkl_layout_set_config (EekXklLayout *layout, + gchar **layouts, + gchar **variants, + gchar **options) +{ + EekXklLayoutPrivate *priv = EEK_XKL_LAYOUT_GET_PRIVATE (layout); + + g_return_if_fail (priv); + g_strfreev (priv->config.layouts); + priv->config.layouts = g_strdupv (layouts); + g_strfreev (priv->config.variants); + priv->config.variants = g_strdupv (variants); + g_strfreev (priv->config.options); + priv->config.options = g_strdupv (options); + get_xkb_component_names (layout); +} + void eek_xkl_layout_set_layouts (EekXklLayout *layout, gchar **layouts) { @@ -268,6 +325,7 @@ get_xkb_component_names (EekXklLayout *layout) XkbComponentNamesRec names; if (xkl_xkb_config_native_prepare (priv->engine, &priv->config, &names)) { + g_debug ("symbols = \"%s\"", names.symbols); EEK_XKB_LAYOUT_GET_CLASS (layout)-> set_names (EEK_XKB_LAYOUT(layout), &names); xkl_xkb_config_native_cleanup (priv->engine, &names); diff --git a/eek/eek-xkl-layout.h b/eek/eek-xkl-layout.h index 18fab763..f251631e 100644 --- a/eek/eek-xkl-layout.h +++ b/eek/eek-xkl-layout.h @@ -51,7 +51,10 @@ struct _EekXklLayoutClass GType eek_xkl_layout_get_type (void) G_GNUC_CONST; -EekLayout *eek_xkl_layout_new (gchar **layouts, +EekLayout *eek_xkl_layout_new (void); + +void eek_xkl_layout_set_config (EekXklLayout *layout, + gchar **layouts, gchar **variants, gchar **options); @@ -63,7 +66,8 @@ void eek_xkl_layout_set_options (EekXklLayout *layout, gchar **options); gchar **eek_xkl_layout_get_layouts (EekXklLayout *layout); -gchar **eek_xkl_layout_get_variants (EekXklLayout *layout); +gchar **eek_xkl_layout_get_variants + (EekXklLayout *layout); gchar **eek_xkl_layout_get_options (EekXklLayout *layout); G_END_DECLS diff --git a/examples/eek-clutter-xkb-test.c b/examples/eek-clutter-xkb-test.c index b8f38c63..634ee974 100644 --- a/examples/eek-clutter-xkb-test.c +++ b/examples/eek-clutter-xkb-test.c @@ -1,9 +1,14 @@ -#include "eek/eek-clutter.h" -#include "eek/eek-xkb.h" #include #include -#include #include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include "eek/eek-clutter.h" +#include "eek/eek-xkb.h" #define CSW 640 #define CSH 480 diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 00000000..148f1c69 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,21 @@ +# Copyright (C) 2010 Daiki Ueno +# Copyright (C) 2010 Red Hat, Inc. + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser 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 +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser 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 + +bin_PROGRAMS = eekboard +eekboard_CFLAGS = -I$(top_srcdir) $(GOBJECT2_CFLAGS) $(CLUTTER_CFLAGS) $(CLUTTER_GTK_CFLAGS) $(GTK2_CFLAGS) $(XKB_CFLAGS) $(LIBXKLAVIER_CFLAGS) $(LIBFAKEKEY_CFLAGS) +eekboard_LDFLAGS = $(top_builddir)/eek/libeek.la $(top_builddir)/eek/libeek-xkl.la $(top_builddir)/eek/libeek-clutter.la $(GOBJECT2_LIBS) $(CLUTTER_LIBS) $(CLUTTER_GTK_LIBS) $(GTK2_CFLAGS) $(XKB_LIBS) $(LIBXKLAVIER_LIBS) $(LIBFAKEKEY_LIBS) diff --git a/src/eekboard.c b/src/eekboard.c new file mode 100644 index 00000000..9dc586df --- /dev/null +++ b/src/eekboard.c @@ -0,0 +1,416 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include "eek/eek-clutter.h" +#include "eek/eek-xkl.h" + +#define CSW 640 +#define CSH 480 + +static gchar *window_id = NULL; +gfloat stage_width, stage_height; +Display *display; +FakeKey *fakekey; +Window target; + +EekLayout *layout; +EekKeyboard *keyboard; + +static void on_capture_key_event_toggled (GtkToggleAction *action, + GtkWidget *window); + +static const GOptionEntry options[] = { + {"window-id", '\0', 0, G_OPTION_ARG_STRING, &window_id, + "the target window ID; use xwininfo to obtain the value", NULL}, + {NULL}, +}; + +static const GtkActionEntry action_entry[] = { + {"FileMenu", NULL, N_("_File")}, + {"KeyboardMenu", NULL, N_("_Keyboard")}, + {"HelpMenu", NULL, N_("_Help")}, + {"Quit", GTK_STOCK_QUIT, NULL, NULL, NULL, G_CALLBACK (gtk_main_quit)}, + {"SetLayout", NULL, N_("Set Layout"), NULL, NULL, NULL}, + {"About", GTK_STOCK_ABOUT, NULL, NULL, NULL, NULL} +}; + +static const GtkToggleActionEntry toggle_action_entry[] = { + {"CaptureKeyEvent", NULL, N_("Capture Key Event"), NULL, NULL, + G_CALLBACK(on_capture_key_event_toggled), FALSE} +}; + +static void +on_capture_key_event_toggled (GtkToggleAction *action, + GtkWidget *window) +{ + gboolean active; + + active = gtk_toggle_action_get_active (action); + g_object_set (G_OBJECT(window), "accept_focus", active, NULL); + g_object_set (G_OBJECT(window), "can_focus", active, NULL); +} + +struct _EekBoardLayoutVariant { + gchar *layout; + gchar *variant; +}; +typedef struct _EekBoardLayoutVariant EekBoardLayoutVariant; + +static void +on_key_pressed (EekKeyboard *keyboard, + EekKey *key) +{ + fakekey_press_keysym (fakekey, eek_key_get_keysym (key), 0); +} + +static void +on_key_released (EekKeyboard *keyboard, + EekKey *key) +{ + fakekey_release (fakekey); +} + +static void +on_activate (GtkAction *action, gpointer user_data) +{ + EekBoardLayoutVariant *config = user_data; + gchar *layouts[2], *variants[2], **vp = NULL; + + layouts[0] = config->layout; + layouts[1] = NULL; + if (config->variant) { + variants[0] = config->variant; + variants[1] = NULL; + vp = variants; + } + eek_xkl_layout_set_config (EEK_XKL_LAYOUT(layout), layouts, vp, NULL); +} + +static EekKeyboard * +create_keyboard (ClutterActor *stage, + EekLayout *layout, + gfloat width, + gfloat height) +{ + EekKeyboard *keyboard; + ClutterActor *actor; + + keyboard = eek_clutter_keyboard_new (width, height); + g_signal_connect (keyboard, "key-pressed", + G_CALLBACK(on_key_pressed), NULL); + g_signal_connect (keyboard, "key-released", + G_CALLBACK(on_key_released), NULL); + eek_keyboard_set_layout (keyboard, layout); + actor = eek_clutter_keyboard_get_actor (EEK_CLUTTER_KEYBOARD(keyboard)); + clutter_actor_set_name (actor, "keyboard"); + clutter_actor_get_size (actor, &width, &height); + clutter_container_add_actor (CLUTTER_CONTAINER(stage), actor); + clutter_actor_set_size (stage, width, height); + return keyboard; +} + +/* FIXME: EekKeyboard should handle relayout by itself. */ +static void +on_changed (EekLayout *layout, gpointer user_data) +{ + ClutterActor *stage = user_data, *actor; + gfloat width, height; + + clutter_actor_get_size (stage, &width, &height); + actor = clutter_container_find_child_by_name (stage, "keyboard"); + if (actor) + clutter_container_remove_actor (CLUTTER_CONTAINER(stage), actor); + g_object_unref (keyboard); + create_keyboard (stage, layout, width, height); +} + +static const char ui_description[] = + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; + +#define SET_LAYOUT_UI_PATH "/MainMenu/KeyboardMenu/SetLayout/LayoutsPH" + +struct _EekBoardAddLayoutData { + GtkUIManager *ui_manager; + GtkActionGroup *action_group; + guint merge_id; +}; +typedef struct _EekBoardAddLayoutData EekBoardAddLayoutData; + +struct _EekBoardAddVariantData { + gchar *name; + gchar *description; +}; +typedef struct _EekBoardAddVariantData EekBoardAddVariantData; + +static void +variant_callback (XklConfigRegistry *registry, + XklConfigItem *item, + gpointer user_data) +{ + EekBoardAddVariantData *variant_data; + GSList **head = user_data; + + variant_data = g_slice_new (EekBoardAddVariantData); + variant_data->name = g_strdup (item->name); + variant_data->description = g_strdup (item->description); + *head = g_slist_prepend (*head, variant_data); +} + +static void +layout_callback (XklConfigRegistry *registry, + XklConfigItem *item, + gpointer user_data) +{ + GtkAction *action; + EekBoardAddLayoutData *layout_data = user_data; + GSList *variants = NULL; + char layout_action_name[128], variant_action_name[128]; + EekBoardLayoutVariant *data; + + g_snprintf (layout_action_name, sizeof (layout_action_name), + "SetLayout%s", item->name); + action = gtk_action_new (layout_action_name, item->description, NULL, NULL); + gtk_action_group_add_action (layout_data->action_group, action); + + xkl_config_registry_foreach_layout_variant (registry, + item->name, + variant_callback, + &variants); + + if (!variants) { + data = g_slice_new (EekBoardLayoutVariant); + data->layout = g_strdup (item->name); + data->variant = NULL; + g_signal_connect (action, "activate", G_CALLBACK (on_activate), + data); + + g_object_unref (action); + gtk_ui_manager_add_ui (layout_data->ui_manager, layout_data->merge_id, + SET_LAYOUT_UI_PATH, + layout_action_name, layout_action_name, + GTK_UI_MANAGER_MENUITEM, FALSE); + } else { + char layout_path[128]; + GSList *head; + + g_object_unref (action); + gtk_ui_manager_add_ui (layout_data->ui_manager, + layout_data->merge_id, + SET_LAYOUT_UI_PATH, + layout_action_name, layout_action_name, + GTK_UI_MANAGER_MENU, FALSE); + g_snprintf (layout_path, sizeof (layout_path), + SET_LAYOUT_UI_PATH "/%s", layout_action_name); + + for (head = variants; head; head = head->next) { + EekBoardAddVariantData *variant_data = head->data; + + g_snprintf (variant_action_name, sizeof (variant_action_name), + "SetVariant%s%s", item->name, variant_data->name); + action = gtk_action_new (variant_action_name, + variant_data->description, + NULL, + NULL); + + data = g_slice_new (EekBoardLayoutVariant); + data->layout = g_strdup (item->name); + data->variant = g_strdup (variant_data->name); + g_signal_connect (action, "activate", G_CALLBACK (on_activate), + data); + + gtk_action_group_add_action (layout_data->action_group, action); + g_object_unref (action); + + gtk_ui_manager_add_ui (layout_data->ui_manager, + layout_data->merge_id, + layout_path, + variant_action_name, variant_action_name, + GTK_UI_MANAGER_MENUITEM, FALSE); + g_free (variant_data->name); + g_free (variant_data->description); + g_slice_free (EekBoardAddVariantData, variant_data); + } + g_slist_free (variants); + } +} + +static void +create_menus (GtkWidget *window, GtkUIManager * ui_manager) +{ + GtkActionGroup *action_group; + + action_group = gtk_action_group_new ("MenuActions"); + + gtk_action_group_add_actions (action_group, action_entry, + G_N_ELEMENTS (action_entry), window); + gtk_action_group_add_toggle_actions (action_group, toggle_action_entry, + G_N_ELEMENTS (toggle_action_entry), window); + + gtk_ui_manager_insert_action_group (ui_manager, action_group, 0); + gtk_ui_manager_add_ui_from_string (ui_manager, ui_description, -1, NULL); + + EekBoardAddLayoutData data; + + data.action_group = gtk_action_group_new ("Layouts"); + gtk_ui_manager_insert_action_group (ui_manager, data.action_group, -1); + data.merge_id = gtk_ui_manager_new_merge_id (ui_manager); + + XklEngine *engine; + XklConfigRegistry *registry; + + engine = xkl_engine_get_instance (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ())); + registry = xkl_config_registry_get_instance (engine); + xkl_config_registry_load (registry, FALSE); + data.ui_manager = ui_manager; + data.action_group = action_group; + xkl_config_registry_foreach_layout (registry, layout_callback, &data); +} + +static void +on_resize (GObject *object, + GParamSpec *param_spec, + gpointer user_data) +{ + GValue value = {0}; + gfloat width, height, scale; + ClutterActor *stage = CLUTTER_ACTOR(object); + + g_object_get (G_OBJECT(stage), "width", &width, NULL); + g_object_get (G_OBJECT(stage), "height", &height, NULL); + + g_value_init (&value, G_TYPE_DOUBLE); + + scale = width > height ? width / stage_width : width / stage_height; + + g_value_set_double (&value, scale); + g_object_set_property (G_OBJECT (stage), + "scale-x", + &value); + + g_value_set_double (&value, scale); + g_object_set_property (G_OBJECT (stage), + "scale-y", + &value); +} + +int +main (int argc, char *argv[]) +{ + ClutterActor *stage; + ClutterColor stage_color = { 0xff, 0xff, 0xff, 0xff }; + GOptionContext *context; + GtkWidget *menubar, *embed, *vbox, *window; + GtkUIManager *ui_manager; + + context = g_option_context_new ("eekboard"); + g_option_context_add_main_entries (context, options, NULL); + g_option_context_parse (context, &argc, &argv, NULL); + g_option_context_free (context); + + clutter_init (&argc, &argv); + gtk_init (&argc, &argv); + display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); + if (!display) { + fprintf (stderr, "Can't open display\n"); + exit (1); + } + if (window_id) { + if (strncmp (window_id, "0x", 2) == 0) + target = strtol (window_id[2], NULL, 16); + else + target = strtol (window_id, NULL, 10); + } else { + int revert_to; + XGetInputFocus (display, &target, &revert_to); + } + fakekey = fakekey_init (display); + if (!fakekey) { + fprintf (stderr, "Can't init fakekey\n"); + exit (1); + } + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_widget_set_can_focus (window, FALSE); + g_object_set (G_OBJECT(window), "accept_focus", FALSE, NULL); + gtk_window_set_title (GTK_WINDOW(window), "Keyboard"); + g_signal_connect (G_OBJECT (window), "destroy", + G_CALLBACK (gtk_main_quit), NULL); + + vbox = gtk_vbox_new (FALSE, 0); + ui_manager = gtk_ui_manager_new (); + create_menus (window, ui_manager); + menubar = gtk_ui_manager_get_widget (ui_manager, "/MainMenu"); + gtk_box_pack_start (GTK_BOX (vbox), menubar, FALSE, FALSE, 0); + + embed = gtk_clutter_embed_new (); + gtk_widget_set_can_focus (embed, TRUE); + gtk_container_add (GTK_CONTAINER(vbox), embed); + gtk_container_add (GTK_CONTAINER(window), vbox); + + stage = gtk_clutter_embed_get_stage (GTK_CLUTTER_EMBED(embed)); + clutter_stage_set_color (CLUTTER_STAGE(stage), &stage_color); + clutter_stage_set_user_resizable (CLUTTER_STAGE (stage), TRUE); + + layout = eek_xkl_layout_new (); + if (!layout) { + fprintf (stderr, "Failed to create layout\n"); + exit (1); + } + + keyboard = create_keyboard (stage, layout, CSW, CSH); + if (!keyboard) { + g_object_unref (layout); + fprintf (stderr, "Failed to create keyboard\n"); + exit (1); + } + clutter_actor_get_size (stage, &stage_width, &stage_height); + clutter_actor_show_all (stage); + + g_signal_connect (layout, "changed", + G_CALLBACK(on_changed), stage); + + g_signal_connect (stage, + "notify::width", + G_CALLBACK (on_resize), + NULL); + + g_signal_connect (stage, + "notify::height", + G_CALLBACK (on_resize), + NULL); + + gtk_widget_set_size_request (embed, stage_width, stage_height); + gtk_widget_show_all (window); + gtk_widget_set_size_request (embed, -1, -1); + + gtk_main (); + + return 0; +}