/* * Copyright (C) 2000 Red Hat Software * Copyright (C) 2003 Motonobu Ichimura * Copyright 2003 Sun Microsystems Inc. * * This 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., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Authors: Hidetoshi Tajima */ #include #include #include #include #include "iiimcf.h" #include "gtkimcontextiiim.h" #include "imswitcher.h" #include "IIIMGdkEventKey.h" #include #include #include /* #define DEBUG */ #ifdef DEBUG #define DEBUG_DO(x) (x) #else #define DEBUG_DO(x) #endif struct _SwitcherInfo { GdkWindow *switcher; GdkAtom selection_atom; GdkAtom set_current_input_language_atom; GdkAtom set_current_client_atom; GdkAtom set_status_text_atom; GdkAtom set_input_language_list_atom; GdkAtom set_language_engine_list_atom; GdkAtom set_conversion_mode_atom; GdkAtom set_hotkey_atom; /* When switcher is NULL while switcher_x_window isn't and works, it is due to gdk_selection_owner_get()'s bug. See bugzilla #126375. */ Window switcher_x_window; }; /* A listener window for input method switcher */ struct _SwitcherContext { GtkWidget *invisible; gulong destroy_handler_id; gulong property_handler_id; }; static GdkFilterReturn switcher_owner_filter (GdkXEvent *xev, GdkEvent *event, gpointer data); static gboolean filter_destroy_event (Display *d, Window w, XEvent *ev, gpointer data); gboolean im_info_switcher_new (GtkIIIMInfo *info) { GdkAtom selection = GDK_NONE; SwitcherInfo *sw_info; if (info == NULL) return FALSE; selection = gdk_atom_intern ("_IIIM_SWITCHER", FALSE); sw_info = im_info_get_switcher_info (info); if (sw_info == NULL) { sw_info = g_new0 (SwitcherInfo, 1); im_info_set_switcher_info (info, sw_info); } if (selection != GDK_NONE) sw_info->switcher = gdk_selection_owner_get (selection); sw_info->selection_atom = selection; if (sw_info->switcher) gdk_window_add_filter (sw_info->switcher, switcher_owner_filter, info); else { /* this could be due to bugzilla 126375, hence try to do Xlib directly. */ GdkScreen *screen; GdkDisplay *display; Atom x_atom; Window xwindow; screen = im_info_get_screen (info); if (screen == None) return FALSE; display = gdk_screen_get_display (screen); x_atom = gdk_x11_atom_to_xatom_for_display (display, selection); xwindow = XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display), x_atom); if (xwindow == None) { DEBUG_DO (g_message ("Unable to find input method switcher")); return FALSE; } sw_info->switcher_x_window = xwindow; _XRegisterFilterByType (GDK_DISPLAY_XDISPLAY (display), xwindow, DestroyNotify, DestroyNotify, filter_destroy_event, info); XSelectInput (GDK_DISPLAY_XDISPLAY (display), xwindow, StructureNotifyMask); } sw_info->set_current_input_language_atom = gdk_atom_intern ("_IIIM_SWITCHER_CURRENT_INPUT_LANGUAGE", FALSE); sw_info->set_current_client_atom = gdk_atom_intern ("_IIIM_SWITCHER_CURRENT_CLIENT", FALSE); sw_info->set_status_text_atom = gdk_atom_intern ("_IIIM_SWITCHER_STATUS_TEXT", FALSE); sw_info->set_input_language_list_atom = gdk_atom_intern ("_IIIM_SWITCHER_INPUT_LANGUAGE_LIST", FALSE); sw_info->set_language_engine_list_atom = gdk_atom_intern ("_IIIM_SWITCHER_LANGUAGE_ENGINE_LIST", FALSE); sw_info->set_conversion_mode_atom = gdk_atom_intern ("_IIIM_SWITCHER_SET_CONVERSION_MODE", FALSE); sw_info->set_hotkey_atom = gdk_atom_intern ("_IIIM_SWITCHER_SET_HOTKEY", FALSE); return TRUE; } static void destroy_switcher_window (GtkWidget *widget, GtkIMContextIIIM *context_iiim) { SwitcherContext *w = context_iiim->switcher_context; if (!w) return; gtk_widget_destroy (w->invisible); g_free (w); context_iiim->switcher_context = NULL; return; } static void property_notify_switcher_window (GtkWidget *widget, GdkEventProperty *ev, GtkIMContextIIIM *context_iiim) { GdkAtom type; guchar *data = NULL; gint format; gint length; SwitcherInfo *sw_info = im_info_get_switcher_info (context_iiim->iiim_info); if (context_iiim->context == NULL) return; if (ev->atom == sw_info->set_current_input_language_atom) { gdk_property_get (widget->window, ev->atom, ev->atom, 0, INT_MAX, FALSE, &type, &format, &length, &data); im_context_initialize_with_input_language (context_iiim, data); /* This callback to switcher is needed for ensuring switcher always shows correct client language indicator. The timing of focus-in event sometimes causes switcher's language indicator inconsistency problem without this. */ im_context_switcher_set_input_language (context_iiim, NULL); g_free (data); } if (ev->atom == sw_info->set_conversion_mode_atom) { gdk_property_get (widget->window, ev->atom, ev->atom, 0, INT_MAX, FALSE, &type, &format, &length, &data); im_context_change_conversion_mode (context_iiim, data); g_free (data); } return; } void im_context_switcher_set_status_text (GtkIMContextIIIM *context_iiim, gchar *utf8) { GtkIIIMInfo *info = context_iiim->iiim_info; SwitcherInfo *sw_info = im_info_get_switcher_info (info); if (sw_info == NULL) return; if (sw_info->switcher) { gdk_property_change (sw_info->switcher, sw_info->set_status_text_atom, sw_info->set_status_text_atom, 8, GDK_PROP_MODE_REPLACE, (unsigned char*)utf8, strlen (utf8)); } else if (sw_info->switcher_x_window) { GdkScreen *screen = im_info_get_screen (info); GdkDisplay *display = gdk_screen_get_display (screen); Atom x_atom = gdk_x11_atom_to_xatom_for_display (display, sw_info->set_status_text_atom); XChangeProperty (GDK_DISPLAY_XDISPLAY (display), sw_info->switcher_x_window, x_atom, x_atom, 8, PropModeReplace, (guchar *)utf8, strlen (utf8)); } } #ifdef HAS_IIIM_PROPERTIES extern gboolean is_sync_activation (); extern gint set_global_conv_mode (gint); #endif /* HAS_IIIM_PROPERTIES */ void im_context_switcher_set_conversion_mode (GtkIMContextIIIM *context_iiim) { GtkIIIMInfo *info = context_iiim->iiim_info; SwitcherInfo *sw_info = im_info_get_switcher_info (info); IIIMF_status st; gint conversion_mode = FALSE; long conversion_mode_long; if (sw_info == NULL) return; st = iiimcf_get_current_conversion_mode (context_iiim->context, &conversion_mode); conversion_mode_long = conversion_mode; if (sw_info->switcher) { gdk_property_change (sw_info->switcher, sw_info->set_conversion_mode_atom, sw_info->set_conversion_mode_atom, 32, GDK_PROP_MODE_REPLACE, (unsigned char*)&conversion_mode_long, 1); } else if (sw_info->switcher_x_window) { GdkScreen *screen = im_info_get_screen (info); GdkDisplay *display = gdk_screen_get_display (screen); Atom x_atom = gdk_x11_atom_to_xatom_for_display (display, sw_info->set_conversion_mode_atom); XChangeProperty (GDK_DISPLAY_XDISPLAY (display), sw_info->switcher_x_window, x_atom, x_atom, 32, PropModeReplace, (guchar *)&conversion_mode_long, 1); } if (is_sync_activation ()) { set_global_conv_mode (conversion_mode); } } /* change the hotkey property for the gimlet window */ void im_context_switcher_set_hotkey (GtkIMContextIIIM *context_iiim, char *hotkey) { GtkIIIMInfo *info = context_iiim->iiim_info; SwitcherInfo *sw_info = im_info_get_switcher_info (info); if (sw_info == NULL || hotkey == NULL) return; if (sw_info->switcher) { gdk_property_change (sw_info->switcher, sw_info->set_hotkey_atom, sw_info->set_hotkey_atom, 8, GDK_PROP_MODE_REPLACE, (unsigned char*)hotkey, strlen (hotkey)); } else if (sw_info->switcher_x_window) { GdkScreen *screen = im_info_get_screen (info); GdkDisplay *display = gdk_screen_get_display (screen); Atom x_atom = gdk_x11_atom_to_xatom_for_display (display, sw_info->set_hotkey_atom); XChangeProperty (GDK_DISPLAY_XDISPLAY (display), sw_info->switcher_x_window, x_atom, x_atom, 8, PropModeReplace, (guchar *)hotkey, strlen (hotkey)); } } void im_context_switcher_set_input_language (GtkIMContextIIIM *context_iiim, gchar *input_lang) { GtkIIIMInfo *info = context_iiim->iiim_info; SwitcherInfo *sw_info = im_info_get_switcher_info (info); if (sw_info == NULL) return; if (input_lang == NULL) input_lang = context_iiim->current_language; if (sw_info->switcher && input_lang) { gdk_property_change (sw_info->switcher, sw_info->set_current_input_language_atom, sw_info->set_current_input_language_atom, 8, GDK_PROP_MODE_REPLACE, (unsigned char*)input_lang, strlen (input_lang)); } else if (sw_info->switcher_x_window && input_lang) { GdkScreen *screen = im_info_get_screen (info); GdkDisplay *display = gdk_screen_get_display (screen); Atom x_atom = gdk_x11_atom_to_xatom_for_display (display, sw_info->set_current_input_language_atom); XChangeProperty (GDK_DISPLAY_XDISPLAY (display), sw_info->switcher_x_window, x_atom, x_atom, 8, PropModeReplace, (guchar *)input_lang, strlen (input_lang)); } } void im_context_switcher_set_language_engine_list (GtkIMContextIIIM *context_iiim, gchar *le_list) { GtkIIIMInfo *info = context_iiim->iiim_info; SwitcherInfo *sw_info = im_info_get_switcher_info (info); gsize len; if (sw_info == NULL) return; len = strlen (le_list); if (len == 0) return; if (sw_info->switcher) gdk_property_change (sw_info->switcher, sw_info->set_language_engine_list_atom, sw_info->set_language_engine_list_atom, 8, GDK_PROP_MODE_REPLACE, (guchar*)le_list, len); else if (sw_info->switcher_x_window) { GdkScreen *screen = im_info_get_screen (info); GdkDisplay *display = gdk_screen_get_display (screen); Atom x_atom = gdk_x11_atom_to_xatom_for_display (display, sw_info->set_language_engine_list_atom); XChangeProperty (GDK_DISPLAY_XDISPLAY (display), sw_info->switcher_x_window, x_atom, x_atom, 8, PropModeReplace, (guchar *)le_list, len); } } void im_context_switcher_set_language_list (GtkIMContextIIIM *context_iiim, IIIMCF_language *lang_list, int n_lang) { GtkIIIMInfo *info = context_iiim->iiim_info; SwitcherInfo *sw_info = im_info_get_switcher_info (info); gchar *languages; gchar *ptr; IIIMF_status st; char *langid; gint i; gsize len; const char *separator = ";"; gsize separator_len; if (sw_info == NULL) return; if (lang_list == NULL || n_lang == 0) return; /* First part, getting length */ st = iiimcf_get_language_id (lang_list[0], (const char **) &langid); if (st != IIIMF_STATUS_SUCCESS) return; separator_len = strlen (separator); len = strlen (langid); for (i = 1; i < n_lang; i++) { st = iiimcf_get_language_id (lang_list[i], (const char **) &langid); if (st != IIIMF_STATUS_SUCCESS) continue; len += strlen (langid); } len += separator_len * (i - 1); /* Second part, building string */ languages = g_new (gchar, len + 1); st = iiimcf_get_language_id (lang_list[0], (const char **) &langid); ptr = g_stpcpy (languages, langid); for (i = 1; i < n_lang; i++) { ptr = g_stpcpy (ptr, separator); st = iiimcf_get_language_id (lang_list[i], (const char **) &langid); if (st != IIIMF_STATUS_SUCCESS) continue; ptr = g_stpcpy (ptr, langid); } if (sw_info->switcher) gdk_property_change (sw_info->switcher, sw_info->set_input_language_list_atom, sw_info->set_input_language_list_atom, 8, GDK_PROP_MODE_REPLACE, (guchar*)languages, len); else if (sw_info->switcher_x_window) { GdkScreen *screen = im_info_get_screen (info); GdkDisplay *display = gdk_screen_get_display (screen); Atom x_atom = gdk_x11_atom_to_xatom_for_display (display, sw_info->set_input_language_list_atom); XChangeProperty (GDK_DISPLAY_XDISPLAY (display), sw_info->switcher_x_window, x_atom, x_atom, 8, PropModeReplace, (guchar *)languages, len); } g_free (languages); } void im_context_switcher_new (GtkIMContextIIIM *context_iiim) { SwitcherContext *w = g_new0 (SwitcherContext, 1); g_return_if_fail (context_iiim != NULL); g_return_if_fail (context_iiim->iiim_info != NULL); w->invisible = gtk_invisible_new (); gtk_widget_realize (w->invisible); gtk_widget_add_events (w->invisible, GDK_PROPERTY_CHANGE_MASK | GDK_STRUCTURE_MASK); w->destroy_handler_id = g_signal_connect (G_OBJECT (w->invisible), "destroy", G_CALLBACK (destroy_switcher_window), context_iiim); w->property_handler_id = g_signal_connect (G_OBJECT (w->invisible), "property-notify-event", G_CALLBACK (property_notify_switcher_window), context_iiim); context_iiim->switcher_context = w; } gboolean im_info_switcher_active (GtkIIIMInfo *info) { SwitcherInfo *sw_info = im_info_get_switcher_info (info); return (sw_info && (sw_info->switcher || sw_info->switcher_x_window)); } void im_context_switcher_set_focus (GtkIMContextIIIM *context_iiim) { SwitcherContext *w; GtkIIIMInfo *info = context_iiim->iiim_info; SwitcherInfo *sw_info; if (!im_info_switcher_active (info)) { im_info_switcher_new (info); if (!im_info_switcher_active (info)) return; } if (context_iiim->switcher_context == NULL) im_context_switcher_new (context_iiim); w = context_iiim->switcher_context; sw_info = im_info_get_switcher_info (info); if (w && w->invisible) gdk_selection_convert (w->invisible->window, sw_info->selection_atom, sw_info->set_current_client_atom, gtk_get_current_event_time ()); } /* input method switcher */ static GdkFilterReturn switcher_owner_filter (GdkXEvent *xev, GdkEvent *event, gpointer data) { XEvent *xevent = (GdkXEvent *)xev; GtkIIIMInfo *info = data; SwitcherInfo *sw_info = im_info_get_switcher_info (info); if (sw_info != NULL) { switch (event->type) { case SelectionClear: sw_info->switcher = NULL; sw_info->switcher_x_window = None; g_free (sw_info); im_info_set_switcher_info (info, NULL); break; default: break; } } return GDK_FILTER_CONTINUE; } static gboolean filter_destroy_event (Display *d, Window w, XEvent *ev, gpointer data) { GtkIIIMInfo *info = data; SwitcherInfo *sw_info = im_info_get_switcher_info (info); if (sw_info && sw_info->switcher_x_window == w) { sw_info->switcher = NULL; sw_info->switcher_x_window = None; g_free (sw_info); im_info_set_switcher_info (info, NULL); return TRUE; } return FALSE; } void im_info_switcher_shutdown (GtkIIIMInfo *info) { SwitcherInfo *sw_info = im_info_get_switcher_info (info); if (sw_info) { if (sw_info->switcher) gdk_window_remove_filter (sw_info->switcher, switcher_owner_filter, info); else if (sw_info->switcher_x_window) { GdkScreen *screen = im_info_get_screen (info); GdkDisplay *display = gdk_screen_get_display (screen); _XUnregisterFilter (GDK_DISPLAY_XDISPLAY (display), sw_info->switcher_x_window, filter_destroy_event, info); } } } void im_context_switcher_finalize (GtkIMContextIIIM *context_iiim) { SwitcherContext *w = context_iiim->switcher_context; if (w == NULL) return; g_signal_handler_disconnect (G_OBJECT (w->invisible), w->destroy_handler_id); g_signal_handler_disconnect (G_OBJECT (w->invisible), w->property_handler_id); gtk_widget_destroy (w->invisible); g_free (w); context_iiim->switcher_context = NULL; return; } static gchar * im_context_switcher_get_hotkey_with_type (char *string, char *type) { char *label_delimiter = ":"; char **hotkeys = g_strsplit (string, label_delimiter, -1); int num_hotkeys = 0, i; char *keys = NULL; // num_hotkeys = g_strv_length (hotkeys) / 2; while (hotkeys [num_hotkeys]) ++num_hotkeys; num_hotkeys /= 2; for (i = 0; i < num_hotkeys; ++i) { if (!strcasecmp (hotkeys[i * 2], type)) { keys = strdup (hotkeys[i * 2 + 1]); break; } } g_strfreev (hotkeys); return keys; } static gchar * im_context_switcher_get_hotkey (GtkIMContextIIIM *context_iiim) { int num_hotkey = 0, i; IIIMCF_hotkey *hotkeys; char s[512], *p = s; memset (s, 0, 512); iiimcf_get_hotkeys (context_iiim->context, &num_hotkey, &hotkeys); if (num_hotkey == 0) return NULL; for (i = 0; i < num_hotkey; ++i) { char label_delimiter = ':'; char key_delimiter = ','; int k; strcpy (p, hotkeys[i].hotkey_label); p += strlen (hotkeys[i].hotkey_label); *p++ = label_delimiter; for (k = 0; k < hotkeys[i].nkeys; ++k) { IIIMCF_keyevent *key = hotkeys[i].keys + k; GdkEventKey event; char *keyname; if (k) *p++ = key_delimiter; if (key->modifier & IIIMF_CONTROL_MODIFIER) { strcpy (p, "Ctrl+"); p += strlen ("Ctrl+"); } if (key->modifier & IIIMF_SHIFT_MODIFIER) { strcpy (p, "Shift+"); p += strlen ("Shift+"); } if (key->modifier & IIIMF_ALT_MODIFIER) { strcpy (p, "Alt+"); p += strlen ("Alt+"); } convert_IIIMCF_keyevent_to_GdkEventKey (key, &event); keyname = gdk_keyval_name (event.keyval); if (keyname) { strcpy (p, keyname); p += strlen (keyname); } } if (i < num_hotkey - 1) *p++ = label_delimiter; } return strdup (s); } static void change_hotkey_with_type (GtkIMContextIIIM *context_iiim, char *type, int num_keys, IIIMCF_keyevent *keys) { int num_hotkey = 0, i; IIIMCF_hotkey *hotkeys = NULL; IIIMCF_handle handle = im_info_get_handle (context_iiim->iiim_info); iiimcf_get_hotkeys (context_iiim->context, &num_hotkey, &hotkeys); if (num_hotkey == 0) return; for (i = 0; i < num_hotkey; ++i) { if (!strcasecmp (type, hotkeys[i].hotkey_label)) { /* * return if the hotkey setting remains unchanged */ if (num_keys == hotkeys[i].nkeys) { int k; Bool changed = False; IIIMCF_keyevent *pk = keys, *ph = hotkeys[i].keys; for (k=0; k < num_keys; ++k) if (pk[k].keycode != ph[k].keycode || pk[k].modifier != ph[k].modifier) { changed = True; break; } if (!changed) return; } if (hotkeys[i].nkeys < num_keys) { g_free (hotkeys[i].keys); hotkeys[i].keys = g_new0 (IIIMCF_keyevent, num_keys); } hotkeys[i].nkeys = num_keys; memcpy (hotkeys[i].keys, keys, sizeof (IIIMCF_keyevent) * num_keys); break; } } /* change the onkeys and offkeys if the target type is TRIGGER_KEY_LABEL */ if ( handle && !strcasecmp (type, TRIGGER_KEY_LABEL)) { int num_on_keys = 0, num_off_keys = 0; static int initial_num_on_keys = 0, initial_num_off_keys = 0; IIIMCF_keyevent *onkeys, *offkeys; iiimcf_get_trigger_keys (handle, &num_on_keys, &onkeys, &num_off_keys, &offkeys); if (!initial_num_on_keys && !initial_num_off_keys) { initial_num_on_keys = num_on_keys; initial_num_off_keys = num_off_keys; } num_keys = initial_num_on_keys < num_keys ? initial_num_on_keys : num_keys; memcpy (onkeys, keys, sizeof (IIIMCF_keyevent) * num_keys); memset (onkeys + num_keys, 0, sizeof (IIIMCF_keyevent) * (initial_num_on_keys - num_keys)); num_keys = initial_num_off_keys < num_keys ? initial_num_off_keys : num_keys; memcpy (offkeys, keys, sizeof (IIIMCF_keyevent) * num_keys); memset (offkeys + num_keys, 0, sizeof (IIIMCF_keyevent) * (initial_num_off_keys - num_keys)); } } /* * The triggerkey information is set by gimlet with set_hotkey_atom, it is gimlet's responsibility * to validate its content before change the atom's content. * Usually the format of hotkey would take "Ctrl+space,Kanji,Shift+Kanji". * * Now hotkey supports Left and Right distinguishable modifiers. (5/29/2008) * ex: Control_L+P does not correspond to right control plus p. * Old format Ctl+space correspond to Control_L+space and Control_R+space. */ static void convert_hotkey_to_IIIMCF_keyevent (gchar *triggerkey, int *num_keys, IIIMCF_keyevent **keys) { char *key_delimiter = ","; char **hotkeys = g_strsplit (triggerkey, key_delimiter, -1); int i; if (hotkeys == NULL) return; // *num_keys = g_strv_length (hotkeys); *num_keys = 0; while (hotkeys[*num_keys]) ++ *num_keys; *keys = g_new0 (IIIMCF_keyevent, *num_keys); for (i=0; i < *num_keys; ++i) { char *key_separator = "+"; gchar **k, **keys_text = g_strsplit (hotkeys[i], key_separator, -1); k = keys_text; for (; *k; ++k) { if (!strcasecmp (*k, "Ctrl")) (*keys)[i].modifier |= IIIMF_CONTROL_MODIFIER; else if (!strcasecmp (*k, "Shift")) (*keys)[i].modifier |= IIIMF_SHIFT_MODIFIER; else if (!strcasecmp (*k, "Alt")) (*keys)[i].modifier |= IIIMF_ALT_MODIFIER; else if (!strcasecmp (*k, "Control_L")) (*keys)[i].modifier |= IIIMCF_CONTROL_L_STATE; else if (!strcasecmp (*k, "Control_R")) (*keys)[i].modifier |= IIIMCF_CONTROL_R_STATE; else if (!strcasecmp (*k, "Shift_L")) (*keys)[i].modifier |= IIIMCF_SHIFT_L_STATE; else if (!strcasecmp (*k, "Shift_R")) (*keys)[i].modifier |= IIIMCF_SHIFT_R_STATE; else if (!strcasecmp (*k, "Alt_L")) (*keys)[i].modifier |= IIIMCF_ALT_L_STATE; else if (!strcasecmp (*k, "Alt_R")) (*keys)[i].modifier |= IIIMCF_ALT_R_STATE; else if (!strcasecmp (*k, "Meta_L")) (*keys)[i].modifier |= IIIMCF_META_L_STATE; else if (!strcasecmp (*k, "Meta_R")) (*keys)[i].modifier |= IIIMCF_META_R_STATE; else { guint keyval = gdk_keyval_from_name (*k); gint keycode = g2icode (keyval); if (keycode < 0) { (*keys)[i].keychar = 0; (*keys)[i].keycode = -keycode; } else { (*keys)[i].keychar = gdk_keyval_to_unicode (keyval); (*keys)[i].keycode = keycode; } } } g_strfreev (keys_text); } g_strfreev (hotkeys); } static void im_context_switcher_change_hotkey_with_type (GtkIMContextIIIM *context_iiim, gchar *type, gchar *string) { int num_keys; IIIMCF_keyevent *keys; convert_hotkey_to_IIIMCF_keyevent (string, &num_keys, &keys); change_hotkey_with_type (context_iiim, type, num_keys, keys); g_free (keys); } static char * im_context_switcher_get_hotkey_with_atom (GtkIMContextIIIM *context_iiim) { SwitcherInfo *sw_info = im_info_get_switcher_info (context_iiim->iiim_info); GdkScreen *screen; GdkDisplay *display; Atom x_atom; int format; unsigned long length, nitem; unsigned char *data = NULL; Atom type; screen = im_info_get_screen (context_iiim->iiim_info); display = gdk_screen_get_display (screen); #ifdef HAS_IIIM_PROPERTIES { Atom hotkey_list_atom; XTextProperty text_props; Display *x_display; x_display = GDK_DISPLAY_XDISPLAY (display); text_props.value = NULL; hotkey_list_atom = XInternAtom (x_display, "_IIIM_SWITCHER_HOTKEY_LIST", True); if (hotkey_list_atom != None) { XGetTextProperty (x_display, RootWindow (x_display, DefaultScreen (x_display)), &text_props, hotkey_list_atom); data = text_props.value; } } #else /* HAS_IIIM_PROPERTIES */ if (!im_info_switcher_active (context_iiim->iiim_info)) return NULL; x_atom = gdk_x11_atom_to_xatom_for_display (display, sw_info->set_hotkey_atom); XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), sw_info->switcher_x_window, x_atom, 0, INT_MAX, False, x_atom, &type, &format, &nitem, &length, &data); #endif /* HAS_IIIM_PROPERTIES */ return data; } void IIIMSetHotkey (GtkIMContextIIIM *context_iiim) { char *hotkey; /* * server sends the hotkey when client's context is constructed, client * tries to get the hotkey info with iiimcf_register_hotkeys by sending * one HOTKEY_NOTIFY message. Usually it will happen at the first time * when client sends seticfocus event to server within im_context_iiim_focus_in. * The hotkey wouldn't be changed at the client's life cycle unless another * hotkey profile is used insteads. * * To communicate with gimlet, current implementation is as followings: * once client get focus, it checks whether hotkey info exists with * _IIIM_SWITCHER_SET_HOTKEY atom, if it does exists, check further * whether the conversion keys get changed with the atom by gimlet, * if yes, change the conversion keys. Otherwise, get the hotkey info * from server and notify to gimlet with the above atom. * * FIXME one better way to handle the communication is that each client * can be notified asynchronously with the atom change from gimlet window. */ if ((hotkey = im_context_switcher_get_hotkey_with_atom (context_iiim)) == NULL) { if (hotkey = im_context_switcher_get_hotkey (context_iiim)) im_context_switcher_set_hotkey (context_iiim, hotkey); } else { char *conversion_keys; char *langlist_menu_keys; char *cycle_lang_switch_keys; char *reverse_cycle_lang_switch_keys; /* change the conversion keys */ conversion_keys = im_context_switcher_get_hotkey_with_type (hotkey, TRIGGER_KEY_LABEL); im_context_switcher_change_hotkey_with_type (context_iiim, TRIGGER_KEY_LABEL, conversion_keys); g_free (conversion_keys); langlist_menu_keys = im_context_switcher_get_hotkey_with_type (hotkey, LANGLIST_MENU_LABEL); if (langlist_menu_keys != NULL) { im_context_switcher_change_hotkey_with_type (context_iiim, LANGLIST_MENU_LABEL, langlist_menu_keys); free (langlist_menu_keys); } cycle_lang_switch_keys = im_context_switcher_get_hotkey_with_type (hotkey, CYCLE_LANG_SWITCH_LABEL); if (cycle_lang_switch_keys != NULL) { im_context_switcher_change_hotkey_with_type (context_iiim, CYCLE_LANG_SWITCH_LABEL, cycle_lang_switch_keys); free (cycle_lang_switch_keys); } reverse_cycle_lang_switch_keys = im_context_switcher_get_hotkey_with_type (hotkey, RE_CYCLE_LANG_SWITCH_LABEL); if (reverse_cycle_lang_switch_keys != NULL) { im_context_switcher_change_hotkey_with_type (context_iiim, RE_CYCLE_LANG_SWITCH_LABEL, reverse_cycle_lang_switch_keys); free (reverse_cycle_lang_switch_keys); } } if (hotkey) g_free (hotkey); }