| File: | modules/input/ctkimcontextxim.c |
| Warning: | line 1187, column 17 Casting a non-structure type to a structure type and accessing a field can lead to memory access errors or data corruption |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* CTK - The GIMP Toolkit |
| 2 | * Copyright (C) 2000 Red Hat, Inc. |
| 3 | * |
| 4 | * This library is free software; you can redistribute it and/or |
| 5 | * modify it under the terms of the GNU Lesser General Public |
| 6 | * License as published by the Free Software Foundation; either |
| 7 | * version 2 of the License, or (at your option) any later version. |
| 8 | * |
| 9 | * This library is distributed in the hope that it will be useful, |
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 12 | * Lesser General Public License for more details. |
| 13 | * |
| 14 | * You should have received a copy of the GNU Lesser General Public |
| 15 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
| 16 | */ |
| 17 | |
| 18 | #include "config.h" |
| 19 | #include "locale.h" |
| 20 | #include <string.h> |
| 21 | #include <stdlib.h> |
| 22 | |
| 23 | #include "ctkimcontextxim.h" |
| 24 | |
| 25 | #include "ctk/ctkintl.h" |
| 26 | |
| 27 | typedef struct _StatusWindow StatusWindow; |
| 28 | typedef struct _CtkXIMInfo CtkXIMInfo; |
| 29 | |
| 30 | struct _CtkIMContextXIM |
| 31 | { |
| 32 | CtkIMContext object; |
| 33 | |
| 34 | CtkXIMInfo *im_info; |
| 35 | |
| 36 | gchar *locale; |
| 37 | gchar *mb_charset; |
| 38 | |
| 39 | CdkWindow *client_window; |
| 40 | CtkWidget *client_widget; |
| 41 | |
| 42 | /* The status window for this input context; we claim the |
| 43 | * status window when we are focused and have created an XIC |
| 44 | */ |
| 45 | StatusWindow *status_window; |
| 46 | |
| 47 | gint preedit_size; |
| 48 | gint preedit_length; |
| 49 | gunichar *preedit_chars; |
| 50 | XIMFeedback *feedbacks; |
| 51 | |
| 52 | gint preedit_cursor; |
| 53 | |
| 54 | XIMCallback preedit_start_callback; |
| 55 | XIMCallback preedit_done_callback; |
| 56 | XIMCallback preedit_draw_callback; |
| 57 | XIMCallback preedit_caret_callback; |
| 58 | |
| 59 | XIMCallback status_start_callback; |
| 60 | XIMCallback status_done_callback; |
| 61 | XIMCallback status_draw_callback; |
| 62 | |
| 63 | XIMCallback string_conversion_callback; |
| 64 | |
| 65 | XIC ic; |
| 66 | |
| 67 | guint filter_key_release : 1; |
| 68 | guint use_preedit : 1; |
| 69 | guint finalizing : 1; |
| 70 | guint in_toplevel : 1; |
| 71 | guint has_focus : 1; |
| 72 | }; |
| 73 | |
| 74 | struct _CtkXIMInfo |
| 75 | { |
| 76 | CdkScreen *screen; |
| 77 | XIM im; |
| 78 | char *locale; |
| 79 | XIMStyle preedit_style_setting; |
| 80 | XIMStyle status_style_setting; |
| 81 | XIMStyle style; |
| 82 | CtkSettings *settings; |
| 83 | gulong status_set; |
| 84 | gulong preedit_set; |
| 85 | gulong display_closed_cb; |
| 86 | XIMStyles *xim_styles; |
| 87 | GSList *ics; |
| 88 | |
| 89 | guint reconnecting :1; |
| 90 | guint supports_string_conversion; |
| 91 | }; |
| 92 | |
| 93 | /* A context status window; these are kept in the status_windows list. */ |
| 94 | struct _StatusWindow |
| 95 | { |
| 96 | CtkWidget *window; |
| 97 | |
| 98 | /* Toplevel window to which the status window corresponds */ |
| 99 | CtkWidget *toplevel; |
| 100 | |
| 101 | /* Currently focused CtkIMContextXIM for the toplevel, if any */ |
| 102 | CtkIMContextXIM *context; |
| 103 | }; |
| 104 | |
| 105 | static void ctk_im_context_xim_class_init (CtkIMContextXIMClass *class); |
| 106 | static void ctk_im_context_xim_init (CtkIMContextXIM *im_context_xim); |
| 107 | static void ctk_im_context_xim_finalize (GObject *obj); |
| 108 | static void ctk_im_context_xim_set_client_window (CtkIMContext *context, |
| 109 | CdkWindow *client_window); |
| 110 | static gboolean ctk_im_context_xim_filter_keypress (CtkIMContext *context, |
| 111 | CdkEventKey *key); |
| 112 | static void ctk_im_context_xim_reset (CtkIMContext *context); |
| 113 | static void ctk_im_context_xim_focus_in (CtkIMContext *context); |
| 114 | static void ctk_im_context_xim_focus_out (CtkIMContext *context); |
| 115 | static void ctk_im_context_xim_set_cursor_location (CtkIMContext *context, |
| 116 | CdkRectangle *area); |
| 117 | static void ctk_im_context_xim_set_use_preedit (CtkIMContext *context, |
| 118 | gboolean use_preedit); |
| 119 | static void ctk_im_context_xim_get_preedit_string (CtkIMContext *context, |
| 120 | gchar **str, |
| 121 | PangoAttrList **attrs, |
| 122 | gint *cursor_pos); |
| 123 | |
| 124 | static void reinitialize_ic (CtkIMContextXIM *context_xim); |
| 125 | static void set_ic_client_window (CtkIMContextXIM *context_xim, |
| 126 | CdkWindow *client_window); |
| 127 | |
| 128 | static void setup_styles (CtkXIMInfo *info); |
| 129 | |
| 130 | static void update_client_widget (CtkIMContextXIM *context_xim); |
| 131 | static void update_status_window (CtkIMContextXIM *context_xim); |
| 132 | |
| 133 | static StatusWindow *status_window_get (CtkWidget *toplevel); |
| 134 | static void status_window_free (StatusWindow *status_window); |
| 135 | static void status_window_set_text (StatusWindow *status_window, |
| 136 | const gchar *text); |
| 137 | |
| 138 | static void xim_destroy_callback (XIM xim, |
| 139 | XPointer client_data, |
| 140 | XPointer call_data); |
| 141 | |
| 142 | static XIC ctk_im_context_xim_get_ic (CtkIMContextXIM *context_xim); |
| 143 | static void xim_info_display_closed (CdkDisplay *display, |
| 144 | gboolean is_error, |
| 145 | CtkXIMInfo *info); |
| 146 | |
| 147 | static GObjectClass *parent_class; |
| 148 | |
| 149 | GType ctk_type_im_context_xim = 0; |
| 150 | |
| 151 | static GSList *open_ims = NULL((void*)0); |
| 152 | |
| 153 | /* List of status windows for different toplevels */ |
| 154 | static GSList *status_windows = NULL((void*)0); |
| 155 | |
| 156 | void |
| 157 | ctk_im_context_xim_register_type (GTypeModule *type_module) |
| 158 | { |
| 159 | const GTypeInfo im_context_xim_info = |
| 160 | { |
| 161 | .class_size = sizeof (CtkIMContextXIMClass), |
| 162 | .base_init = (GBaseInitFunc) NULL((void*)0), |
| 163 | .base_finalize = (GBaseFinalizeFunc) NULL((void*)0), |
| 164 | .class_init = (GClassInitFunc) ctk_im_context_xim_class_init, |
| 165 | .instance_size = sizeof (CtkIMContextXIM), |
| 166 | .n_preallocs = 0, |
| 167 | .instance_init = (GInstanceInitFunc) ctk_im_context_xim_init, |
| 168 | }; |
| 169 | |
| 170 | ctk_type_im_context_xim = |
| 171 | g_type_module_register_type (type_module, |
| 172 | CTK_TYPE_IM_CONTEXT(ctk_im_context_get_type ()), |
| 173 | "CtkIMContextXIM", |
| 174 | &im_context_xim_info, 0); |
| 175 | } |
| 176 | |
| 177 | #define PREEDIT_MASK(0x0002L | 0x0004L | 0x0001L | 0x0008L | 0x0010L) (XIMPreeditCallbacks0x0002L | XIMPreeditPosition0x0004L | \ |
| 178 | XIMPreeditArea0x0001L | XIMPreeditNothing0x0008L | XIMPreeditNone0x0010L) |
| 179 | #define STATUS_MASK(0x0200L | 0x0100L | 0x0400L | 0x0800L) (XIMStatusCallbacks0x0200L | XIMStatusArea0x0100L | \ |
| 180 | XIMStatusNothing0x0400L | XIMStatusNone0x0800L) |
| 181 | #define ALLOWED_MASK(0x0002L | 0x0008L | 0x0010L | 0x0200L | 0x0400L | 0x0800L) (XIMPreeditCallbacks0x0002L | XIMPreeditNothing0x0008L | XIMPreeditNone0x0010L | \ |
| 182 | XIMStatusCallbacks0x0200L | XIMStatusNothing0x0400L | XIMStatusNone0x0800L) |
| 183 | |
| 184 | static XIMStyle |
| 185 | choose_better_style (XIMStyle style1, XIMStyle style2) |
| 186 | { |
| 187 | XIMStyle s1, s2, u; |
| 188 | |
| 189 | if (style1 == 0) return style2; |
| 190 | if (style2 == 0) return style1; |
| 191 | if ((style1 & (PREEDIT_MASK(0x0002L | 0x0004L | 0x0001L | 0x0008L | 0x0010L) | STATUS_MASK(0x0200L | 0x0100L | 0x0400L | 0x0800L))) |
| 192 | == (style2 & (PREEDIT_MASK(0x0002L | 0x0004L | 0x0001L | 0x0008L | 0x0010L) | STATUS_MASK(0x0200L | 0x0100L | 0x0400L | 0x0800L)))) |
| 193 | return style1; |
| 194 | |
| 195 | s1 = style1 & PREEDIT_MASK(0x0002L | 0x0004L | 0x0001L | 0x0008L | 0x0010L); |
| 196 | s2 = style2 & PREEDIT_MASK(0x0002L | 0x0004L | 0x0001L | 0x0008L | 0x0010L); |
| 197 | u = s1 | s2; |
| 198 | if (s1 != s2) { |
| 199 | if (u & XIMPreeditCallbacks0x0002L) |
| 200 | return (s1 == XIMPreeditCallbacks0x0002L) ? style1 : style2; |
| 201 | else if (u & XIMPreeditPosition0x0004L) |
| 202 | return (s1 == XIMPreeditPosition0x0004L) ? style1 :style2; |
| 203 | else if (u & XIMPreeditArea0x0001L) |
| 204 | return (s1 == XIMPreeditArea0x0001L) ? style1 : style2; |
| 205 | else if (u & XIMPreeditNothing0x0008L) |
| 206 | return (s1 == XIMPreeditNothing0x0008L) ? style1 : style2; |
| 207 | else if (u & XIMPreeditNone0x0010L) |
| 208 | return (s1 == XIMPreeditNone0x0010L) ? style1 : style2; |
| 209 | } else { |
| 210 | s1 = style1 & STATUS_MASK(0x0200L | 0x0100L | 0x0400L | 0x0800L); |
| 211 | s2 = style2 & STATUS_MASK(0x0200L | 0x0100L | 0x0400L | 0x0800L); |
| 212 | u = s1 | s2; |
| 213 | if (u & XIMStatusCallbacks0x0200L) |
| 214 | return (s1 == XIMStatusCallbacks0x0200L) ? style1 : style2; |
| 215 | else if (u & XIMStatusArea0x0100L) |
| 216 | return (s1 == XIMStatusArea0x0100L) ? style1 : style2; |
| 217 | else if (u & XIMStatusNothing0x0400L) |
| 218 | return (s1 == XIMStatusNothing0x0400L) ? style1 : style2; |
| 219 | else if (u & XIMStatusNone0x0800L) |
| 220 | return (s1 == XIMStatusNone0x0800L) ? style1 : style2; |
| 221 | } |
| 222 | return 0; /* Get rid of stupid warning */ |
| 223 | } |
| 224 | |
| 225 | static void |
| 226 | reinitialize_all_ics (CtkXIMInfo *info) |
| 227 | { |
| 228 | GSList *tmp_list; |
| 229 | |
| 230 | for (tmp_list = info->ics; tmp_list; tmp_list = tmp_list->next) |
| 231 | reinitialize_ic (tmp_list->data); |
| 232 | } |
| 233 | |
| 234 | static void |
| 235 | setup_styles (CtkXIMInfo *info) |
| 236 | { |
| 237 | unsigned long settings_preference; |
| 238 | XIMStyles *xim_styles = info->xim_styles; |
| 239 | |
| 240 | settings_preference = info->status_style_setting|info->preedit_style_setting; |
| 241 | info->style = 0; |
| 242 | if (xim_styles) |
| 243 | { |
| 244 | int i; |
| 245 | |
| 246 | for (i = 0; i < xim_styles->count_styles; i++) |
| 247 | if ((xim_styles->supported_styles[i] & ALLOWED_MASK(0x0002L | 0x0008L | 0x0010L | 0x0200L | 0x0400L | 0x0800L)) == xim_styles->supported_styles[i]) |
| 248 | { |
| 249 | if (settings_preference == xim_styles->supported_styles[i]) |
| 250 | { |
| 251 | info->style = settings_preference; |
| 252 | break; |
| 253 | } |
| 254 | info->style = choose_better_style (info->style, |
| 255 | xim_styles->supported_styles[i]); |
| 256 | } |
| 257 | } |
| 258 | if (info->style == 0) |
| 259 | info->style = XIMPreeditNothing0x0008L | XIMStatusNothing0x0400L; |
| 260 | } |
| 261 | |
| 262 | static void |
| 263 | setup_im (CtkXIMInfo *info) |
| 264 | { |
| 265 | XIMValuesList *ic_values = NULL((void*)0); |
| 266 | XIMCallback im_destroy_callback; |
| 267 | CdkDisplay *display; |
| 268 | |
| 269 | if (info->im == NULL((void*)0)) |
| 270 | return; |
| 271 | |
| 272 | im_destroy_callback.client_data = (XPointer)info; |
| 273 | im_destroy_callback.callback = (XIMProc)xim_destroy_callback; |
| 274 | XSetIMValues (info->im, |
| 275 | XNDestroyCallback"destroyCallback", &im_destroy_callback, |
| 276 | NULL((void*)0)); |
| 277 | |
| 278 | XGetIMValues (info->im, |
| 279 | XNQueryInputStyle"queryInputStyle", &info->xim_styles, |
| 280 | XNQueryICValuesList"queryICValuesList", &ic_values, |
| 281 | NULL((void*)0)); |
| 282 | |
| 283 | info->supports_string_conversion = FALSE(0); |
| 284 | if (ic_values) |
| 285 | { |
| 286 | int i; |
| 287 | |
| 288 | for (i = 0; i < ic_values->count_values; i++) |
| 289 | if (strcmp (ic_values->supported_values[i], |
| 290 | XNStringConversionCallback"stringConversionCallback") == 0) |
| 291 | { |
| 292 | info->supports_string_conversion = TRUE(!(0)); |
| 293 | break; |
| 294 | } |
| 295 | |
| 296 | #if 0 |
| 297 | for (i = 0; i < ic_values->count_values; i++) |
| 298 | g_print ("%s\n", ic_values->supported_values[i]); |
| 299 | for (i = 0; i < xim_styles->count_styles; i++) |
| 300 | g_print ("%#x\n", xim_styles->supported_styles[i]); |
| 301 | #endif |
| 302 | |
| 303 | XFree (ic_values); |
| 304 | } |
| 305 | |
| 306 | info->status_style_setting = XIMStatusCallbacks0x0200L; |
| 307 | info->preedit_style_setting = XIMPreeditCallbacks0x0002L; |
| 308 | setup_styles (info); |
| 309 | reinitialize_all_ics (info); |
| 310 | |
| 311 | display = cdk_screen_get_display (info->screen); |
| 312 | info->display_closed_cb = g_signal_connect (display, "closed",g_signal_connect_data ((display), ("closed"), (((GCallback) ( xim_info_display_closed))), (info), ((void*)0), (GConnectFlags ) 0) |
| 313 | G_CALLBACK (xim_info_display_closed), info)g_signal_connect_data ((display), ("closed"), (((GCallback) ( xim_info_display_closed))), (info), ((void*)0), (GConnectFlags ) 0); |
| 314 | } |
| 315 | |
| 316 | static void |
| 317 | xim_info_display_closed (CdkDisplay *display, |
| 318 | gboolean is_error G_GNUC_UNUSED__attribute__ ((__unused__)), |
| 319 | CtkXIMInfo *info) |
| 320 | { |
| 321 | GSList *ics, *tmp_list; |
| 322 | |
| 323 | open_ims = g_slist_remove (open_ims, info); |
| 324 | |
| 325 | ics = info->ics; |
| 326 | info->ics = NULL((void*)0); |
| 327 | |
| 328 | for (tmp_list = ics; tmp_list; tmp_list = tmp_list->next) |
| 329 | set_ic_client_window (tmp_list->data, NULL((void*)0)); |
| 330 | |
| 331 | g_slist_free (ics); |
| 332 | |
| 333 | if (info->status_set) |
| 334 | g_signal_handler_disconnect (info->settings, info->status_set); |
| 335 | if (info->preedit_set) |
| 336 | g_signal_handler_disconnect (info->settings, info->preedit_set); |
| 337 | if (info->display_closed_cb) |
| 338 | g_signal_handler_disconnect (display, info->display_closed_cb); |
| 339 | |
| 340 | if (info->xim_styles) |
| 341 | XFree (info->xim_styles); |
| 342 | g_free (info->locale); |
| 343 | |
| 344 | if (info->im) |
| 345 | XCloseIM (info->im); |
| 346 | |
| 347 | g_free (info); |
| 348 | } |
| 349 | |
| 350 | static void |
| 351 | xim_instantiate_callback (Display *display, XPointer client_data, |
| 352 | XPointer call_data G_GNUC_UNUSED__attribute__ ((__unused__))) |
| 353 | { |
| 354 | CtkXIMInfo *info = (CtkXIMInfo*)client_data; |
| 355 | XIM im = NULL((void*)0); |
| 356 | |
| 357 | im = XOpenIM (display, NULL((void*)0), NULL((void*)0), NULL((void*)0)); |
| 358 | |
| 359 | if (!im) |
| 360 | return; |
| 361 | |
| 362 | info->im = im; |
| 363 | setup_im (info); |
| 364 | |
| 365 | XUnregisterIMInstantiateCallback (display, NULL((void*)0), NULL((void*)0), NULL((void*)0), |
| 366 | xim_instantiate_callback, |
| 367 | (XPointer)info); |
| 368 | info->reconnecting = FALSE(0); |
| 369 | } |
| 370 | |
| 371 | /* initialize info->im */ |
| 372 | static void |
| 373 | xim_info_try_im (CtkXIMInfo *info) |
| 374 | { |
| 375 | CdkScreen *screen = info->screen; |
| 376 | CdkDisplay *display = cdk_screen_get_display (screen); |
| 377 | |
| 378 | g_assert (info->im == NULL)do { if (info->im == ((void*)0)) ; else g_assertion_message_expr (((gchar*) 0), "ctkimcontextxim.c", 378, ((const char*) (__func__ )), "info->im == NULL"); } while (0); |
| 379 | if (info->reconnecting) |
| 380 | return; |
| 381 | |
| 382 | if (XSupportsLocale ()) |
| 383 | { |
| 384 | if (!XSetLocaleModifiers ("")) |
| 385 | g_warning ("Unable to set locale modifiers with XSetLocaleModifiers()"); |
| 386 | info->im = XOpenIM (CDK_DISPLAY_XDISPLAY (display)(cdk_x11_display_get_xdisplay (display)), NULL((void*)0), NULL((void*)0), NULL((void*)0)); |
| 387 | if (!info->im) |
| 388 | { |
| 389 | XRegisterIMInstantiateCallback (CDK_DISPLAY_XDISPLAY(display)(cdk_x11_display_get_xdisplay (display)), |
| 390 | NULL((void*)0), NULL((void*)0), NULL((void*)0), |
| 391 | xim_instantiate_callback, |
| 392 | (XPointer)info); |
| 393 | info->reconnecting = TRUE(!(0)); |
| 394 | return; |
| 395 | } |
| 396 | setup_im (info); |
| 397 | } |
| 398 | } |
| 399 | |
| 400 | static void |
| 401 | xim_destroy_callback (XIM xim G_GNUC_UNUSED__attribute__ ((__unused__)), |
| 402 | XPointer client_data, |
| 403 | XPointer call_data G_GNUC_UNUSED__attribute__ ((__unused__))) |
| 404 | { |
| 405 | CtkXIMInfo *info = (CtkXIMInfo*)client_data; |
| 406 | |
| 407 | info->im = NULL((void*)0); |
| 408 | |
| 409 | g_signal_handler_disconnect (info->settings, info->status_set); |
| 410 | info->status_set = 0; |
| 411 | g_signal_handler_disconnect (info->settings, info->preedit_set); |
| 412 | info->preedit_set = 0; |
| 413 | |
| 414 | reinitialize_all_ics (info); |
| 415 | xim_info_try_im (info); |
| 416 | return; |
| 417 | } |
| 418 | |
| 419 | static CtkXIMInfo * |
| 420 | get_im (CdkWindow *client_window, |
| 421 | const char *locale) |
| 422 | { |
| 423 | GSList *tmp_list; |
| 424 | CtkXIMInfo *info; |
| 425 | CdkScreen *screen = cdk_window_get_screen (client_window); |
| 426 | |
| 427 | info = NULL((void*)0); |
| 428 | tmp_list = open_ims; |
| 429 | while (tmp_list) |
| 430 | { |
| 431 | CtkXIMInfo *tmp_info = tmp_list->data; |
| 432 | if (tmp_info->screen == screen && |
| 433 | strcmp (tmp_info->locale, locale) == 0) |
| 434 | { |
| 435 | if (tmp_info->im) |
| 436 | { |
| 437 | return tmp_info; |
| 438 | } |
| 439 | else |
| 440 | { |
| 441 | tmp_info = tmp_info; |
| 442 | break; |
| 443 | } |
| 444 | } |
| 445 | tmp_list = tmp_list->next; |
| 446 | } |
| 447 | |
| 448 | if (info == NULL((void*)0)) |
| 449 | { |
| 450 | info = g_new (CtkXIMInfo, 1)((CtkXIMInfo *) g_malloc_n ((1), sizeof (CtkXIMInfo))); |
| 451 | open_ims = g_slist_prepend (open_ims, info); |
| 452 | |
| 453 | info->screen = screen; |
| 454 | info->locale = g_strdup (locale)g_strdup_inline (locale); |
| 455 | info->xim_styles = NULL((void*)0); |
| 456 | info->preedit_style_setting = 0; |
| 457 | info->status_style_setting = 0; |
| 458 | info->settings = NULL((void*)0); |
| 459 | info->preedit_set = 0; |
| 460 | info->status_set = 0; |
| 461 | info->display_closed_cb = 0; |
| 462 | info->ics = NULL((void*)0); |
| 463 | info->reconnecting = FALSE(0); |
| 464 | info->im = NULL((void*)0); |
| 465 | } |
| 466 | |
| 467 | xim_info_try_im (info); |
| 468 | return info; |
| 469 | } |
| 470 | |
| 471 | static void |
| 472 | ctk_im_context_xim_class_init (CtkIMContextXIMClass *class) |
| 473 | { |
| 474 | CtkIMContextClass *im_context_class = CTK_IM_CONTEXT_CLASS (class)((((CtkIMContextClass*) (void *) g_type_check_class_cast ((GTypeClass *) ((class)), ((ctk_im_context_get_type ())))))); |
| 475 | GObjectClass *gobject_class = G_OBJECT_CLASS (class)((((GObjectClass*) (void *) g_type_check_class_cast ((GTypeClass *) ((class)), (((GType) ((20) << (2)))))))); |
| 476 | |
| 477 | parent_class = g_type_class_peek_parent (class); |
| 478 | |
| 479 | im_context_class->set_client_window = ctk_im_context_xim_set_client_window; |
| 480 | im_context_class->filter_keypress = ctk_im_context_xim_filter_keypress; |
| 481 | im_context_class->reset = ctk_im_context_xim_reset; |
| 482 | im_context_class->get_preedit_string = ctk_im_context_xim_get_preedit_string; |
| 483 | im_context_class->focus_in = ctk_im_context_xim_focus_in; |
| 484 | im_context_class->focus_out = ctk_im_context_xim_focus_out; |
| 485 | im_context_class->set_cursor_location = ctk_im_context_xim_set_cursor_location; |
| 486 | im_context_class->set_use_preedit = ctk_im_context_xim_set_use_preedit; |
| 487 | gobject_class->finalize = ctk_im_context_xim_finalize; |
| 488 | } |
| 489 | |
| 490 | static void |
| 491 | ctk_im_context_xim_init (CtkIMContextXIM *im_context_xim) |
| 492 | { |
| 493 | im_context_xim->use_preedit = TRUE(!(0)); |
| 494 | im_context_xim->filter_key_release = FALSE(0); |
| 495 | im_context_xim->finalizing = FALSE(0); |
| 496 | im_context_xim->has_focus = FALSE(0); |
| 497 | im_context_xim->in_toplevel = FALSE(0); |
| 498 | } |
| 499 | |
| 500 | static void |
| 501 | ctk_im_context_xim_finalize (GObject *obj) |
| 502 | { |
| 503 | CtkIMContextXIM *context_xim = CTK_IM_CONTEXT_XIM (obj)((((CtkIMContextXIM*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((obj)), ((ctk_type_im_context_xim)))))); |
| 504 | |
| 505 | context_xim->finalizing = TRUE(!(0)); |
| 506 | |
| 507 | if (context_xim->im_info && !context_xim->im_info->ics->next) |
| 508 | { |
| 509 | if (context_xim->im_info->reconnecting) |
| 510 | { |
| 511 | CdkDisplay *display; |
| 512 | |
| 513 | display = cdk_screen_get_display (context_xim->im_info->screen); |
| 514 | XUnregisterIMInstantiateCallback (CDK_DISPLAY_XDISPLAY (display)(cdk_x11_display_get_xdisplay (display)), |
| 515 | NULL((void*)0), NULL((void*)0), NULL((void*)0), |
| 516 | xim_instantiate_callback, |
| 517 | (XPointer)context_xim->im_info); |
| 518 | } |
| 519 | else if (context_xim->im_info->im) |
| 520 | { |
| 521 | XIMCallback im_destroy_callback; |
| 522 | |
| 523 | im_destroy_callback.client_data = NULL((void*)0); |
| 524 | im_destroy_callback.callback = NULL((void*)0); |
| 525 | XSetIMValues (context_xim->im_info->im, |
| 526 | XNDestroyCallback"destroyCallback", &im_destroy_callback, |
| 527 | NULL((void*)0)); |
| 528 | } |
| 529 | } |
| 530 | |
| 531 | set_ic_client_window (context_xim, NULL((void*)0)); |
| 532 | |
| 533 | g_free (context_xim->locale); |
| 534 | g_free (context_xim->mb_charset); |
| 535 | |
| 536 | G_OBJECT_CLASS (parent_class)((((GObjectClass*) (void *) g_type_check_class_cast ((GTypeClass *) ((parent_class)), (((GType) ((20) << (2))))))))->finalize (obj); |
| 537 | } |
| 538 | |
| 539 | static void |
| 540 | reinitialize_ic (CtkIMContextXIM *context_xim) |
| 541 | { |
| 542 | if (context_xim->ic) |
| 543 | { |
| 544 | XDestroyIC (context_xim->ic); |
| 545 | context_xim->ic = NULL((void*)0); |
| 546 | update_status_window (context_xim); |
| 547 | |
| 548 | if (context_xim->preedit_length) |
| 549 | { |
| 550 | context_xim->preedit_length = 0; |
| 551 | if (!context_xim->finalizing) |
| 552 | g_signal_emit_by_name (context_xim, "preedit-changed"); |
| 553 | } |
| 554 | } |
| 555 | /* |
| 556 | reset filter_key_release flag, otherwise keystrokes will be doubled |
| 557 | until reconnecting to XIM. |
| 558 | */ |
| 559 | context_xim->filter_key_release = FALSE(0); |
| 560 | } |
| 561 | |
| 562 | static void |
| 563 | set_ic_client_window (CtkIMContextXIM *context_xim, |
| 564 | CdkWindow *client_window) |
| 565 | { |
| 566 | reinitialize_ic (context_xim); |
| 567 | if (context_xim->client_window) |
| 568 | { |
| 569 | context_xim->im_info->ics = g_slist_remove (context_xim->im_info->ics, context_xim); |
| 570 | context_xim->im_info = NULL((void*)0); |
| 571 | } |
| 572 | |
| 573 | context_xim->client_window = client_window; |
| 574 | |
| 575 | if (context_xim->client_window) |
| 576 | { |
| 577 | context_xim->im_info = get_im (context_xim->client_window, context_xim->locale); |
| 578 | context_xim->im_info->ics = g_slist_prepend (context_xim->im_info->ics, context_xim); |
| 579 | } |
| 580 | |
| 581 | update_client_widget (context_xim); |
| 582 | } |
| 583 | |
| 584 | static void |
| 585 | ctk_im_context_xim_set_client_window (CtkIMContext *context, |
| 586 | CdkWindow *client_window) |
| 587 | { |
| 588 | CtkIMContextXIM *context_xim = CTK_IM_CONTEXT_XIM (context)((((CtkIMContextXIM*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((context)), ((ctk_type_im_context_xim)))))); |
| 589 | |
| 590 | set_ic_client_window (context_xim, client_window); |
| 591 | } |
| 592 | |
| 593 | CtkIMContext * |
| 594 | ctk_im_context_xim_new (void) |
| 595 | { |
| 596 | CtkIMContextXIM *result; |
| 597 | const gchar *charset; |
| 598 | |
| 599 | if (!CDK_IS_X11_DISPLAY(cdk_display_get_default())(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) ( (cdk_display_get_default())); GType __t = ((cdk_x11_display_get_type ())); gboolean __r; if (!__inst) __r = (0); else if (__inst-> g_class && __inst->g_class->g_type == __t) __r = (!(0)); else __r = g_type_check_instance_is_a (__inst, __t); __r; }))))) |
| 600 | return NULL((void*)0); |
| 601 | result = g_object_new (CTK_TYPE_IM_CONTEXT_XIM(ctk_type_im_context_xim), NULL((void*)0)); |
| 602 | |
| 603 | result->locale = g_strdup (setlocale (LC_CTYPE, NULL))g_strdup_inline (setlocale (0, ((void*)0))); |
| 604 | |
| 605 | g_get_charset (&charset); |
| 606 | result->mb_charset = g_strdup (charset)g_strdup_inline (charset); |
| 607 | |
| 608 | return CTK_IM_CONTEXT (result)((((CtkIMContext*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((result)), ((ctk_im_context_get_type ())))))); |
| 609 | } |
| 610 | |
| 611 | static char * |
| 612 | mb_to_utf8 (CtkIMContextXIM *context_xim, |
| 613 | const char *str) |
| 614 | { |
| 615 | GError *error = NULL((void*)0); |
| 616 | gchar *result; |
| 617 | |
| 618 | if (strcmp (context_xim->mb_charset, "UTF-8") == 0) |
| 619 | result = g_strdup (str)g_strdup_inline (str); |
| 620 | else |
| 621 | { |
| 622 | result = g_convert (str, -1, |
| 623 | "UTF-8", context_xim->mb_charset, |
| 624 | NULL((void*)0), NULL((void*)0), &error); |
| 625 | if (!result) |
| 626 | { |
| 627 | g_warning ("Error converting text from IM to UTF-8: %s\n", error->message); |
| 628 | g_error_free (error); |
| 629 | } |
| 630 | } |
| 631 | |
| 632 | return result; |
| 633 | } |
| 634 | |
| 635 | static gboolean |
| 636 | ctk_im_context_xim_filter_keypress (CtkIMContext *context, |
| 637 | CdkEventKey *event) |
| 638 | { |
| 639 | CtkIMContextXIM *context_xim = CTK_IM_CONTEXT_XIM (context)((((CtkIMContextXIM*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((context)), ((ctk_type_im_context_xim)))))); |
| 640 | XIC ic = ctk_im_context_xim_get_ic (context_xim); |
| 641 | gchar static_buffer[256]; |
| 642 | gchar *buffer = static_buffer; |
| 643 | gint buffer_size = sizeof(static_buffer) - 1; |
| 644 | gint num_bytes = 0; |
| 645 | KeySym keysym; |
| 646 | Statusint status; |
| 647 | gboolean result = FALSE(0); |
| 648 | CdkWindow *root_window; |
| 649 | CdkWindow *window; |
| 650 | XKeyPressedEvent xevent; |
| 651 | |
| 652 | if (context_xim->client_window == NULL((void*)0)) |
| 653 | return FALSE(0); |
| 654 | |
| 655 | if (event->type == CDK_KEY_RELEASE && !context_xim->filter_key_release) |
| 656 | return FALSE(0); |
| 657 | |
| 658 | root_window = cdk_screen_get_root_window (cdk_window_get_screen (event->window)); |
| 659 | window = cdk_window_get_toplevel (event->window); |
| 660 | |
| 661 | xevent.type = (event->type == CDK_KEY_PRESS) ? KeyPress2 : KeyRelease3; |
| 662 | xevent.serial = 0; /* hope it doesn't matter */ |
| 663 | xevent.send_event = event->send_event; |
| 664 | xevent.display = CDK_WINDOW_XDISPLAY (window)((cdk_x11_display_get_xdisplay (cdk_window_get_display (window )))); |
| 665 | xevent.window = CDK_WINDOW_XID (window)(cdk_x11_window_get_xid (window)); |
| 666 | xevent.root = CDK_WINDOW_XID (root_window)(cdk_x11_window_get_xid (root_window)); |
| 667 | xevent.subwindow = xevent.window; |
| 668 | xevent.time = event->time; |
| 669 | xevent.x = xevent.x_root = 0; |
| 670 | xevent.y = xevent.y_root = 0; |
| 671 | xevent.state = event->state; |
| 672 | xevent.keycode = event->hardware_keycode; |
| 673 | xevent.same_screen = True1; |
| 674 | |
| 675 | if (XFilterEvent ((XEvent *)&xevent, CDK_WINDOW_XID (context_xim->client_window)(cdk_x11_window_get_xid (context_xim->client_window)))) |
| 676 | return TRUE(!(0)); |
| 677 | |
| 678 | if (event->state & |
| 679 | (ctk_accelerator_get_default_mod_mask () & ~(CDK_SHIFT_MASK | CDK_CONTROL_MASK))) |
| 680 | return FALSE(0); |
| 681 | |
| 682 | again: |
| 683 | if (ic) |
| 684 | num_bytes = XmbLookupString (ic, &xevent, buffer, buffer_size, &keysym, &status); |
| 685 | else |
| 686 | { |
| 687 | num_bytes = XLookupString (&xevent, buffer, buffer_size, &keysym, NULL((void*)0)); |
| 688 | status = XLookupBoth4; |
| 689 | } |
| 690 | |
| 691 | if (status == XBufferOverflow-1) |
| 692 | { |
| 693 | buffer_size = num_bytes; |
| 694 | if (buffer != static_buffer) |
| 695 | g_free (buffer); |
| 696 | buffer = g_malloc (num_bytes + 1); |
| 697 | goto again; |
| 698 | } |
| 699 | |
| 700 | /* I don't know how we should properly handle XLookupKeysym or XLookupBoth |
| 701 | * here ... do input methods actually change the keysym? we can't really |
| 702 | * feed it back to accelerator processing at this point... |
| 703 | */ |
| 704 | if (status == XLookupChars2 || status == XLookupBoth4) |
| 705 | { |
| 706 | char *result_utf8; |
| 707 | |
| 708 | buffer[num_bytes] = '\0'; |
| 709 | |
| 710 | result_utf8 = mb_to_utf8 (context_xim, buffer); |
| 711 | if (result_utf8) |
| 712 | { |
| 713 | if ((guchar)result_utf8[0] >= 0x20 && |
| 714 | result_utf8[0] != 0x7f) /* Some IM have a nasty habit of converting |
| 715 | * control characters into strings |
| 716 | */ |
| 717 | { |
| 718 | g_signal_emit_by_name (context, "commit", result_utf8); |
| 719 | result = TRUE(!(0)); |
| 720 | } |
| 721 | |
| 722 | g_free (result_utf8); |
| 723 | } |
| 724 | } |
| 725 | |
| 726 | if (buffer != static_buffer) |
| 727 | g_free (buffer); |
| 728 | |
| 729 | return result; |
| 730 | } |
| 731 | |
| 732 | static void |
| 733 | ctk_im_context_xim_focus_in (CtkIMContext *context) |
| 734 | { |
| 735 | CtkIMContextXIM *context_xim = CTK_IM_CONTEXT_XIM (context)((((CtkIMContextXIM*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((context)), ((ctk_type_im_context_xim)))))); |
| 736 | |
| 737 | if (!context_xim->has_focus) |
| 738 | { |
| 739 | XIC ic = ctk_im_context_xim_get_ic (context_xim); |
| 740 | |
| 741 | context_xim->has_focus = TRUE(!(0)); |
| 742 | update_status_window (context_xim); |
| 743 | |
| 744 | if (ic) |
| 745 | XSetICFocus (ic); |
| 746 | } |
| 747 | |
| 748 | return; |
| 749 | } |
| 750 | |
| 751 | static void |
| 752 | ctk_im_context_xim_focus_out (CtkIMContext *context) |
| 753 | { |
| 754 | CtkIMContextXIM *context_xim = CTK_IM_CONTEXT_XIM (context)((((CtkIMContextXIM*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((context)), ((ctk_type_im_context_xim)))))); |
| 755 | |
| 756 | if (context_xim->has_focus) |
| 757 | { |
| 758 | XIC ic = ctk_im_context_xim_get_ic (context_xim); |
| 759 | |
| 760 | context_xim->has_focus = FALSE(0); |
| 761 | update_status_window (context_xim); |
| 762 | |
| 763 | if (ic) |
| 764 | XUnsetICFocus (ic); |
| 765 | } |
| 766 | |
| 767 | return; |
| 768 | } |
| 769 | |
| 770 | static void |
| 771 | ctk_im_context_xim_set_cursor_location (CtkIMContext *context, |
| 772 | CdkRectangle *area) |
| 773 | { |
| 774 | CtkIMContextXIM *context_xim = CTK_IM_CONTEXT_XIM (context)((((CtkIMContextXIM*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((context)), ((ctk_type_im_context_xim)))))); |
| 775 | XIC ic = ctk_im_context_xim_get_ic (context_xim); |
| 776 | |
| 777 | XVaNestedList preedit_attr; |
| 778 | XPoint spot; |
| 779 | |
| 780 | if (!ic) |
| 781 | return; |
| 782 | |
| 783 | spot.x = area->x; |
| 784 | spot.y = area->y + area->height; |
| 785 | |
| 786 | preedit_attr = XVaCreateNestedList (0, |
| 787 | XNSpotLocation"spotLocation", &spot, |
| 788 | NULL((void*)0)); |
| 789 | XSetICValues (ic, |
| 790 | XNPreeditAttributes"preeditAttributes", preedit_attr, |
| 791 | NULL((void*)0)); |
| 792 | XFree(preedit_attr); |
| 793 | |
| 794 | return; |
| 795 | } |
| 796 | |
| 797 | static void |
| 798 | ctk_im_context_xim_set_use_preedit (CtkIMContext *context, |
| 799 | gboolean use_preedit) |
| 800 | { |
| 801 | CtkIMContextXIM *context_xim = CTK_IM_CONTEXT_XIM (context)((((CtkIMContextXIM*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((context)), ((ctk_type_im_context_xim)))))); |
| 802 | |
| 803 | use_preedit = use_preedit != FALSE(0); |
| 804 | |
| 805 | if (context_xim->use_preedit != use_preedit) |
| 806 | { |
| 807 | context_xim->use_preedit = use_preedit; |
| 808 | reinitialize_ic (context_xim); |
| 809 | } |
| 810 | |
| 811 | return; |
| 812 | } |
| 813 | |
| 814 | static void |
| 815 | ctk_im_context_xim_reset (CtkIMContext *context) |
| 816 | { |
| 817 | CtkIMContextXIM *context_xim = CTK_IM_CONTEXT_XIM (context)((((CtkIMContextXIM*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((context)), ((ctk_type_im_context_xim)))))); |
| 818 | XIC ic = ctk_im_context_xim_get_ic (context_xim); |
| 819 | gchar *result; |
| 820 | |
| 821 | /* restore conversion state after resetting ic later */ |
| 822 | XIMPreeditState preedit_state = XIMPreeditUnKnown0L; |
| 823 | XVaNestedList preedit_attr; |
| 824 | gboolean have_preedit_state = FALSE(0); |
| 825 | |
| 826 | if (!ic) |
| 827 | return; |
| 828 | |
| 829 | |
| 830 | if (context_xim->preedit_length == 0) |
| 831 | return; |
| 832 | |
| 833 | preedit_attr = XVaCreateNestedList(0, |
| 834 | XNPreeditState"preeditState", &preedit_state, |
| 835 | NULL((void*)0)); |
| 836 | if (!XGetICValues(ic, |
| 837 | XNPreeditAttributes"preeditAttributes", preedit_attr, |
| 838 | NULL((void*)0))) |
| 839 | have_preedit_state = TRUE(!(0)); |
| 840 | |
| 841 | XFree(preedit_attr); |
| 842 | |
| 843 | result = XmbResetIC (ic); |
| 844 | |
| 845 | preedit_attr = XVaCreateNestedList(0, |
| 846 | XNPreeditState"preeditState", preedit_state, |
| 847 | NULL((void*)0)); |
| 848 | if (have_preedit_state) |
| 849 | XSetICValues(ic, |
| 850 | XNPreeditAttributes"preeditAttributes", preedit_attr, |
| 851 | NULL((void*)0)); |
| 852 | |
| 853 | XFree(preedit_attr); |
| 854 | |
| 855 | if (result) |
| 856 | { |
| 857 | char *result_utf8 = mb_to_utf8 (context_xim, result); |
| 858 | if (result_utf8) |
| 859 | { |
| 860 | g_signal_emit_by_name (context, "commit", result_utf8); |
| 861 | g_free (result_utf8); |
| 862 | } |
| 863 | } |
| 864 | |
| 865 | if (context_xim->preedit_length) |
| 866 | { |
| 867 | context_xim->preedit_length = 0; |
| 868 | g_signal_emit_by_name (context, "preedit-changed"); |
| 869 | } |
| 870 | |
| 871 | XFree (result); |
| 872 | } |
| 873 | |
| 874 | /* Mask of feedback bits that we render |
| 875 | */ |
| 876 | #define FEEDBACK_MASK(1L | (1L<<1)) (XIMReverse1L | XIMUnderline(1L<<1)) |
| 877 | |
| 878 | static void |
| 879 | add_feedback_attr (PangoAttrList *attrs, |
| 880 | const gchar *str, |
| 881 | XIMFeedback feedback, |
| 882 | gint start_pos, |
| 883 | gint end_pos) |
| 884 | { |
| 885 | PangoAttribute *attr; |
| 886 | |
| 887 | gint start_index = g_utf8_offset_to_pointer (str, start_pos) - str; |
| 888 | gint end_index = g_utf8_offset_to_pointer (str, end_pos) - str; |
| 889 | |
| 890 | if (feedback & XIMUnderline(1L<<1)) |
| 891 | { |
| 892 | attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE); |
| 893 | attr->start_index = start_index; |
| 894 | attr->end_index = end_index; |
| 895 | |
| 896 | pango_attr_list_change (attrs, attr); |
| 897 | } |
| 898 | |
| 899 | if (feedback & XIMReverse1L) |
| 900 | { |
| 901 | attr = pango_attr_foreground_new (0xffff, 0xffff, 0xffff); |
| 902 | attr->start_index = start_index; |
| 903 | attr->end_index = end_index; |
| 904 | |
| 905 | pango_attr_list_change (attrs, attr); |
| 906 | |
| 907 | attr = pango_attr_background_new (0, 0, 0); |
| 908 | attr->start_index = start_index; |
| 909 | attr->end_index = end_index; |
| 910 | |
| 911 | pango_attr_list_change (attrs, attr); |
| 912 | } |
| 913 | |
| 914 | if (feedback & ~FEEDBACK_MASK(1L | (1L<<1))) |
| 915 | g_warning ("Unrendered feedback style: %#lx", feedback & ~FEEDBACK_MASK(1L | (1L<<1))); |
| 916 | } |
| 917 | |
| 918 | static void |
| 919 | ctk_im_context_xim_get_preedit_string (CtkIMContext *context, |
| 920 | gchar **str, |
| 921 | PangoAttrList **attrs, |
| 922 | gint *cursor_pos) |
| 923 | { |
| 924 | CtkIMContextXIM *context_xim = CTK_IM_CONTEXT_XIM (context)((((CtkIMContextXIM*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((context)), ((ctk_type_im_context_xim)))))); |
| 925 | gchar *utf8 = g_ucs4_to_utf8 (context_xim->preedit_chars, context_xim->preedit_length, NULL((void*)0), NULL((void*)0), NULL((void*)0)); |
| 926 | |
| 927 | if (attrs) |
| 928 | { |
| 929 | int i; |
| 930 | XIMFeedback last_feedback = 0; |
| 931 | gint start = -1; |
| 932 | |
| 933 | *attrs = pango_attr_list_new (); |
| 934 | |
| 935 | for (i = 0; i < context_xim->preedit_length; i++) |
| 936 | { |
| 937 | XIMFeedback new_feedback = context_xim->feedbacks[i] & FEEDBACK_MASK(1L | (1L<<1)); |
| 938 | if (new_feedback != last_feedback) |
| 939 | { |
| 940 | if (start >= 0) |
| 941 | add_feedback_attr (*attrs, utf8, last_feedback, start, i); |
| 942 | |
| 943 | last_feedback = new_feedback; |
| 944 | start = i; |
| 945 | } |
| 946 | } |
| 947 | |
| 948 | if (start >= 0) |
| 949 | add_feedback_attr (*attrs, utf8, last_feedback, start, i); |
| 950 | } |
| 951 | |
| 952 | if (str) |
| 953 | *str = utf8; |
| 954 | else |
| 955 | g_free (utf8); |
| 956 | |
| 957 | if (cursor_pos) |
| 958 | *cursor_pos = context_xim->preedit_cursor; |
| 959 | } |
| 960 | |
| 961 | static int |
| 962 | preedit_start_callback (XIC xic G_GNUC_UNUSED__attribute__ ((__unused__)), |
| 963 | XPointer client_data, |
| 964 | XPointer call_data G_GNUC_UNUSED__attribute__ ((__unused__))) |
| 965 | { |
| 966 | CtkIMContext *context = CTK_IM_CONTEXT (client_data)((((CtkIMContext*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((client_data)), ((ctk_im_context_get_type ())))))); |
| 967 | CtkIMContextXIM *context_xim = CTK_IM_CONTEXT_XIM (context)((((CtkIMContextXIM*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((context)), ((ctk_type_im_context_xim)))))); |
| 968 | |
| 969 | if (!context_xim->finalizing) |
| 970 | g_signal_emit_by_name (context, "preedit-start"); |
| 971 | |
| 972 | return -1; /* No length limit */ |
| 973 | } |
| 974 | |
| 975 | static void |
| 976 | preedit_done_callback (XIC xic G_GNUC_UNUSED__attribute__ ((__unused__)), |
| 977 | XPointer client_data, |
| 978 | XPointer call_data G_GNUC_UNUSED__attribute__ ((__unused__))) |
| 979 | { |
| 980 | CtkIMContext *context = CTK_IM_CONTEXT (client_data)((((CtkIMContext*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((client_data)), ((ctk_im_context_get_type ())))))); |
| 981 | CtkIMContextXIM *context_xim = CTK_IM_CONTEXT_XIM (context)((((CtkIMContextXIM*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((context)), ((ctk_type_im_context_xim)))))); |
| 982 | |
| 983 | if (context_xim->preedit_length) |
| 984 | { |
| 985 | context_xim->preedit_length = 0; |
| 986 | if (!context_xim->finalizing) |
| 987 | g_signal_emit_by_name (context_xim, "preedit-changed"); |
| 988 | } |
| 989 | |
| 990 | if (!context_xim->finalizing) |
| 991 | g_signal_emit_by_name (context, "preedit-end"); |
| 992 | } |
| 993 | |
| 994 | static gint |
| 995 | xim_text_to_utf8 (CtkIMContextXIM *context, XIMText *xim_text, gchar **text) |
| 996 | { |
| 997 | gint text_length = 0; |
| 998 | GError *error = NULL((void*)0); |
| 999 | gchar *result = NULL((void*)0); |
| 1000 | |
| 1001 | if (xim_text && xim_text->string.multi_byte) |
| 1002 | { |
| 1003 | if (xim_text->encoding_is_wchar) |
| 1004 | { |
| 1005 | g_warning ("Wide character return from Xlib not currently supported"); |
| 1006 | *text = NULL((void*)0); |
| 1007 | return 0; |
| 1008 | } |
| 1009 | |
| 1010 | if (strcmp (context->mb_charset, "UTF-8") == 0) |
| 1011 | result = g_strdup (xim_text->string.multi_byte)g_strdup_inline (xim_text->string.multi_byte); |
| 1012 | else |
| 1013 | result = g_convert (xim_text->string.multi_byte, |
| 1014 | -1, |
| 1015 | "UTF-8", |
| 1016 | context->mb_charset, |
| 1017 | NULL((void*)0), NULL((void*)0), &error); |
| 1018 | |
| 1019 | if (result) |
| 1020 | { |
| 1021 | text_length = g_utf8_strlen (result, -1); |
| 1022 | |
| 1023 | if (text_length != xim_text->length) |
| 1024 | { |
| 1025 | g_warning ("Size mismatch when converting text from input method: supplied length = %d\n, result length = %d", xim_text->length, text_length); |
| 1026 | } |
| 1027 | } |
| 1028 | else |
| 1029 | { |
| 1030 | g_warning ("Error converting text from IM to UCS-4: %s", error->message); |
| 1031 | g_error_free (error); |
| 1032 | |
| 1033 | *text = NULL((void*)0); |
| 1034 | return 0; |
| 1035 | } |
| 1036 | |
| 1037 | *text = result; |
| 1038 | return text_length; |
| 1039 | } |
| 1040 | else |
| 1041 | { |
| 1042 | *text = NULL((void*)0); |
| 1043 | return 0; |
| 1044 | } |
| 1045 | } |
| 1046 | |
| 1047 | static void |
| 1048 | preedit_draw_callback (XIC xic G_GNUC_UNUSED__attribute__ ((__unused__)), |
| 1049 | XPointer client_data, |
| 1050 | XIMPreeditDrawCallbackStruct *call_data) |
| 1051 | { |
| 1052 | CtkIMContextXIM *context = CTK_IM_CONTEXT_XIM (client_data)((((CtkIMContextXIM*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((client_data)), ((ctk_type_im_context_xim)))))); |
| 1053 | |
| 1054 | XIMText *new_xim_text = call_data->text; |
| 1055 | gint new_text_length; |
| 1056 | gunichar *new_text = NULL((void*)0); |
| 1057 | gint i; |
| 1058 | gint diff; |
| 1059 | gint new_length; |
| 1060 | gchar *tmp; |
| 1061 | |
| 1062 | gint chg_first = CLAMP (call_data->chg_first, 0, context->preedit_length)(((call_data->chg_first) > (context->preedit_length) ) ? (context->preedit_length) : (((call_data->chg_first ) < (0)) ? (0) : (call_data->chg_first))); |
| 1063 | gint chg_length = CLAMP (call_data->chg_length, 0, context->preedit_length - chg_first)(((call_data->chg_length) > (context->preedit_length - chg_first)) ? (context->preedit_length - chg_first) : ( ((call_data->chg_length) < (0)) ? (0) : (call_data-> chg_length))); |
| 1064 | |
| 1065 | context->preedit_cursor = call_data->caret; |
| 1066 | |
| 1067 | if (chg_first != call_data->chg_first || chg_length != call_data->chg_length) |
| 1068 | g_warning ("Invalid change to preedit string, first=%d length=%d (orig length == %d)", |
| 1069 | call_data->chg_first, call_data->chg_length, context->preedit_length); |
| 1070 | |
| 1071 | new_text_length = xim_text_to_utf8 (context, new_xim_text, &tmp); |
| 1072 | if (tmp) |
| 1073 | { |
| 1074 | new_text = g_utf8_to_ucs4_fast (tmp, -1, NULL((void*)0)); |
| 1075 | g_free (tmp); |
| 1076 | } |
| 1077 | |
| 1078 | diff = new_text_length - chg_length; |
| 1079 | new_length = context->preedit_length + diff; |
| 1080 | |
| 1081 | if (new_length > context->preedit_size) |
| 1082 | { |
| 1083 | context->preedit_size = new_length; |
| 1084 | context->preedit_chars = g_renew (gunichar, context->preedit_chars, new_length)((gunichar *) g_realloc_n (context->preedit_chars, (new_length ), sizeof (gunichar))); |
| 1085 | context->feedbacks = g_renew (XIMFeedback, context->feedbacks, new_length)((XIMFeedback *) g_realloc_n (context->feedbacks, (new_length ), sizeof (XIMFeedback))); |
| 1086 | } |
| 1087 | |
| 1088 | if (diff < 0) |
| 1089 | { |
| 1090 | for (i = chg_first + chg_length ; i < context->preedit_length; i++) |
| 1091 | { |
| 1092 | context->preedit_chars[i + diff] = context->preedit_chars[i]; |
| 1093 | context->feedbacks[i + diff] = context->feedbacks[i]; |
| 1094 | } |
| 1095 | } |
| 1096 | else |
| 1097 | { |
| 1098 | for (i = context->preedit_length - 1; i >= chg_first + chg_length ; i--) |
| 1099 | { |
| 1100 | context->preedit_chars[i + diff] = context->preedit_chars[i]; |
| 1101 | context->feedbacks[i + diff] = context->feedbacks[i]; |
| 1102 | } |
| 1103 | } |
| 1104 | |
| 1105 | for (i = 0; i < new_text_length; i++) |
| 1106 | { |
| 1107 | context->preedit_chars[chg_first + i] = new_text[i]; |
| 1108 | context->feedbacks[chg_first + i] = new_xim_text->feedback[i]; |
| 1109 | } |
| 1110 | |
| 1111 | context->preedit_length += diff; |
| 1112 | |
| 1113 | g_free (new_text); |
| 1114 | |
| 1115 | if (!context->finalizing) |
| 1116 | g_signal_emit_by_name (context, "preedit-changed"); |
| 1117 | } |
| 1118 | |
| 1119 | |
| 1120 | static void |
| 1121 | preedit_caret_callback (XIC xic G_GNUC_UNUSED__attribute__ ((__unused__)), |
| 1122 | XPointer client_data, |
| 1123 | XIMPreeditCaretCallbackStruct *call_data) |
| 1124 | { |
| 1125 | CtkIMContextXIM *context = CTK_IM_CONTEXT_XIM (client_data)((((CtkIMContextXIM*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((client_data)), ((ctk_type_im_context_xim)))))); |
| 1126 | |
| 1127 | if (call_data->direction == XIMAbsolutePosition) |
| 1128 | { |
| 1129 | context->preedit_cursor = call_data->position; |
| 1130 | if (!context->finalizing) |
| 1131 | g_signal_emit_by_name (context, "preedit-changed"); |
| 1132 | } |
| 1133 | else |
| 1134 | { |
| 1135 | g_warning ("Caret movement command: %d %d %d not supported", |
| 1136 | call_data->position, call_data->direction, call_data->style); |
| 1137 | } |
| 1138 | } |
| 1139 | |
| 1140 | static void |
| 1141 | status_start_callback (XIC xic G_GNUC_UNUSED__attribute__ ((__unused__)), |
| 1142 | XPointer client_data G_GNUC_UNUSED__attribute__ ((__unused__)), |
| 1143 | XPointer call_data G_GNUC_UNUSED__attribute__ ((__unused__))) |
| 1144 | { |
| 1145 | return; |
| 1146 | } |
| 1147 | |
| 1148 | static void |
| 1149 | status_done_callback (XIC xic G_GNUC_UNUSED__attribute__ ((__unused__)), |
| 1150 | XPointer client_data G_GNUC_UNUSED__attribute__ ((__unused__)), |
| 1151 | XPointer call_data G_GNUC_UNUSED__attribute__ ((__unused__))) |
| 1152 | { |
| 1153 | return; |
| 1154 | } |
| 1155 | |
| 1156 | static void |
| 1157 | status_draw_callback (XIC xic G_GNUC_UNUSED__attribute__ ((__unused__)), |
| 1158 | XPointer client_data, |
| 1159 | XIMStatusDrawCallbackStruct *call_data) |
| 1160 | { |
| 1161 | CtkIMContextXIM *context = CTK_IM_CONTEXT_XIM (client_data)((((CtkIMContextXIM*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((client_data)), ((ctk_type_im_context_xim)))))); |
| 1162 | |
| 1163 | if (call_data->type == XIMTextType) |
| 1164 | { |
| 1165 | gchar *text; |
| 1166 | xim_text_to_utf8 (context, call_data->data.text, &text); |
| 1167 | |
| 1168 | if (context->status_window) |
| 1169 | status_window_set_text (context->status_window, text ? text : ""); |
| 1170 | } |
| 1171 | else /* bitmap */ |
| 1172 | { |
| 1173 | g_print ("Status drawn with bitmap - id = %#lx\n", call_data->data.bitmap); |
| 1174 | } |
| 1175 | } |
| 1176 | |
| 1177 | static void |
| 1178 | string_conversion_callback (XIC xic G_GNUC_UNUSED__attribute__ ((__unused__)), |
| 1179 | XPointer client_data, |
| 1180 | XPointer call_data) |
| 1181 | { |
| 1182 | CtkIMContextXIM *context_xim; |
| 1183 | XIMStringConversionCallbackStruct *conv_data; |
| 1184 | gchar *surrounding; |
| 1185 | gint cursor_index; |
| 1186 | |
| 1187 | context_xim = (CtkIMContextXIM *)client_data; |
Casting a non-structure type to a structure type and accessing a field can lead to memory access errors or data corruption | |
| 1188 | conv_data = (XIMStringConversionCallbackStruct *)call_data; |
| 1189 | |
| 1190 | if (ctk_im_context_get_surrounding ((CtkIMContext *)context_xim, |
| 1191 | &surrounding, &cursor_index)) |
| 1192 | { |
| 1193 | gchar *text = NULL((void*)0); |
| 1194 | gsize text_len = 0; |
| 1195 | gint subst_offset = 0, subst_nchars = 0; |
| 1196 | gint i; |
| 1197 | gchar *p = surrounding + cursor_index, *q; |
| 1198 | gshort position = (gshort)conv_data->position; |
| 1199 | |
| 1200 | if (position > 0) |
| 1201 | { |
| 1202 | for (i = position; i > 0 && *p; --i) |
| 1203 | p = g_utf8_next_char (p)((p) + g_utf8_skip[*(const guchar *)(p)]); |
| 1204 | if (i > 0) |
| 1205 | return; |
| 1206 | } |
| 1207 | /* According to X11R6.4 Xlib - C Library Reference Manual |
| 1208 | * section 13.5.7.3 String Conversion Callback, |
| 1209 | * XIMStringConversionPosition is starting position _relative_ |
| 1210 | * to current client's cursor position. So it should be able |
| 1211 | * to be negative, or referring to a position before the cursor |
| 1212 | * would be impossible. But current X protocol defines this as |
| 1213 | * unsigned short. So, compiler may warn about the value range |
| 1214 | * here. We hope the X protocol is fixed soon. |
| 1215 | */ |
| 1216 | else if (position < 0) |
| 1217 | { |
| 1218 | for (i = position; i < 0 && p > surrounding; ++i) |
| 1219 | p = g_utf8_prev_char (p); |
| 1220 | if (i < 0) |
| 1221 | return; |
| 1222 | } |
| 1223 | |
| 1224 | switch (conv_data->direction) |
| 1225 | { |
| 1226 | case XIMForwardChar: |
| 1227 | for (i = conv_data->factor, q = p; i > 0 && *q; --i) |
| 1228 | q = g_utf8_next_char (q)((q) + g_utf8_skip[*(const guchar *)(q)]); |
| 1229 | if (i > 0) |
| 1230 | break; |
| 1231 | text = g_locale_from_utf8 (p, q - p, NULL((void*)0), &text_len, NULL((void*)0)); |
| 1232 | subst_offset = position; |
| 1233 | subst_nchars = conv_data->factor; |
| 1234 | break; |
| 1235 | |
| 1236 | case XIMBackwardChar: |
| 1237 | for (i = conv_data->factor, q = p; i > 0 && q > surrounding; --i) |
| 1238 | q = g_utf8_prev_char (q); |
| 1239 | if (i > 0) |
| 1240 | break; |
| 1241 | text = g_locale_from_utf8 (q, p - q, NULL((void*)0), &text_len, NULL((void*)0)); |
| 1242 | subst_offset = position - conv_data->factor; |
| 1243 | subst_nchars = conv_data->factor; |
| 1244 | break; |
| 1245 | |
| 1246 | case XIMForwardWord: |
| 1247 | case XIMBackwardWord: |
| 1248 | case XIMCaretUp: |
| 1249 | case XIMCaretDown: |
| 1250 | case XIMNextLine: |
| 1251 | case XIMPreviousLine: |
| 1252 | case XIMLineStart: |
| 1253 | case XIMLineEnd: |
| 1254 | case XIMAbsolutePosition: |
| 1255 | case XIMDontChange: |
| 1256 | default: |
| 1257 | break; |
| 1258 | } |
| 1259 | /* block out any failure happenning to "text", including conversion */ |
| 1260 | if (text) |
| 1261 | { |
| 1262 | conv_data->text = (XIMStringConversionText *) |
| 1263 | malloc (sizeof (XIMStringConversionText)); |
| 1264 | if (conv_data->text) |
| 1265 | { |
| 1266 | conv_data->text->length = text_len; |
| 1267 | conv_data->text->feedback = NULL((void*)0); |
| 1268 | conv_data->text->encoding_is_wchar = False0; |
| 1269 | conv_data->text->string.mbs = (char *)malloc (text_len); |
| 1270 | if (conv_data->text->string.mbs) |
| 1271 | memcpy (conv_data->text->string.mbs, text, text_len); |
| 1272 | else |
| 1273 | { |
| 1274 | free (conv_data->text); |
| 1275 | conv_data->text = NULL((void*)0); |
| 1276 | } |
| 1277 | } |
| 1278 | |
| 1279 | g_free (text); |
| 1280 | } |
| 1281 | if (conv_data->operation == XIMStringConversionSubstitution(0x0001) |
| 1282 | && subst_nchars > 0) |
| 1283 | { |
| 1284 | ctk_im_context_delete_surrounding ((CtkIMContext *)context_xim, |
| 1285 | subst_offset, subst_nchars); |
| 1286 | } |
| 1287 | |
| 1288 | g_free (surrounding); |
| 1289 | } |
| 1290 | } |
| 1291 | |
| 1292 | |
| 1293 | static XVaNestedList |
| 1294 | set_preedit_callback (CtkIMContextXIM *context_xim) |
| 1295 | { |
| 1296 | context_xim->preedit_start_callback.client_data = (XPointer)context_xim; |
| 1297 | context_xim->preedit_start_callback.callback = (XIMProc)preedit_start_callback; |
| 1298 | context_xim->preedit_done_callback.client_data = (XPointer)context_xim; |
| 1299 | context_xim->preedit_done_callback.callback = (XIMProc)preedit_done_callback; |
| 1300 | context_xim->preedit_draw_callback.client_data = (XPointer)context_xim; |
| 1301 | context_xim->preedit_draw_callback.callback = (XIMProc)preedit_draw_callback; |
| 1302 | context_xim->preedit_caret_callback.client_data = (XPointer)context_xim; |
| 1303 | context_xim->preedit_caret_callback.callback = (XIMProc)preedit_caret_callback; |
| 1304 | return XVaCreateNestedList (0, |
| 1305 | XNPreeditStartCallback"preeditStartCallback", &context_xim->preedit_start_callback, |
| 1306 | XNPreeditDoneCallback"preeditDoneCallback", &context_xim->preedit_done_callback, |
| 1307 | XNPreeditDrawCallback"preeditDrawCallback", &context_xim->preedit_draw_callback, |
| 1308 | XNPreeditCaretCallback"preeditCaretCallback", &context_xim->preedit_caret_callback, |
| 1309 | NULL((void*)0)); |
| 1310 | } |
| 1311 | |
| 1312 | static XVaNestedList |
| 1313 | set_status_callback (CtkIMContextXIM *context_xim) |
| 1314 | { |
| 1315 | context_xim->status_start_callback.client_data = (XPointer)context_xim; |
| 1316 | context_xim->status_start_callback.callback = (XIMProc)status_start_callback; |
| 1317 | context_xim->status_done_callback.client_data = (XPointer)context_xim; |
| 1318 | context_xim->status_done_callback.callback = (XIMProc)status_done_callback; |
| 1319 | context_xim->status_draw_callback.client_data = (XPointer)context_xim; |
| 1320 | context_xim->status_draw_callback.callback = (XIMProc)status_draw_callback; |
| 1321 | |
| 1322 | return XVaCreateNestedList (0, |
| 1323 | XNStatusStartCallback"statusStartCallback", &context_xim->status_start_callback, |
| 1324 | XNStatusDoneCallback"statusDoneCallback", &context_xim->status_done_callback, |
| 1325 | XNStatusDrawCallback"statusDrawCallback", &context_xim->status_draw_callback, |
| 1326 | NULL((void*)0)); |
| 1327 | } |
| 1328 | |
| 1329 | |
| 1330 | static void |
| 1331 | set_string_conversion_callback (CtkIMContextXIM *context_xim, XIC xic) |
| 1332 | { |
| 1333 | if (!context_xim->im_info->supports_string_conversion) |
| 1334 | return; |
| 1335 | |
| 1336 | context_xim->string_conversion_callback.client_data = (XPointer)context_xim; |
| 1337 | context_xim->string_conversion_callback.callback = (XIMProc)string_conversion_callback; |
| 1338 | |
| 1339 | XSetICValues (xic, |
| 1340 | XNStringConversionCallback"stringConversionCallback", |
| 1341 | (XPointer)&context_xim->string_conversion_callback, |
| 1342 | NULL((void*)0)); |
| 1343 | } |
| 1344 | |
| 1345 | static XIC |
| 1346 | ctk_im_context_xim_get_ic (CtkIMContextXIM *context_xim) |
| 1347 | { |
| 1348 | if (context_xim->im_info == NULL((void*)0) || context_xim->im_info->im == NULL((void*)0)) |
| 1349 | return NULL((void*)0); |
| 1350 | |
| 1351 | if (context_xim->client_window == NULL((void*)0)) |
| 1352 | return NULL((void*)0); |
| 1353 | |
| 1354 | if (!context_xim->ic) |
| 1355 | { |
| 1356 | const char *name1 = NULL((void*)0); |
| 1357 | XVaNestedList list1 = NULL((void*)0); |
| 1358 | const char *name2 = NULL((void*)0); |
| 1359 | XVaNestedList list2 = NULL((void*)0); |
| 1360 | XIMStyle im_style = 0; |
| 1361 | XIC xic = NULL((void*)0); |
| 1362 | |
| 1363 | if (context_xim->use_preedit && |
| 1364 | (context_xim->im_info->style & PREEDIT_MASK(0x0002L | 0x0004L | 0x0001L | 0x0008L | 0x0010L)) == XIMPreeditCallbacks0x0002L) |
| 1365 | { |
| 1366 | im_style |= XIMPreeditCallbacks0x0002L; |
| 1367 | name1 = XNPreeditAttributes"preeditAttributes"; |
| 1368 | list1 = set_preedit_callback (context_xim); |
| 1369 | } |
| 1370 | else if ((context_xim->im_info->style & PREEDIT_MASK(0x0002L | 0x0004L | 0x0001L | 0x0008L | 0x0010L)) == XIMPreeditNone0x0010L) |
| 1371 | im_style |= XIMPreeditNone0x0010L; |
| 1372 | else |
| 1373 | im_style |= XIMPreeditNothing0x0008L; |
| 1374 | |
| 1375 | if ((context_xim->im_info->style & STATUS_MASK(0x0200L | 0x0100L | 0x0400L | 0x0800L)) == XIMStatusCallbacks0x0200L) |
| 1376 | { |
| 1377 | im_style |= XIMStatusCallbacks0x0200L; |
| 1378 | if (name1 == NULL((void*)0)) |
| 1379 | { |
| 1380 | name1 = XNStatusAttributes"statusAttributes"; |
| 1381 | list1 = set_status_callback (context_xim); |
| 1382 | } |
| 1383 | else |
| 1384 | { |
| 1385 | name2 = XNStatusAttributes"statusAttributes"; |
| 1386 | list2 = set_status_callback (context_xim); |
| 1387 | } |
| 1388 | } |
| 1389 | else if ((context_xim->im_info->style & STATUS_MASK(0x0200L | 0x0100L | 0x0400L | 0x0800L)) == XIMStatusNone0x0800L) |
| 1390 | im_style |= XIMStatusNone0x0800L; |
| 1391 | else |
| 1392 | im_style |= XIMStatusNothing0x0400L; |
| 1393 | |
| 1394 | xic = XCreateIC (context_xim->im_info->im, |
| 1395 | XNInputStyle"inputStyle", im_style, |
| 1396 | XNClientWindow"clientWindow", CDK_WINDOW_XID (context_xim->client_window)(cdk_x11_window_get_xid (context_xim->client_window)), |
| 1397 | name1, list1, |
| 1398 | name2, list2, |
| 1399 | NULL((void*)0)); |
| 1400 | if (list1) |
| 1401 | XFree (list1); |
| 1402 | if (list2) |
| 1403 | XFree (list2); |
| 1404 | |
| 1405 | if (xic) |
| 1406 | { |
| 1407 | /* Don't filter key released events with XFilterEvents unless |
| 1408 | * input methods ask for. This is a workaround for Solaris input |
| 1409 | * method bug in C and European locales. It doubles each key |
| 1410 | * stroke if both key pressed and released events are filtered. |
| 1411 | * (bugzilla #81759) |
| 1412 | */ |
| 1413 | gulong mask = 0xaaaaaaaa; |
| 1414 | XGetICValues (xic, |
| 1415 | XNFilterEvents"filterEvents", &mask, |
| 1416 | NULL((void*)0)); |
| 1417 | context_xim->filter_key_release = (mask & KeyReleaseMask(1L<<1)) != 0; |
| 1418 | set_string_conversion_callback (context_xim, xic); |
| 1419 | } |
| 1420 | |
| 1421 | context_xim->ic = xic; |
| 1422 | |
| 1423 | update_status_window (context_xim); |
| 1424 | |
| 1425 | if (xic && context_xim->has_focus) |
| 1426 | XSetICFocus (xic); |
| 1427 | } |
| 1428 | return context_xim->ic; |
| 1429 | } |
| 1430 | |
| 1431 | /***************************************************************** |
| 1432 | * Status Window handling |
| 1433 | * |
| 1434 | * A status window is a small window attached to the toplevel |
| 1435 | * that is used to display information to the user about the |
| 1436 | * current input operation. |
| 1437 | * |
| 1438 | * We claim the toplevel's status window for an input context if: |
| 1439 | * |
| 1440 | * A) The input context has a toplevel |
| 1441 | * B) The input context has the focus |
| 1442 | * C) The input context has an XIC associated with it |
| 1443 | * |
| 1444 | * Tracking A) and C) is pretty reliable since we |
| 1445 | * compute A) and create the XIC for C) ourselves. |
| 1446 | * For B) we basically have to depend on our callers |
| 1447 | * calling ::focus-in and ::focus-out at the right time. |
| 1448 | * |
| 1449 | * The toplevel is computed by walking up the CdkWindow |
| 1450 | * hierarchy from context->client_window until we find a |
| 1451 | * window that is owned by some widget, and then calling |
| 1452 | * ctk_widget_get_toplevel() on that widget. This should |
| 1453 | * handle both cases where we might have CdkWindows without widgets, |
| 1454 | * and cases where CtkWidgets have strange window hierarchies |
| 1455 | * (like a torn off CtkHandleBox.) |
| 1456 | * |
| 1457 | * The status window is visible if and only if there is text |
| 1458 | * for it; whenever a new CtkIMContextXIM claims the status |
| 1459 | * window, we blank out any existing text. We actually only |
| 1460 | * create a CtkWindow for the status window the first time |
| 1461 | * it is shown; this is an important optimization when we are |
| 1462 | * using XIM with something like a simple compose-key input |
| 1463 | * method that never needs a status window. |
| 1464 | *****************************************************************/ |
| 1465 | |
| 1466 | /* Called when we no longer need a status window |
| 1467 | */ |
| 1468 | static void |
| 1469 | disclaim_status_window (CtkIMContextXIM *context_xim) |
| 1470 | { |
| 1471 | if (context_xim->status_window) |
| 1472 | { |
| 1473 | g_assert (context_xim->status_window->context == context_xim)do { if (context_xim->status_window->context == context_xim ) ; else g_assertion_message_expr (((gchar*) 0), "ctkimcontextxim.c" , 1473, ((const char*) (__func__)), "context_xim->status_window->context == context_xim" ); } while (0); |
| 1474 | |
| 1475 | status_window_set_text (context_xim->status_window, ""); |
| 1476 | |
| 1477 | context_xim->status_window->context = NULL((void*)0); |
| 1478 | context_xim->status_window = NULL((void*)0); |
| 1479 | } |
| 1480 | } |
| 1481 | |
| 1482 | /* Called when we need a status window |
| 1483 | */ |
| 1484 | static void |
| 1485 | claim_status_window (CtkIMContextXIM *context_xim) |
| 1486 | { |
| 1487 | if (!context_xim->status_window && context_xim->client_widget) |
| 1488 | { |
| 1489 | CtkWidget *toplevel = ctk_widget_get_toplevel (context_xim->client_widget); |
| 1490 | if (toplevel && ctk_widget_is_toplevel (toplevel)) |
| 1491 | { |
| 1492 | StatusWindow *status_window = status_window_get (toplevel); |
| 1493 | |
| 1494 | if (status_window->context) |
| 1495 | disclaim_status_window (status_window->context); |
| 1496 | |
| 1497 | status_window->context = context_xim; |
| 1498 | context_xim->status_window = status_window; |
| 1499 | } |
| 1500 | } |
| 1501 | } |
| 1502 | |
| 1503 | /* Basic call made whenever something changed that might cause |
| 1504 | * us to need, or not to need a status window. |
| 1505 | */ |
| 1506 | static void |
| 1507 | update_status_window (CtkIMContextXIM *context_xim) |
| 1508 | { |
| 1509 | if (context_xim->ic && context_xim->in_toplevel && context_xim->has_focus) |
| 1510 | claim_status_window (context_xim); |
| 1511 | else |
| 1512 | disclaim_status_window (context_xim); |
| 1513 | } |
| 1514 | |
| 1515 | /* Updates the in_toplevel flag for @context_xim |
| 1516 | */ |
| 1517 | static void |
| 1518 | update_in_toplevel (CtkIMContextXIM *context_xim) |
| 1519 | { |
| 1520 | if (context_xim->client_widget) |
| 1521 | { |
| 1522 | CtkWidget *toplevel = ctk_widget_get_toplevel (context_xim->client_widget); |
| 1523 | |
| 1524 | context_xim->in_toplevel = (toplevel && ctk_widget_is_toplevel (toplevel)); |
| 1525 | } |
| 1526 | else |
| 1527 | context_xim->in_toplevel = FALSE(0); |
| 1528 | |
| 1529 | /* Some paranoia, in case we don't get a focus out */ |
| 1530 | if (!context_xim->in_toplevel) |
| 1531 | context_xim->has_focus = FALSE(0); |
| 1532 | |
| 1533 | update_status_window (context_xim); |
| 1534 | } |
| 1535 | |
| 1536 | /* Callback when @widget's toplevel changes. It will always |
| 1537 | * change from NULL to a window, or a window to NULL; |
| 1538 | * we use that intermediate NULL state to make sure |
| 1539 | * that we disclaim the toplevel status window for the old |
| 1540 | * window. |
| 1541 | */ |
| 1542 | static void |
| 1543 | on_client_widget_hierarchy_changed (CtkWidget *widget G_GNUC_UNUSED__attribute__ ((__unused__)), |
| 1544 | CtkWidget *old_toplevel G_GNUC_UNUSED__attribute__ ((__unused__)), |
| 1545 | CtkIMContextXIM *context_xim) |
| 1546 | { |
| 1547 | update_in_toplevel (context_xim); |
| 1548 | } |
| 1549 | |
| 1550 | /* Finds the CtkWidget that owns the window, or if none, the |
| 1551 | * widget owning the nearest parent that has a widget. |
| 1552 | */ |
| 1553 | static CtkWidget * |
| 1554 | widget_for_window (CdkWindow *window) |
| 1555 | { |
| 1556 | while (window) |
| 1557 | { |
| 1558 | gpointer user_data; |
| 1559 | cdk_window_get_user_data (window, &user_data); |
| 1560 | if (user_data) |
| 1561 | return user_data; |
| 1562 | |
| 1563 | window = cdk_window_get_parent (window); |
| 1564 | } |
| 1565 | |
| 1566 | return NULL((void*)0); |
| 1567 | } |
| 1568 | |
| 1569 | /* Called when context_xim->client_window changes; takes care of |
| 1570 | * removing and/or setting up our watches for the toplevel |
| 1571 | */ |
| 1572 | static void |
| 1573 | update_client_widget (CtkIMContextXIM *context_xim) |
| 1574 | { |
| 1575 | CtkWidget *new_client_widget = widget_for_window (context_xim->client_window); |
| 1576 | |
| 1577 | if (new_client_widget != context_xim->client_widget) |
| 1578 | { |
| 1579 | if (context_xim->client_widget) |
| 1580 | { |
| 1581 | g_signal_handlers_disconnect_by_func (context_xim->client_widget,g_signal_handlers_disconnect_matched ((context_xim->client_widget ), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA ), 0, 0, ((void*)0), (((GCallback) (on_client_widget_hierarchy_changed ))), (context_xim)) |
| 1582 | G_CALLBACK (on_client_widget_hierarchy_changed),g_signal_handlers_disconnect_matched ((context_xim->client_widget ), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA ), 0, 0, ((void*)0), (((GCallback) (on_client_widget_hierarchy_changed ))), (context_xim)) |
| 1583 | context_xim)g_signal_handlers_disconnect_matched ((context_xim->client_widget ), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA ), 0, 0, ((void*)0), (((GCallback) (on_client_widget_hierarchy_changed ))), (context_xim)); |
| 1584 | } |
| 1585 | context_xim->client_widget = new_client_widget; |
| 1586 | if (context_xim->client_widget) |
| 1587 | { |
| 1588 | g_signal_connect (context_xim->client_widget, "hierarchy-changed",g_signal_connect_data ((context_xim->client_widget), ("hierarchy-changed" ), (((GCallback) (on_client_widget_hierarchy_changed))), (context_xim ), ((void*)0), (GConnectFlags) 0) |
| 1589 | G_CALLBACK (on_client_widget_hierarchy_changed),g_signal_connect_data ((context_xim->client_widget), ("hierarchy-changed" ), (((GCallback) (on_client_widget_hierarchy_changed))), (context_xim ), ((void*)0), (GConnectFlags) 0) |
| 1590 | context_xim)g_signal_connect_data ((context_xim->client_widget), ("hierarchy-changed" ), (((GCallback) (on_client_widget_hierarchy_changed))), (context_xim ), ((void*)0), (GConnectFlags) 0); |
| 1591 | } |
| 1592 | |
| 1593 | update_in_toplevel (context_xim); |
| 1594 | } |
| 1595 | } |
| 1596 | |
| 1597 | /* Called when the toplevel is destroyed; frees the status window |
| 1598 | */ |
| 1599 | static void |
| 1600 | on_status_toplevel_destroy (CtkWidget *toplevel G_GNUC_UNUSED__attribute__ ((__unused__)), |
| 1601 | StatusWindow *status_window) |
| 1602 | { |
| 1603 | status_window_free (status_window); |
| 1604 | } |
| 1605 | |
| 1606 | /* Called when the screen for the toplevel changes; updates the |
| 1607 | * screen for the status window to match. |
| 1608 | */ |
| 1609 | static void |
| 1610 | on_status_toplevel_notify_screen (CtkWindow *toplevel, |
| 1611 | GParamSpec *pspec G_GNUC_UNUSED__attribute__ ((__unused__)), |
| 1612 | StatusWindow *status_window) |
| 1613 | { |
| 1614 | if (status_window->window) |
| 1615 | ctk_window_set_screen (CTK_WINDOW (status_window->window)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((status_window->window)), ((ctk_window_get_type ())))) )), |
| 1616 | ctk_widget_get_screen (CTK_WIDGET (toplevel)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((toplevel)), ((ctk_widget_get_type ())))))))); |
| 1617 | } |
| 1618 | |
| 1619 | /* Called when the toplevel window is moved; updates the position of |
| 1620 | * the status window to follow it. |
| 1621 | */ |
| 1622 | static gboolean |
| 1623 | on_status_toplevel_configure (CtkWidget *toplevel, |
| 1624 | CdkEventConfigure *event G_GNUC_UNUSED__attribute__ ((__unused__)), |
| 1625 | StatusWindow *status_window) |
| 1626 | { |
| 1627 | CdkRectangle rect; |
| 1628 | CtkRequisition requisition; |
| 1629 | gint y; |
| 1630 | gint height; |
| 1631 | |
| 1632 | if (status_window->window) |
| 1633 | { |
| 1634 | G_GNUC_BEGIN_IGNORE_DEPRECATIONSclang diagnostic push
clang diagnostic ignored "-Wdeprecated-declarations" |
| 1635 | height = cdk_screen_get_height (ctk_widget_get_screen (toplevel)); |
| 1636 | G_GNUC_END_IGNORE_DEPRECATIONSclang diagnostic pop |
| 1637 | |
| 1638 | cdk_window_get_frame_extents (ctk_widget_get_window (toplevel), |
| 1639 | &rect); |
| 1640 | ctk_widget_get_preferred_size ( (status_window->window), |
| 1641 | &requisition, NULL((void*)0)); |
| 1642 | |
| 1643 | if (rect.y + rect.height + requisition.height < height) |
| 1644 | y = rect.y + rect.height; |
| 1645 | else |
| 1646 | y = height - requisition.height; |
| 1647 | |
| 1648 | ctk_window_move (CTK_WINDOW (status_window->window)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((status_window->window)), ((ctk_window_get_type ())))) )), rect.x, y); |
| 1649 | } |
| 1650 | |
| 1651 | return FALSE(0); |
| 1652 | } |
| 1653 | |
| 1654 | /* Frees a status window and removes its link from the status_windows list |
| 1655 | */ |
| 1656 | static void |
| 1657 | status_window_free (StatusWindow *status_window) |
| 1658 | { |
| 1659 | status_windows = g_slist_remove (status_windows, status_window); |
| 1660 | |
| 1661 | if (status_window->context) |
| 1662 | status_window->context->status_window = NULL((void*)0); |
| 1663 | |
| 1664 | g_signal_handlers_disconnect_by_func (status_window->toplevel,g_signal_handlers_disconnect_matched ((status_window->toplevel ), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA ), 0, 0, ((void*)0), (((GCallback) (on_status_toplevel_destroy ))), (status_window)) |
| 1665 | G_CALLBACK (on_status_toplevel_destroy),g_signal_handlers_disconnect_matched ((status_window->toplevel ), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA ), 0, 0, ((void*)0), (((GCallback) (on_status_toplevel_destroy ))), (status_window)) |
| 1666 | status_window)g_signal_handlers_disconnect_matched ((status_window->toplevel ), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA ), 0, 0, ((void*)0), (((GCallback) (on_status_toplevel_destroy ))), (status_window)); |
| 1667 | g_signal_handlers_disconnect_by_func (status_window->toplevel,g_signal_handlers_disconnect_matched ((status_window->toplevel ), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA ), 0, 0, ((void*)0), (((GCallback) (on_status_toplevel_notify_screen ))), (status_window)) |
| 1668 | G_CALLBACK (on_status_toplevel_notify_screen),g_signal_handlers_disconnect_matched ((status_window->toplevel ), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA ), 0, 0, ((void*)0), (((GCallback) (on_status_toplevel_notify_screen ))), (status_window)) |
| 1669 | status_window)g_signal_handlers_disconnect_matched ((status_window->toplevel ), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA ), 0, 0, ((void*)0), (((GCallback) (on_status_toplevel_notify_screen ))), (status_window)); |
| 1670 | g_signal_handlers_disconnect_by_func (status_window->toplevel,g_signal_handlers_disconnect_matched ((status_window->toplevel ), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA ), 0, 0, ((void*)0), (((GCallback) (on_status_toplevel_configure ))), (status_window)) |
| 1671 | G_CALLBACK (on_status_toplevel_configure),g_signal_handlers_disconnect_matched ((status_window->toplevel ), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA ), 0, 0, ((void*)0), (((GCallback) (on_status_toplevel_configure ))), (status_window)) |
| 1672 | status_window)g_signal_handlers_disconnect_matched ((status_window->toplevel ), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA ), 0, 0, ((void*)0), (((GCallback) (on_status_toplevel_configure ))), (status_window)); |
| 1673 | |
| 1674 | if (status_window->window) |
| 1675 | ctk_widget_destroy (status_window->window); |
| 1676 | |
| 1677 | g_object_set_data (G_OBJECT (status_window->toplevel)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((status_window->toplevel)), (((GType) ((20) << ( 2)))))))), "ctk-im-xim-status-window", NULL((void*)0)); |
| 1678 | |
| 1679 | g_free (status_window); |
| 1680 | } |
| 1681 | |
| 1682 | /* Finds the status window object for a toplevel, creating it if necessary. |
| 1683 | */ |
| 1684 | static StatusWindow * |
| 1685 | status_window_get (CtkWidget *toplevel) |
| 1686 | { |
| 1687 | StatusWindow *status_window; |
| 1688 | |
| 1689 | status_window = g_object_get_data (G_OBJECT (toplevel)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((toplevel)), (((GType) ((20) << (2)))))))), "ctk-im-xim-status-window"); |
| 1690 | if (status_window) |
| 1691 | return status_window; |
| 1692 | |
| 1693 | status_window = g_new0 (StatusWindow, 1)((StatusWindow *) g_malloc0_n ((1), sizeof (StatusWindow))); |
| 1694 | status_window->toplevel = toplevel; |
| 1695 | |
| 1696 | status_windows = g_slist_prepend (status_windows, status_window); |
| 1697 | |
| 1698 | g_signal_connect (toplevel, "destroy",g_signal_connect_data ((toplevel), ("destroy"), (((GCallback) (on_status_toplevel_destroy))), (status_window), ((void*)0), (GConnectFlags) 0) |
| 1699 | G_CALLBACK (on_status_toplevel_destroy),g_signal_connect_data ((toplevel), ("destroy"), (((GCallback) (on_status_toplevel_destroy))), (status_window), ((void*)0), (GConnectFlags) 0) |
| 1700 | status_window)g_signal_connect_data ((toplevel), ("destroy"), (((GCallback) (on_status_toplevel_destroy))), (status_window), ((void*)0), (GConnectFlags) 0); |
| 1701 | g_signal_connect (toplevel, "configure-event",g_signal_connect_data ((toplevel), ("configure-event"), (((GCallback ) (on_status_toplevel_configure))), (status_window), ((void*) 0), (GConnectFlags) 0) |
| 1702 | G_CALLBACK (on_status_toplevel_configure),g_signal_connect_data ((toplevel), ("configure-event"), (((GCallback ) (on_status_toplevel_configure))), (status_window), ((void*) 0), (GConnectFlags) 0) |
| 1703 | status_window)g_signal_connect_data ((toplevel), ("configure-event"), (((GCallback ) (on_status_toplevel_configure))), (status_window), ((void*) 0), (GConnectFlags) 0); |
| 1704 | g_signal_connect (toplevel, "notify::screen",g_signal_connect_data ((toplevel), ("notify::screen"), (((GCallback ) (on_status_toplevel_notify_screen))), (status_window), ((void *)0), (GConnectFlags) 0) |
| 1705 | G_CALLBACK (on_status_toplevel_notify_screen),g_signal_connect_data ((toplevel), ("notify::screen"), (((GCallback ) (on_status_toplevel_notify_screen))), (status_window), ((void *)0), (GConnectFlags) 0) |
| 1706 | status_window)g_signal_connect_data ((toplevel), ("notify::screen"), (((GCallback ) (on_status_toplevel_notify_screen))), (status_window), ((void *)0), (GConnectFlags) 0); |
| 1707 | |
| 1708 | g_object_set_data (G_OBJECT (toplevel)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((toplevel)), (((GType) ((20) << (2)))))))), "ctk-im-xim-status-window", status_window); |
| 1709 | |
| 1710 | return status_window; |
| 1711 | } |
| 1712 | |
| 1713 | /* Creates the widgets for the status window; called when we |
| 1714 | * first need to show text for the status window. |
| 1715 | */ |
| 1716 | static void |
| 1717 | status_window_make_window (StatusWindow *status_window) |
| 1718 | { |
| 1719 | CtkWidget *window; |
| 1720 | CtkWidget *status_label; |
| 1721 | |
| 1722 | status_window->window = ctk_window_new (CTK_WINDOW_POPUP); |
| 1723 | window = status_window->window; |
| 1724 | |
| 1725 | ctk_window_set_resizable (CTK_WINDOW (window)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((window)), ((ctk_window_get_type ())))))), FALSE(0)); |
| 1726 | |
| 1727 | status_label = ctk_label_new (""); |
| 1728 | g_object_set (status_label, "margin", 1, NULL((void*)0)); |
| 1729 | ctk_widget_show (status_label); |
| 1730 | |
| 1731 | ctk_container_add (CTK_CONTAINER (window)((((CtkContainer*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((window)), ((ctk_container_get_type ())))))), status_label); |
| 1732 | |
| 1733 | ctk_window_set_screen (CTK_WINDOW (status_window->window)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((status_window->window)), ((ctk_window_get_type ())))) )), |
| 1734 | ctk_widget_get_screen (status_window->toplevel)); |
| 1735 | |
| 1736 | on_status_toplevel_configure (status_window->toplevel, NULL((void*)0), status_window); |
| 1737 | } |
| 1738 | |
| 1739 | /* Updates the text in the status window, hiding or |
| 1740 | * showing the window as necessary. |
| 1741 | */ |
| 1742 | static void |
| 1743 | status_window_set_text (StatusWindow *status_window, |
| 1744 | const gchar *text) |
| 1745 | { |
| 1746 | if (text[0]) |
| 1747 | { |
| 1748 | CtkWidget *label; |
| 1749 | |
| 1750 | if (!status_window->window) |
| 1751 | status_window_make_window (status_window); |
| 1752 | |
| 1753 | label = ctk_bin_get_child (CTK_BIN (status_window->window)((((CtkBin*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((status_window->window)), ((ctk_bin_get_type ()))))))); |
| 1754 | ctk_label_set_text (CTK_LABEL (label)((((CtkLabel*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((label)), ((ctk_label_get_type ())))))), text); |
| 1755 | |
| 1756 | ctk_widget_show (status_window->window); |
| 1757 | } |
| 1758 | else |
| 1759 | { |
| 1760 | if (status_window->window) |
| 1761 | ctk_widget_hide (status_window->window); |
| 1762 | } |
| 1763 | } |
| 1764 | |
| 1765 | /** |
| 1766 | * ctk_im_context_xim_shutdown: |
| 1767 | * |
| 1768 | * Destroys all the status windows that are kept by the XIM contexts. This |
| 1769 | * function should only be called by the XIM module exit routine. |
| 1770 | **/ |
| 1771 | void |
| 1772 | ctk_im_context_xim_shutdown (void) |
| 1773 | { |
| 1774 | while (status_windows) |
| 1775 | status_window_free (status_windows->data); |
| 1776 | |
| 1777 | while (open_ims) |
| 1778 | { |
| 1779 | CtkXIMInfo *info = open_ims->data; |
| 1780 | CdkDisplay *display = cdk_screen_get_display (info->screen); |
| 1781 | |
| 1782 | xim_info_display_closed (display, FALSE(0), info); |
| 1783 | open_ims = g_slist_remove_link (open_ims, open_ims); |
| 1784 | } |
| 1785 | } |