/* * 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 */ #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 *eek_keyboard; static void on_monitor_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[] = { {"MonitorKeyEvent", NULL, N_("Monitor Key Typing"), NULL, NULL, G_CALLBACK(on_monitor_key_event_toggled), FALSE} }; static void on_monitor_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"); /* FIXME: currently keyboard must be finalized before actor. */ g_object_unref (eek_keyboard); if (actor) clutter_container_remove_actor (CLUTTER_CONTAINER(stage), actor); eek_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); } clutter_actor_show (stage); /* workaround for clutter-gtk (<= 0.10.2) */ eek_keyboard = create_keyboard (stage, layout, CSW, CSH); if (!eek_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; }