1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
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/>.Free
16 */
17
18 /*
19 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
20 * file for a list of people on the GTK+ Team. See the ChangeLog
21 * files for a list of changes. These files are distributed with
22 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23 */
24
25 #include "config.h"
26
27 #include <errno.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31
32 #include <glib/gstdio.h>
33 #include <gmodule.h>
34 #include "gtkimmoduleprivate.h"
35 #include "gtkimcontextsimple.h"
36 #include "gtksettings.h"
37 #include "gtkprivate.h"
38 #include "gtkutilsprivate.h"
39 #include "gtkintl.h"
40
41 #ifdef GDK_WINDOWING_X11
42 #include "x11/gdkx.h"
43 #endif
44
45 #ifdef GDK_WINDOWING_WAYLAND
46 #include "wayland/gdkwayland.h"
47 #endif
48
49 #ifdef GDK_WINDOWING_BROADWAY
50 #include "broadway/gdkbroadway.h"
51 #endif
52
53 #ifdef GDK_WINDOWING_WIN32
54 #include "win32/gdkwin32.h"
55 #endif
56
57 #ifdef G_OS_WIN32
58 #include <windows.h>
59 #endif
60
61 #undef GDK_DEPRECATED
62 #undef GDK_DEPRECATED_FOR
63 #define GDK_DEPRECATED
64 #define GDK_DEPRECATED_FOR(f)
65
66 #include "deprecated/gtkrc.h"
67
68 /* We need to call getc() a lot in a loop. This is suboptimal,
69 * as getc() does thread locking on the FILE it is given.
70 * To optimize that, lock the file first, then call getc(),
71 * then unlock.
72 * If locking functions are not present in libc, fall back
73 * to the suboptimal getc().
74 */
75 #if !defined(HAVE_FLOCKFILE) && !defined(HAVE__LOCK_FILE)
76 # define flockfile(f) (void)1
77 # define funlockfile(f) (void)1
78 # define getc_unlocked(f) getc(f)
79 #elif !defined(HAVE_FLOCKFILE) && defined(HAVE__LOCK_FILE)
80 # define flockfile(f) _lock_file(f)
81 # define funlockfile(f) _unlock_file(f)
82 # define getc_unlocked(f) _getc_nolock(f)
83 #endif
84
85 #define SIMPLE_ID "gtk-im-context-simple"
86 #define NONE_ID "gtk-im-context-none"
87
88 /**
89 * GtkIMContextInfo:
90 * @context_id: The unique identification string of the input method.
91 * @context_name: The human-readable name of the input method.
92 * @domain: Translation domain to be used with dgettext()
93 * @domain_dirname: Name of locale directory for use with bindtextdomain()
94 * @default_locales: A colon-separated list of locales where this input method
95 * should be the default. The asterisk “*” sets the default for all locales.
96 *
97 * Bookkeeping information about a loadable input method.
98 */
99
100 typedef struct _GtkIMModule GtkIMModule;
101 typedef struct _GtkIMModuleClass GtkIMModuleClass;
102
103 #define GTK_TYPE_IM_MODULE (gtk_im_module_get_type ())
104 #define GTK_IM_MODULE(im_module) (G_TYPE_CHECK_INSTANCE_CAST ((im_module), GTK_TYPE_IM_MODULE, GtkIMModule))
105 #define GTK_IS_IM_MODULE(im_module) (G_TYPE_CHECK_INSTANCE_TYPE ((im_module), GTK_TYPE_IM_MODULE))
106
107 struct _GtkIMModule
108 {
109 GTypeModule parent_instance;
110
111 gboolean builtin;
112
113 GModule *library;
114
115 void (*list) (const GtkIMContextInfo ***contexts,
116 guint *n_contexts);
117 void (*init) (GTypeModule *module);
118 void (*exit) (void);
119 GtkIMContext *(*create) (const gchar *context_id);
120
121 GtkIMContextInfo **contexts;
122 guint n_contexts;
123
124 gchar *path;
125 };
126
127 struct _GtkIMModuleClass
128 {
129 GTypeModuleClass parent_class;
130 };
131
132 static GType gtk_im_module_get_type (void);
133
134 static gint n_loaded_contexts = 0;
135 static GHashTable *contexts_hash = NULL;
136 static GSList *modules_list = NULL;
137
138 static gboolean
gtk_im_module_load(GTypeModule * module)139 gtk_im_module_load (GTypeModule *module)
140 {
141 GtkIMModule *im_module = GTK_IM_MODULE (module);
142
143 if (!im_module->builtin)
144 {
145 im_module->library = g_module_open (im_module->path, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
146 if (!im_module->library)
147 {
148 g_warning ("%s", g_module_error());
149 return FALSE;
150 }
151
152 /* extract symbols from the lib */
153 if (!g_module_symbol (im_module->library, "im_module_init",
154 (gpointer *)&im_module->init) ||
155 !g_module_symbol (im_module->library, "im_module_exit",
156 (gpointer *)&im_module->exit) ||
157 !g_module_symbol (im_module->library, "im_module_list",
158 (gpointer *)&im_module->list) ||
159 !g_module_symbol (im_module->library, "im_module_create",
160 (gpointer *)&im_module->create))
161 {
162 g_warning ("%s", g_module_error());
163 g_module_close (im_module->library);
164
165 return FALSE;
166 }
167 }
168
169 /* call the module's init function to let it */
170 /* setup anything it needs to set up. */
171 im_module->init (module);
172
173 return TRUE;
174 }
175
176 static void
gtk_im_module_unload(GTypeModule * module)177 gtk_im_module_unload (GTypeModule *module)
178 {
179 GtkIMModule *im_module = GTK_IM_MODULE (module);
180
181 im_module->exit();
182
183 if (!im_module->builtin)
184 {
185 g_module_close (im_module->library);
186 im_module->library = NULL;
187
188 im_module->init = NULL;
189 im_module->exit = NULL;
190 im_module->list = NULL;
191 im_module->create = NULL;
192 }
193 }
194
G_DEFINE_TYPE(GtkIMModule,gtk_im_module,G_TYPE_TYPE_MODULE)195 G_DEFINE_TYPE (GtkIMModule, gtk_im_module, G_TYPE_TYPE_MODULE)
196
197 /* This only will ever be called if an error occurs during
198 * initialization
199 */
200 static void
201 gtk_im_module_finalize (GObject *object)
202 {
203 GtkIMModule *module = GTK_IM_MODULE (object);
204
205 g_free (module->path);
206
207 G_OBJECT_CLASS (gtk_im_module_parent_class)->finalize (object);
208 }
209
210 static void
gtk_im_module_class_init(GtkIMModuleClass * class)211 gtk_im_module_class_init (GtkIMModuleClass *class)
212 {
213 GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (class);
214 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
215
216 module_class->load = gtk_im_module_load;
217 module_class->unload = gtk_im_module_unload;
218
219 gobject_class->finalize = gtk_im_module_finalize;
220 }
221
222 static void
gtk_im_module_init(GtkIMModule * object)223 gtk_im_module_init (GtkIMModule* object)
224 {
225 }
226
227 static void
free_info(GtkIMContextInfo * info)228 free_info (GtkIMContextInfo *info)
229 {
230 g_free ((char *)info->context_id);
231 g_free ((char *)info->context_name);
232 g_free ((char *)info->domain);
233 g_free ((char *)info->domain_dirname);
234 g_free ((char *)info->default_locales);
235 g_free (info);
236 }
237
238 static void
add_module(GtkIMModule * module,GSList * infos)239 add_module (GtkIMModule *module, GSList *infos)
240 {
241 GSList *tmp_list = infos;
242 gint i = 0;
243 gint n = g_slist_length (infos);
244 module->contexts = g_new (GtkIMContextInfo *, n);
245
246 while (tmp_list)
247 {
248 GtkIMContextInfo *info = tmp_list->data;
249
250 if (g_hash_table_lookup (contexts_hash, info->context_id))
251 {
252 free_info (info); /* Duplicate */
253 }
254 else
255 {
256 g_hash_table_insert (contexts_hash, (char *)info->context_id, module);
257 module->contexts[i++] = tmp_list->data;
258 n_loaded_contexts++;
259 }
260
261 tmp_list = tmp_list->next;
262 }
263 g_slist_free (infos);
264 module->n_contexts = i;
265
266 modules_list = g_slist_prepend (modules_list, module);
267 }
268
269 #ifdef G_OS_WIN32
270
271 static void
correct_libdir_prefix(gchar ** path)272 correct_libdir_prefix (gchar **path)
273 {
274 /* GTK_LIBDIR is the build-time libdir */
275 if (strncmp (*path, GTK_LIBDIR, strlen (GTK_LIBDIR)) == 0)
276 {
277 /* This is an entry put there by make install on the
278 * packager's system. On Windows a prebuilt GTK+
279 * package can be installed in a random
280 * location. The gtk.immodules file distributed in
281 * such a package contains paths from the package
282 * builder's machine. Replace the path with the real
283 * one on this machine.
284 */
285 gchar *tem = *path;
286 *path = g_strconcat (_gtk_get_libdir (), tem + strlen (GTK_LIBDIR), NULL);
287 g_free (tem);
288 }
289 }
290
291 static void
correct_localedir_prefix(gchar ** path)292 correct_localedir_prefix (gchar **path)
293 {
294 /* See above */
295 if (strncmp (*path, GTK_LOCALEDIR, strlen (GTK_LOCALEDIR)) == 0)
296 {
297 gchar *tem = *path;
298 *path = g_strconcat (_gtk_get_localedir (), tem + strlen (GTK_LOCALEDIR), NULL);
299 g_free (tem);
300 }
301 }
302 #endif
303
304
305 G_GNUC_UNUSED static GtkIMModule *
add_builtin_module(const gchar * module_name,const GtkIMContextInfo ** contexts,int n_contexts)306 add_builtin_module (const gchar *module_name,
307 const GtkIMContextInfo **contexts,
308 int n_contexts)
309 {
310 GtkIMModule *module = g_object_new (GTK_TYPE_IM_MODULE, NULL);
311 GSList *infos = NULL;
312 int i;
313
314 for (i = 0; i < n_contexts; i++)
315 {
316 GtkIMContextInfo *info = g_new (GtkIMContextInfo, 1);
317 info->context_id = g_strdup (contexts[i]->context_id);
318 info->context_name = g_strdup (contexts[i]->context_name);
319 info->domain = g_strdup (contexts[i]->domain);
320 info->domain_dirname = g_strdup (contexts[i]->domain_dirname);
321 #ifdef G_OS_WIN32
322 correct_localedir_prefix ((char **) &info->domain_dirname);
323 #endif
324 info->default_locales = g_strdup (contexts[i]->default_locales);
325 infos = g_slist_prepend (infos, info);
326 }
327
328 module->builtin = TRUE;
329 g_type_module_set_name (G_TYPE_MODULE (module), module_name);
330 add_module (module, infos);
331
332 return module;
333 }
334
335 static void
gtk_im_module_initialize(void)336 gtk_im_module_initialize (void)
337 {
338 GString *line_buf = g_string_new (NULL);
339 GString *tmp_buf = g_string_new (NULL);
340 gchar *filename = gtk_rc_get_im_module_file();
341 FILE *file;
342 gboolean have_error = FALSE;
343
344 GtkIMModule *module = NULL;
345 GSList *infos = NULL;
346
347 contexts_hash = g_hash_table_new (g_str_hash, g_str_equal);
348
349 #define do_builtin(m) \
350 { \
351 const GtkIMContextInfo **contexts; \
352 int n_contexts; \
353 extern void _gtk_immodule_ ## m ## _list (const GtkIMContextInfo ***contexts, \
354 int *n_contexts); \
355 extern void _gtk_immodule_ ## m ## _init (GTypeModule *module); \
356 extern void _gtk_immodule_ ## m ## _exit (void); \
357 extern GtkIMContext *_gtk_immodule_ ## m ## _create (const gchar *context_id); \
358 \
359 _gtk_immodule_ ## m ## _list (&contexts, &n_contexts); \
360 module = add_builtin_module (#m, contexts, n_contexts); \
361 module->init = _gtk_immodule_ ## m ## _init; \
362 module->exit = _gtk_immodule_ ## m ## _exit; \
363 module->create = _gtk_immodule_ ## m ## _create; \
364 module = NULL; \
365 }
366
367 #ifdef INCLUDE_IM_am_et
368 do_builtin (am_et);
369 #endif
370 #ifdef INCLUDE_IM_cedilla
371 do_builtin (cedilla);
372 #endif
373 #ifdef INCLUDE_IM_cyrillic_translit
374 do_builtin (cyrillic_translit);
375 #endif
376 #ifdef INCLUDE_IM_ime
377 do_builtin (ime);
378 #endif
379 #ifdef INCLUDE_IM_inuktitut
380 do_builtin (inuktitut);
381 #endif
382 #ifdef INCLUDE_IM_ipa
383 do_builtin (ipa);
384 #endif
385 #ifdef INCLUDE_IM_multipress
386 do_builtin (multipress);
387 #endif
388 #ifdef INCLUDE_IM_thai
389 do_builtin (thai);
390 #endif
391 #ifdef INCLUDE_IM_ti_er
392 do_builtin (ti_er);
393 #endif
394 #ifdef INCLUDE_IM_ti_et
395 do_builtin (ti_et);
396 #endif
397 #ifdef INCLUDE_IM_viqr
398 do_builtin (viqr);
399 #endif
400 #ifdef INCLUDE_IM_xim
401 do_builtin (xim);
402 #endif
403 #ifdef INCLUDE_IM_broadway
404 do_builtin (broadway);
405 #endif
406 #ifdef INCLUDE_IM_wayland
407 do_builtin (wayland);
408 #endif
409
410 #undef do_builtin
411
412 file = g_fopen (filename, "r");
413 if (!file)
414 {
415 /* In case someone wants only the default input method,
416 * we allow no file at all.
417 */
418 g_string_free (line_buf, TRUE);
419 g_string_free (tmp_buf, TRUE);
420 g_free (filename);
421 return;
422 }
423
424 while (!have_error && gtk_read_line (file, line_buf))
425 {
426 const char *p;
427
428 p = line_buf->str;
429
430 if (!gtk_skip_space (&p))
431 {
432 /* Blank line marking the end of a module
433 */
434 if (module && *p != '#')
435 {
436 add_module (module, infos);
437 module = NULL;
438 infos = NULL;
439 }
440
441 continue;
442 }
443
444 if (!module)
445 {
446 /* Read a module location
447 */
448 module = g_object_new (GTK_TYPE_IM_MODULE, NULL);
449
450 if (!gtk_scan_string (&p, tmp_buf) || gtk_skip_space (&p))
451 {
452 g_warning ("Error parsing context info in '%s'\n %s", filename, line_buf->str);
453 have_error = TRUE;
454 }
455
456 module->path = g_strdup (tmp_buf->str);
457 #ifdef G_OS_WIN32
458 correct_libdir_prefix (&module->path);
459 #endif
460 g_type_module_set_name (G_TYPE_MODULE (module), module->path);
461 }
462 else
463 {
464 GtkIMContextInfo *info = g_new0 (GtkIMContextInfo, 1);
465
466 /* Read information about a context type
467 */
468 if (!gtk_scan_string (&p, tmp_buf))
469 goto context_error;
470 info->context_id = g_strdup (tmp_buf->str);
471
472 if (!gtk_scan_string (&p, tmp_buf))
473 goto context_error;
474 info->context_name = g_strdup (tmp_buf->str);
475
476 if (!gtk_scan_string (&p, tmp_buf))
477 goto context_error;
478 info->domain = g_strdup (tmp_buf->str);
479
480 if (!gtk_scan_string (&p, tmp_buf))
481 goto context_error;
482
483 info->domain_dirname = g_strdup (tmp_buf->str);
484 #ifdef G_OS_WIN32
485 correct_localedir_prefix ((char **) &info->domain_dirname);
486 #endif
487
488 if (!gtk_scan_string (&p, tmp_buf))
489 goto context_error;
490 info->default_locales = g_strdup (tmp_buf->str);
491
492 if (gtk_skip_space (&p))
493 goto context_error;
494
495 infos = g_slist_prepend (infos, info);
496 continue;
497
498 context_error:
499 g_warning ("Error parsing context info in '%s'\n %s", filename, line_buf->str);
500 have_error = TRUE;
501 }
502 }
503
504 if (have_error)
505 {
506 g_slist_free_full (infos, (GDestroyNotify)free_info);
507 g_object_unref (module);
508 }
509 else if (module)
510 add_module (module, infos);
511
512 fclose (file);
513 g_string_free (line_buf, TRUE);
514 g_string_free (tmp_buf, TRUE);
515 g_free (filename);
516 }
517
518 static gint
compare_gtkimcontextinfo_name(const GtkIMContextInfo ** a,const GtkIMContextInfo ** b)519 compare_gtkimcontextinfo_name (const GtkIMContextInfo **a,
520 const GtkIMContextInfo **b)
521 {
522 return g_utf8_collate ((*a)->context_name, (*b)->context_name);
523 }
524
525 /**
526 * _gtk_im_module_list:
527 * @contexts: location to store an array of pointers to #GtkIMContextInfo
528 * this array should be freed with g_free() when you are finished.
529 * The structures it points are statically allocated and should
530 * not be modified or freed.
531 * @n_contexts: the length of the array stored in @contexts
532 *
533 * List all available types of input method context
534 */
535 void
_gtk_im_module_list(const GtkIMContextInfo *** contexts,guint * n_contexts)536 _gtk_im_module_list (const GtkIMContextInfo ***contexts,
537 guint *n_contexts)
538 {
539 int n = 0;
540
541 static
542 #ifndef G_OS_WIN32
543 const
544 #endif
545 GtkIMContextInfo simple_context_info = {
546 SIMPLE_ID,
547 NC_("input method menu", "Simple"),
548 GETTEXT_PACKAGE,
549 #ifdef GTK_LOCALEDIR
550 GTK_LOCALEDIR,
551 #else
552 "",
553 #endif
554 ""
555 };
556
557 static
558 #ifndef G_OS_WIN32
559 const
560 #endif
561 GtkIMContextInfo none_context_info = {
562 NONE_ID,
563 NC_("input method menu", "None"),
564 GETTEXT_PACKAGE,
565 #ifdef GTK_LOCALEDIR
566 GTK_LOCALEDIR,
567 #else
568 "",
569 #endif
570 ""
571 };
572
573 #ifdef G_OS_WIN32
574 static gboolean beenhere = FALSE;
575 #endif
576
577 if (!contexts_hash)
578 gtk_im_module_initialize ();
579
580 #ifdef G_OS_WIN32
581 if (!beenhere)
582 {
583 beenhere = TRUE;
584 /* correct_localedir_prefix() requires its parameter to be a
585 * malloced string
586 */
587 simple_context_info.domain_dirname = g_strdup (simple_context_info.domain_dirname);
588 correct_localedir_prefix ((char **) &simple_context_info.domain_dirname);
589 none_context_info.domain_dirname = g_strdup (none_context_info.domain_dirname);
590 correct_localedir_prefix ((char **) &none_context_info.domain_dirname);
591 }
592 #endif
593
594 if (n_contexts)
595 *n_contexts = n_loaded_contexts + 2;
596
597 if (contexts)
598 {
599 GSList *tmp_list;
600 int i;
601
602 *contexts = g_new (const GtkIMContextInfo *, n_loaded_contexts + 2);
603
604 (*contexts)[n++] = &none_context_info;
605 (*contexts)[n++] = &simple_context_info;
606
607 tmp_list = modules_list;
608 while (tmp_list)
609 {
610 GtkIMModule *module = tmp_list->data;
611
612 for (i=0; i<module->n_contexts; i++)
613 (*contexts)[n++] = module->contexts[i];
614
615 tmp_list = tmp_list->next;
616 }
617
618 /* first elements (Simple and None) should always be at top */
619 qsort ((*contexts)+2, n-2, sizeof (GtkIMContextInfo *), (GCompareFunc)compare_gtkimcontextinfo_name);
620 }
621 }
622
623 /**
624 * _gtk_im_module_create:
625 * @context_id: the context ID for the context type to create
626 *
627 * Create an IM context of a type specified by the string
628 * ID @context_id.
629 *
630 * Returns: a newly created input context of or @context_id, or
631 * if that could not be created, a newly created GtkIMContextSimple.
632 */
633 GtkIMContext *
_gtk_im_module_create(const gchar * context_id)634 _gtk_im_module_create (const gchar *context_id)
635 {
636 GtkIMModule *im_module;
637 GtkIMContext *context = NULL;
638
639 if (strcmp (context_id, NONE_ID) == 0)
640 return NULL;
641
642 if (!contexts_hash)
643 gtk_im_module_initialize ();
644
645 if (strcmp (context_id, SIMPLE_ID) != 0)
646 {
647 im_module = g_hash_table_lookup (contexts_hash, context_id);
648 if (!im_module)
649 {
650 g_warning ("Attempt to load unknown IM context type '%s'", context_id);
651 }
652 else
653 {
654 if (g_type_module_use (G_TYPE_MODULE (im_module)))
655 {
656 context = im_module->create (context_id);
657 g_type_module_unuse (G_TYPE_MODULE (im_module));
658 }
659
660 if (!context)
661 g_warning ("Loading IM context type '%s' failed", context_id);
662 }
663 }
664
665 if (!context)
666 return gtk_im_context_simple_new ();
667 else
668 return context;
669 }
670
671 /* Match @locale against @against.
672 *
673 * 'en_US' against “en_US” => 4
674 * 'en_US' against “en” => 3
675 * 'en', “en_UK” against “en_US” => 2
676 * all locales, against “*” => 1
677 */
678 static gint
match_locale(const gchar * locale,const gchar * against,gint against_len)679 match_locale (const gchar *locale,
680 const gchar *against,
681 gint against_len)
682 {
683 if (strcmp (against, "*") == 0)
684 return 1;
685
686 if (g_ascii_strcasecmp (locale, against) == 0)
687 return 4;
688
689 if (g_ascii_strncasecmp (locale, against, 2) == 0)
690 return (against_len == 2) ? 3 : 2;
691
692 return 0;
693 }
694
695 static gboolean
match_backend(GtkIMContextInfo * context)696 match_backend (GtkIMContextInfo *context)
697 {
698 #ifdef GDK_WINDOWING_WAYLAND
699 if (g_strcmp0 (context->context_id, "wayland") == 0)
700 {
701 GdkDisplay *display = gdk_display_get_default ();
702
703 return GDK_IS_WAYLAND_DISPLAY (display) &&
704 gdk_wayland_display_query_registry (display,
705 "zwp_text_input_manager_v3");
706 }
707 if (g_strcmp0 (context->context_id, "waylandgtk") == 0)
708 {
709 GdkDisplay *display = gdk_display_get_default ();
710
711 return GDK_IS_WAYLAND_DISPLAY (display) &&
712 gdk_wayland_display_query_registry (display,
713 "gtk_text_input_manager");
714 }
715 #endif
716
717 #ifdef GDK_WINDOWING_BROADWAY
718 if (g_strcmp0 (context->context_id, "broadway") == 0)
719 return GDK_IS_BROADWAY_DISPLAY (gdk_display_get_default ());
720 #endif
721
722 #ifdef GDK_WINDOWING_X11
723 if (g_strcmp0 (context->context_id, "xim") == 0)
724 return GDK_IS_X11_DISPLAY (gdk_display_get_default ());
725 #endif
726
727 #ifdef GDK_WINDOWING_WIN32
728 if (g_strcmp0 (context->context_id, "ime") == 0)
729 return GDK_IS_WIN32_DISPLAY (gdk_display_get_default ());
730 #endif
731
732 return TRUE;
733 }
734
735 static const gchar *
lookup_immodule(gchar ** immodules_list)736 lookup_immodule (gchar **immodules_list)
737 {
738 while (immodules_list && *immodules_list)
739 {
740 if (g_strcmp0 (*immodules_list, SIMPLE_ID) == 0)
741 return SIMPLE_ID;
742 else if (g_strcmp0 (*immodules_list, NONE_ID) == 0)
743 return NONE_ID;
744 else
745 {
746 gboolean found;
747 gchar *context_id;
748 found = g_hash_table_lookup_extended (contexts_hash, *immodules_list,
749 (gpointer *) &context_id, NULL);
750 if (found)
751 return context_id;
752 }
753 immodules_list++;
754 }
755
756 return NULL;
757 }
758
759 #ifdef G_OS_WIN32
760
761 /* max size for LOCALE_SISO639LANGNAME and LOCALE_SISO3166CTRYNAME is 9 */
762 #define MAX_NAME_SIZE 9
763
764 static gchar *
get_current_input_language(void)765 get_current_input_language (void)
766 {
767 LCID lcid;
768 LANGID langid;
769 HKL kblayout;
770 int name_size;
771 wchar_t name[MAX_NAME_SIZE];
772 gchar *language;
773 gchar *country;
774 gchar *full;
775
776 /* Current thread's keyboard layout */
777 kblayout = GetKeyboardLayout(0);
778 /* lowest word in the HKL is the LANGID */
779 langid = LOWORD (kblayout);
780 /* LCID is the LANGID without order */
781 lcid = langid;
782
783 /* Get Language ID */
784 name_size = GetLocaleInfoW (lcid, LOCALE_SISO639LANGNAME, NULL, 0);
785 if (name_size <= 1)
786 return NULL;
787
788 g_assert (name_size <= MAX_NAME_SIZE);
789 GetLocaleInfoW (lcid, LOCALE_SISO639LANGNAME, name, name_size);
790
791 language = g_utf16_to_utf8 (name, name_size, NULL, NULL, NULL);
792 if (!language)
793 return NULL;
794
795 if (SUBLANGID (langid) == SUBLANG_NEUTRAL)
796 return language;
797
798 /* Get Country ID */
799 name_size = GetLocaleInfoW (lcid, LOCALE_SISO3166CTRYNAME, NULL, 0);
800 if (name_size <= 1)
801 return language;
802
803 g_assert (name_size <= MAX_NAME_SIZE);
804 GetLocaleInfoW (lcid, LOCALE_SISO3166CTRYNAME, name, name_size);
805
806 country = g_utf16_to_utf8 (name, name_size, NULL, NULL, NULL);
807 if (!country)
808 return language;
809
810 full = g_strdup_printf ("%s_%s", language, country);
811
812 g_free (language);
813 g_free (country);
814
815 return full;
816 }
817
818 #endif
819
820 /**
821 * _gtk_im_module_get_default_context_id:
822 *
823 * Return the context_id of the best IM context type
824 * for the given window.
825 *
826 * Returns: the context ID (will never be %NULL)
827 */
828 const gchar *
_gtk_im_module_get_default_context_id(void)829 _gtk_im_module_get_default_context_id (void)
830 {
831 GSList *tmp_list;
832 const gchar *context_id = NULL;
833 gint best_goodness = 0;
834 gint i;
835 gchar *tmp_locale, *tmp, **immodules;
836 const gchar *envvar;
837 GdkScreen *screen;
838 GtkSettings *settings;
839
840 if (!contexts_hash)
841 gtk_im_module_initialize ();
842
843 envvar = g_getenv ("GTK_IM_MODULE");
844 if (envvar)
845 {
846 immodules = g_strsplit (envvar, ":", 0);
847 context_id = lookup_immodule (immodules);
848 g_strfreev (immodules);
849
850 if (context_id)
851 return context_id;
852 }
853
854 /* Check if the certain immodule is set in XSETTINGS.
855 */
856 screen = gdk_screen_get_default ();
857 settings = gtk_settings_get_for_screen (screen);
858 g_object_get (G_OBJECT (settings), "gtk-im-module", &tmp, NULL);
859 if (tmp)
860 {
861 immodules = g_strsplit (tmp, ":", 0);
862 context_id = lookup_immodule (immodules);
863 g_strfreev (immodules);
864 g_free (tmp);
865
866 if (context_id)
867 return context_id;
868 }
869
870 #ifdef G_OS_WIN32
871 /* Read current input locale from the current keyboard info */
872 tmp_locale = get_current_input_language ();
873 if (!tmp_locale)
874 /* Default to system locale when input language is unknown */
875 tmp_locale = _gtk_get_lc_ctype ();
876 #else
877 tmp_locale = _gtk_get_lc_ctype ();
878 #endif
879
880 /* Strip the locale code down to the essentials
881 */
882 tmp = strchr (tmp_locale, '.');
883 if (tmp)
884 *tmp = '\0';
885 tmp = strchr (tmp_locale, '@');
886 if (tmp)
887 *tmp = '\0';
888
889 tmp_list = modules_list;
890 while (tmp_list)
891 {
892 GtkIMModule *module = tmp_list->data;
893
894 for (i = 0; i < module->n_contexts; i++)
895 {
896 const gchar *p;
897
898 if (!match_backend (module->contexts[i]))
899 continue;
900
901 p = module->contexts[i]->default_locales;
902 while (p)
903 {
904 const gchar *q = strchr (p, ':');
905 gint goodness = match_locale (tmp_locale, p, q ? q - p : strlen (p));
906
907 if (goodness > best_goodness)
908 {
909 context_id = module->contexts[i]->context_id;
910 best_goodness = goodness;
911 }
912
913 p = q ? q + 1 : NULL;
914 }
915 }
916
917 tmp_list = tmp_list->next;
918 }
919
920 g_free (tmp_locale);
921
922 return context_id ? context_id : SIMPLE_ID;
923 }
924