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