1 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */
2 /*
3  * nimf-xim.c
4  * This file is part of Nimf.
5  *
6  * Copyright (C) 2015-2019 Hodong Kim <cogniti@gmail.com>
7  *
8  * Nimf is free software: you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as published
10  * by the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * Nimf is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16  * See the GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program;  If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include "nimf-xim.h"
23 #include "IMdkit/i18nMethod.h"
24 #include "IMdkit/i18nX.h"
25 #include "IMdkit/XimFunc.h"
26 
27 G_DEFINE_DYNAMIC_TYPE (NimfXim, nimf_xim, NIMF_TYPE_SERVICE);
28 
29 static void
nimf_xim_change_engine(NimfService * service,const gchar * engine_id,const gchar * method_id)30 nimf_xim_change_engine (NimfService *service,
31                         const gchar *engine_id,
32                         const gchar *method_id)
33 {
34   g_debug (G_STRLOC ": %s", G_STRFUNC);
35 
36   NimfXim       *xim = NIMF_XIM (service);
37   NimfServiceIC *ic;
38 
39   ic = g_hash_table_lookup (xim->ics,
40                             GUINT_TO_POINTER (xim->last_focused_icid));
41   if (ic)
42     nimf_service_ic_change_engine (ic, engine_id, method_id);
43 }
44 
45 static void
nimf_xim_change_engine_by_id(NimfService * service,const gchar * engine_id)46 nimf_xim_change_engine_by_id (NimfService *service,
47                               const gchar *engine_id)
48 {
49   g_debug (G_STRLOC ": %s", G_STRFUNC);
50 
51   NimfXim       *xim = NIMF_XIM (service);
52   NimfServiceIC *ic;
53 
54   ic = g_hash_table_lookup (xim->ics,
55                             GUINT_TO_POINTER (xim->last_focused_icid));
56   if (ic)
57     nimf_service_ic_change_engine_by_id (ic, engine_id);
58 }
59 
nimf_xim_set_ic_values(NimfXim * xim,IMChangeICStruct * data)60 static int nimf_xim_set_ic_values (NimfXim          *xim,
61                                    IMChangeICStruct *data)
62 {
63   g_debug (G_STRLOC ": %s", G_STRFUNC);
64 
65   NimfServiceIC *ic;
66   NimfXimIC     *xic;
67   CARD16 i;
68 
69   ic = g_hash_table_lookup (xim->ics, GUINT_TO_POINTER (data->icid));
70   xic = NIMF_XIM_IC (ic);
71 
72   for (i = 0; i < data->ic_attr_num; i++)
73   {
74     if (!g_strcmp0 (XNInputStyle, data->ic_attr[i].name))
75     {
76       xic->input_style = *(CARD32*) data->ic_attr[i].value;
77       nimf_service_ic_set_use_preedit (ic, !!(xic->input_style & XIMPreeditCallbacks));
78     }
79     else if (!g_strcmp0 (XNClientWindow, data->ic_attr[i].name))
80     {
81       xic->client_window = *(Window *) data->ic_attr[i].value;
82     }
83     else if (!g_strcmp0 (XNFocusWindow, data->ic_attr[i].name))
84     {
85       xic->focus_window = *(Window *) data->ic_attr[i].value;
86     }
87     else
88     {
89       g_warning (G_STRLOC ": %s %s", G_STRFUNC, data->ic_attr[i].name);
90     }
91   }
92 
93   for (i = 0; i < data->preedit_attr_num; i++)
94   {
95     if (g_strcmp0 (XNPreeditState, data->preedit_attr[i].name) == 0)
96     {
97       XIMPreeditState state = *(XIMPreeditState *) data->preedit_attr[i].value;
98       switch (state)
99       {
100         case XIMPreeditEnable:
101           nimf_service_ic_set_use_preedit (ic, TRUE);
102           break;
103         case XIMPreeditDisable:
104           nimf_service_ic_set_use_preedit (ic, FALSE);
105           break;
106         case XIMPreeditUnKnown:
107           break;
108         default:
109           g_warning (G_STRLOC ": %s: XIMPreeditState: %ld is ignored",
110                      G_STRFUNC, state);
111           break;
112       }
113     }
114     else if (g_strcmp0 (XNSpotLocation, data->preedit_attr[i].name) == 0)
115     {
116       nimf_xim_ic_set_cursor_location (xic,
117                                   ((XPoint *) data->preedit_attr[i].value)->x,
118                                   ((XPoint *) data->preedit_attr[i].value)->y);
119       NimfServer      *server      = nimf_server_get_default ();
120       NimfPreeditable *preeditable = nimf_server_get_preeditable (server);
121       if (nimf_preeditable_is_visible (preeditable))
122         nimf_preeditable_show (preeditable);
123     }
124     else
125     {
126       g_critical (G_STRLOC ": %s: %s is ignored",
127                   G_STRFUNC, data->preedit_attr[i].name);
128     }
129   }
130 
131   for (i = 0; i < data->status_attr_num; i++)
132   {
133     g_critical (G_STRLOC ": %s: %s is ignored",
134                 G_STRFUNC, data->status_attr[i].name);
135   }
136 
137   return 1;
138 }
139 
nimf_xim_create_ic(NimfXim * xim,IMChangeICStruct * data)140 static int nimf_xim_create_ic (NimfXim          *xim,
141                                IMChangeICStruct *data)
142 {
143   g_debug (G_STRLOC ": %s, data->connect_id: %d", G_STRFUNC, data->connect_id);
144 
145   NimfXimIC *xic;
146   xic = g_hash_table_lookup (xim->ics, GUINT_TO_POINTER (data->icid));
147 
148   if (!xic)
149   {
150     guint16 icid;
151 
152     do
153       icid = xim->next_icid++;
154     while (icid == 0 ||
155            g_hash_table_contains (xim->ics, GUINT_TO_POINTER (icid)));
156 
157     xic = nimf_xim_ic_new (xim, data->connect_id, icid);
158     g_hash_table_insert (xim->ics, GUINT_TO_POINTER (icid), xic);
159     data->icid = icid;
160     g_debug (G_STRLOC ": icid = %d", data->icid);
161   }
162 
163   nimf_xim_set_ic_values (xim, data);
164 
165   return 1;
166 }
167 
nimf_xim_destroy_ic(NimfXim * xim,IMDestroyICStruct * data)168 static int nimf_xim_destroy_ic (NimfXim           *xim,
169                                 IMDestroyICStruct *data)
170 {
171   g_debug (G_STRLOC ": %s, data->icid = %d", G_STRFUNC, data->icid);
172 
173   return g_hash_table_remove (xim->ics, GUINT_TO_POINTER (data->icid));
174 }
175 
nimf_xim_get_ic_values(NimfXim * xim,IMChangeICStruct * data)176 static int nimf_xim_get_ic_values (NimfXim          *xim,
177                                    IMChangeICStruct *data)
178 {
179   g_debug (G_STRLOC ": %s", G_STRFUNC);
180 
181   NimfServiceIC *ic;
182   ic = g_hash_table_lookup (xim->ics, GUINT_TO_POINTER (data->icid));
183   CARD16 i;
184 
185   for (i = 0; i < data->ic_attr_num; i++)
186   {
187     if (g_strcmp0 (XNFilterEvents, data->ic_attr[i].name) == 0)
188     {
189       data->ic_attr[i].value_length = sizeof (CARD32);
190       data->ic_attr[i].value = g_malloc (sizeof (CARD32));
191       *(CARD32 *) data->ic_attr[i].value = KeyPressMask | KeyReleaseMask;
192     }
193     else
194       g_critical (G_STRLOC ": %s: %s is ignored",
195                   G_STRFUNC, data->ic_attr[i].name);
196   }
197 
198   for (i = 0; i < data->preedit_attr_num; i++)
199   {
200     if (g_strcmp0 (XNPreeditState, data->preedit_attr[i].name) == 0)
201     {
202       data->preedit_attr[i].value_length = sizeof (XIMPreeditState);
203       data->preedit_attr[i].value = g_malloc (sizeof (XIMPreeditState));
204 
205       if (nimf_service_ic_get_use_preedit (ic))
206         *(XIMPreeditState *) data->preedit_attr[i].value = XIMPreeditEnable;
207       else
208         *(XIMPreeditState *) data->preedit_attr[i].value = XIMPreeditDisable;
209     }
210     else
211       g_critical (G_STRLOC ": %s: %s is ignored",
212                   G_STRFUNC, data->preedit_attr[i].name);
213   }
214 
215   for (i = 0; i < data->status_attr_num; i++)
216     g_critical (G_STRLOC ": %s: %s is ignored",
217                 G_STRFUNC, data->status_attr[i].name);
218 
219   return 1;
220 }
221 
nimf_xim_forward_event(NimfXim * xim,IMForwardEventStruct * data)222 static int nimf_xim_forward_event (NimfXim              *xim,
223                                    IMForwardEventStruct *data)
224 {
225   g_debug (G_STRLOC ": %s", G_STRFUNC);
226 
227   XKeyEvent        *xevent;
228   NimfEvent        *event;
229   gboolean          retval;
230   KeySym            keysym;
231   unsigned int      consumed;
232   NimfModifierType  state;
233 
234   xevent = (XKeyEvent*) &(data->event);
235 
236   event = nimf_event_new (NIMF_EVENT_NOTHING);
237 
238   if (xevent->type == KeyPress)
239     event->key.type = NIMF_EVENT_KEY_PRESS;
240   else
241     event->key.type = NIMF_EVENT_KEY_RELEASE;
242 
243   event->key.state = (NimfModifierType) xevent->state;
244   event->key.keyval = NIMF_KEY_VoidSymbol;
245   event->key.hardware_keycode = xevent->keycode;
246 
247   XkbLookupKeySym (xim->display,
248                    event->key.hardware_keycode,
249                    event->key.state,
250                    &consumed, &keysym);
251   event->key.keyval = (guint) keysym;
252 
253   state = event->key.state & ~consumed;
254   event->key.state |= (NimfModifierType) state;
255 
256   NimfServiceIC *ic;
257   ic = g_hash_table_lookup (xim->ics, GUINT_TO_POINTER (data->icid));
258   retval = nimf_service_ic_filter_event (ic, event);
259   nimf_event_free (event);
260 
261   if (G_UNLIKELY (!retval))
262     return xi18n_forwardEvent (xim, (XPointer) data);
263 
264   return 1;
265 }
266 
267 static const gchar *
nimf_xim_get_id(NimfService * service)268 nimf_xim_get_id (NimfService *service)
269 {
270   g_debug (G_STRLOC ": %s", G_STRFUNC);
271 
272   g_return_val_if_fail (NIMF_IS_SERVICE (service), NULL);
273 
274   return NIMF_XIM (service)->id;
275 }
276 
nimf_xim_set_ic_focus(NimfXim * xim,IMChangeFocusStruct * data)277 static int nimf_xim_set_ic_focus (NimfXim             *xim,
278                                   IMChangeFocusStruct *data)
279 {
280   NimfServiceIC *ic;
281   NimfXimIC     *xic;
282 
283   ic  = g_hash_table_lookup (xim->ics, GUINT_TO_POINTER (data->icid));
284   xic = NIMF_XIM_IC (ic);
285 
286   g_debug (G_STRLOC ": %s, icid = %d, connection id = %d",
287            G_STRFUNC, data->icid, xic->icid);
288 
289   nimf_service_ic_focus_in (ic);
290   xim->last_focused_icid = xic->icid;
291 
292   if (xic->input_style & XIMPreeditNothing)
293     nimf_xim_ic_set_cursor_location (xic, -1, -1);
294 
295   return 1;
296 }
297 
nimf_xim_unset_ic_focus(NimfXim * xim,IMChangeFocusStruct * data)298 static int nimf_xim_unset_ic_focus (NimfXim             *xim,
299                                     IMChangeFocusStruct *data)
300 {
301   NimfServiceIC *ic;
302   ic = g_hash_table_lookup (xim->ics, GUINT_TO_POINTER (data->icid));
303 
304   g_debug (G_STRLOC ": %s, icid = %d", G_STRFUNC, data->icid);
305 
306   nimf_service_ic_focus_out (ic);
307 
308   return 1;
309 }
310 
nimf_xim_reset_ic(NimfXim * xim,IMResetICStruct * data)311 static int nimf_xim_reset_ic (NimfXim         *xim,
312                               IMResetICStruct *data)
313 {
314   g_debug (G_STRLOC ": %s", G_STRFUNC);
315 
316   NimfServiceIC *ic;
317   ic = g_hash_table_lookup (xim->ics, GUINT_TO_POINTER (data->icid));
318   nimf_service_ic_reset (ic);
319 
320   return 1;
321 }
322 /* FIXME */
323 int
on_incoming_message(NimfXim * xim,IMProtocol * data)324 on_incoming_message (NimfXim    *xim,
325                      IMProtocol *data)
326 {
327   g_debug (G_STRLOC ": %s", G_STRFUNC);
328 
329   g_return_val_if_fail (data != NULL, True);
330 
331   int retval;
332 
333   switch (data->major_code)
334   {
335     case XIM_OPEN:
336       g_debug (G_STRLOC ": XIM_OPEN: connect_id: %u", data->imopen.connect_id);
337       retval = 1;
338       break;
339     case XIM_CLOSE:
340       g_debug (G_STRLOC ": XIM_CLOSE: connect_id: %u",
341                data->imclose.connect_id);
342       retval = 1;
343       break;
344     case XIM_PREEDIT_START_REPLY:
345       g_debug (G_STRLOC ": XIM_PREEDIT_START_REPLY");
346       retval = 1;
347       break;
348     case XIM_CREATE_IC:
349       retval = nimf_xim_create_ic (xim, &data->changeic);
350       break;
351     case XIM_DESTROY_IC:
352       retval = nimf_xim_destroy_ic (xim, &data->destroyic);
353       break;
354     case XIM_SET_IC_VALUES:
355       retval = nimf_xim_set_ic_values (xim, &data->changeic);
356       break;
357     case XIM_GET_IC_VALUES:
358       retval = nimf_xim_get_ic_values (xim, &data->changeic);
359       break;
360     case XIM_FORWARD_EVENT:
361       retval = nimf_xim_forward_event (xim, &data->forwardevent);
362       break;
363     case XIM_SET_IC_FOCUS:
364       retval = nimf_xim_set_ic_focus (xim, &data->changefocus);
365       break;
366     case XIM_UNSET_IC_FOCUS:
367       retval = nimf_xim_unset_ic_focus (xim, &data->changefocus);
368       break;
369     case XIM_RESET_IC:
370       retval = nimf_xim_reset_ic (xim, &data->resetic);
371       break;
372     default:
373       g_warning (G_STRLOC ": %s: major op code %d not handled", G_STRFUNC,
374                  data->major_code);
375       retval = 0;
376       break;
377   }
378 
379   return retval;
380 }
381 
382 static int
on_xerror(Display * display,XErrorEvent * error)383 on_xerror (Display     *display,
384            XErrorEvent *error)
385 {
386   gchar buf[64];
387 
388   XGetErrorText (display, error->error_code, buf, 63);
389 
390   g_warning (G_STRLOC ": %s: %s", G_STRFUNC, buf);
391   g_warning ("type: %d",         error->type);
392   g_warning ("display name: %s", DisplayString (error->display));
393   g_warning ("serial: %lu",      error->serial);
394   g_warning ("error_code: %d",   error->error_code);
395   g_warning ("request_code: %d", error->request_code);
396   g_warning ("minor_code: %d",   error->minor_code);
397   g_warning ("resourceid: %lu",  error->resourceid);
398 
399   return 1;
400 }
401 
402 typedef struct
403 {
404   GSource  source;
405   NimfXim *xim;
406   GPollFD  poll_fd;
407 } NimfXEventSource;
408 
nimf_xevent_source_prepare(GSource * source,gint * timeout)409 static gboolean nimf_xevent_source_prepare (GSource *source,
410                                             gint    *timeout)
411 {
412   g_debug (G_STRLOC ": %s", G_STRFUNC);
413 
414   Display *display = ((NimfXEventSource *) source)->xim->display;
415   *timeout = -1;
416   return XPending (display) > 0;
417 }
418 
nimf_xevent_source_check(GSource * source)419 static gboolean nimf_xevent_source_check (GSource *source)
420 {
421   g_debug (G_STRLOC ": %s", G_STRFUNC);
422 
423   NimfXEventSource *display_source = (NimfXEventSource *) source;
424 
425   if (display_source->poll_fd.revents & G_IO_IN)
426     return XPending (display_source->xim->display) > 0;
427   else
428     return FALSE;
429 }
430 
431 static gboolean
nimf_xevent_source_dispatch(GSource * source,GSourceFunc callback,gpointer user_data)432 nimf_xevent_source_dispatch (GSource     *source,
433                              GSourceFunc  callback,
434                              gpointer     user_data)
435 {
436   g_debug (G_STRLOC ": %s", G_STRFUNC);
437 
438   NimfXim *xim = ((NimfXEventSource*) source)->xim;
439   XEvent   event;
440 
441   while (XPending (xim->display) > 0)
442   {
443     XNextEvent (xim->display, &event);
444     if (!XFilterEvent (&event, None))
445     {
446       switch (event.type)
447       {
448         case SelectionRequest:
449           WaitXSelectionRequest (xim, &event);
450           break;
451         case ClientMessage:
452           {
453             XClientMessageEvent cme = *(XClientMessageEvent *) &event;
454 
455             if (cme.message_type == xim->_xconnect)
456               ReadXConnectMessage (xim, (XClientMessageEvent *) &event);
457             else if (cme.message_type == xim->_protocol)
458               WaitXIMProtocol (xim, &event);
459             else
460               g_warning (G_STRLOC ": %s: ClientMessage type: %ld not handled",
461                          G_STRFUNC, cme.message_type);
462           }
463           break;
464         case MappingNotify:
465           g_message (G_STRLOC ": %s: MappingNotify", G_STRFUNC);
466           break;
467         default:
468           g_warning (G_STRLOC ": %s: event type: %d not filtered",
469                      G_STRFUNC, event.type);
470           break;
471       }
472     }
473   }
474 
475   return TRUE;
476 }
477 
nimf_xevent_source_finalize(GSource * source)478 static void nimf_xevent_source_finalize (GSource *source)
479 {
480   g_debug (G_STRLOC ": %s", G_STRFUNC);
481 }
482 
483 static GSourceFuncs event_funcs = {
484   nimf_xevent_source_prepare,
485   nimf_xevent_source_check,
486   nimf_xevent_source_dispatch,
487   nimf_xevent_source_finalize
488 };
489 
nimf_xevent_source_new(NimfXim * xim)490 static GSource *nimf_xevent_source_new (NimfXim *xim)
491 {
492   g_debug (G_STRLOC ": %s", G_STRFUNC);
493 
494   GSource *source;
495   NimfXEventSource *xevent_source;
496 
497   source = g_source_new (&event_funcs, sizeof (NimfXEventSource));
498   xevent_source = (NimfXEventSource *) source;
499   xevent_source->xim = xim;
500 
501   xevent_source->poll_fd.fd = ConnectionNumber (xevent_source->xim->display);
502   xevent_source->poll_fd.events = G_IO_IN;
503   g_source_add_poll (source, &xevent_source->poll_fd);
504 
505   g_source_set_priority (source, G_PRIORITY_DEFAULT);
506   g_source_set_can_recurse (source, TRUE);
507 
508   return source;
509 }
510 
nimf_xim_is_active(NimfService * service)511 static gboolean nimf_xim_is_active (NimfService *service)
512 {
513   g_debug (G_STRLOC ": %s", G_STRFUNC);
514 
515   return NIMF_XIM (service)->active;
516 }
517 
nimf_xim_start(NimfService * service)518 static gboolean nimf_xim_start (NimfService *service)
519 {
520   g_debug (G_STRLOC ": %s", G_STRFUNC);
521 
522   NimfXim *xim = NIMF_XIM (service);
523 
524   if (xim->active)
525     return TRUE;
526 
527   xim->display = XOpenDisplay (NULL);
528 
529   if (xim->display == NULL)
530   {
531     g_warning (G_STRLOC ": %s: Can't open display", G_STRFUNC);
532     return FALSE;
533   }
534 
535 /*
536  * https://www.x.org/releases/X11R7.7/doc/libX11/libX11/libX11.html
537  * https://www.x.org/releases/X11R7.7/doc/libX11/XIM/xim.html
538  *
539  * The preedit category defines what type of support is provided by the input
540  * method for preedit information.
541  *
542  * XIMPreeditArea      (as known as off-the-spot)
543  *
544  * The client application provides display windows for the pre-edit data to the
545  * input method which displays into them directly.
546  * If chosen, the input method would require the client to provide some area
547  * values for it to do its preediting. Refer to XIC values XNArea and
548  * XNAreaNeeded.
549  *
550  * XIMPreeditCallbacks (as known as on-the-spot)
551  *
552  * The client application is directed by the IM Server to display all pre-edit
553  * data at the site of text insertion. The client registers callbacks invoked by
554  * the input method during pre-editing.
555  * If chosen, the input method would require the client to define the set of
556  * preedit callbacks. Refer to XIC values XNPreeditStartCallback,
557  * XNPreeditDoneCallback, XNPreeditDrawCallback, and XNPreeditCaretCallback.
558  *
559  * XIMPreeditPosition  (as known as over-the-spot)
560  *
561  * The input method displays pre-edit data in a window which it brings up
562  * directly over the text insertion position.
563  * If chosen, the input method would require the client to provide positional
564  * values. Refer to XIC values XNSpotLocation and XNFocusWindow.
565  *
566  * XIMPreeditNothing   (as known as root-window)
567  *
568  * The input method displays all pre-edit data in a separate area of the screen
569  * in a window specific to the input method.
570  * If chosen, the input method can function without any preedit values.
571  *
572  * XIMPreeditNone      none
573  *
574  * The input method does not provide any preedit feedback. Any preedit value is
575  * ignored. This style is mutually exclusive with the other preedit styles.
576  *
577  *
578  * The status category defines what type of support is provided by the input
579  * method for status information.
580  *
581  * XIMStatusArea
582  *
583  * The input method requires the client to provide some area values for it to do
584  * its status feedback. See XNArea and XNAreaNeeded.
585  *
586  * XIMStatusCallbacks
587  *
588  * The input method requires the client to define the set of status callbacks,
589  * XNStatusStartCallback, XNStatusDoneCallback, and XNStatusDrawCallback.
590  *
591  * XIMStatusNothing
592  *
593  * The input method can function without any status values.
594  *
595  * XIMStatusNone
596  *
597  * The input method does not provide any status feedback. If chosen, any status
598  * value is ignored. This style is mutually exclusive with the other status
599  * styles.
600  */
601 
602   xim->im_styles.count_styles = 6;
603   xim->im_styles.supported_styles = g_malloc (sizeof (XIMStyle) * xim->im_styles.count_styles);
604   /* on-the-spot */
605   xim->im_styles.supported_styles[0] = XIMPreeditCallbacks | XIMStatusNothing;
606   xim->im_styles.supported_styles[1] = XIMPreeditCallbacks | XIMStatusNone;
607   /* over-the-spot */
608   xim->im_styles.supported_styles[2] = XIMPreeditPosition  | XIMStatusNothing;
609   xim->im_styles.supported_styles[3] = XIMPreeditPosition  | XIMStatusNone;
610   /* root-window */
611   xim->im_styles.supported_styles[4] = XIMPreeditNothing   | XIMStatusNothing;
612   xim->im_styles.supported_styles[5] = XIMPreeditNothing   | XIMStatusNone;
613 
614   xim->im_event_mask = KeyPressMask | KeyReleaseMask;
615 
616   XSetWindowAttributes attrs;
617 
618   attrs.event_mask = xim->im_event_mask;
619   attrs.override_redirect = True;
620 
621   xim->im_window = XCreateWindow (xim->display, /* Display *display */
622                                   DefaultRootWindow (xim->display),  /* Window parent */
623                                   0, 0,         /* int x, y */
624                                   1, 1,         /* unsigned int width, height */
625                                   0,            /* unsigned int border_width */
626                                   0,            /* int depth */
627                                   InputOutput,  /* unsigned int class */
628                                   CopyFromParent, /* Visual *visual */
629                                   CWOverrideRedirect | CWEventMask, /* unsigned long valuemask */
630                                   &attrs);      /* XSetWindowAttributes *attributes */
631 
632   if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
633       xim->byte_order = 'l';
634   else
635       xim->byte_order = 'B';
636 
637   _Xi18nInitAttrList  (xim);
638   _Xi18nInitExtension (xim);
639 
640   if (!xi18n_openIM (xim, xim->im_window))
641   {
642     XDestroyWindow (xim->display, xim->im_window);
643     XCloseDisplay  (xim->display);
644     xim->im_window = 0;
645     xim->display = NULL;
646     g_warning (G_STRLOC ": %s: XIM is not started.", G_STRFUNC);
647 
648     return FALSE;
649   }
650 
651   xim->xevent_source = nimf_xevent_source_new (xim);
652   g_source_attach (xim->xevent_source, NULL);
653   XSetErrorHandler (on_xerror);
654 
655   xim->active = TRUE;
656 
657   return TRUE;
658 }
659 
nimf_xim_stop(NimfService * service)660 static void nimf_xim_stop (NimfService *service)
661 {
662   g_debug (G_STRLOC ": %s", G_STRFUNC);
663 
664   NimfXim *xim = NIMF_XIM (service);
665 
666   if (!xim->active)
667     return;
668 
669   if (xim->xevent_source)
670   {
671     g_source_destroy (xim->xevent_source);
672     g_source_unref   (xim->xevent_source);
673   }
674 
675   if (xim->im_window)
676   {
677     XDestroyWindow (xim->display, xim->im_window);
678     xim->im_window = 0;
679   }
680 
681   g_free (xim->im_styles.supported_styles);
682 
683   xi18n_closeIM (xim);
684 
685   if (xim->display)
686   {
687     XCloseDisplay (xim->display);
688     xim->display = NULL;
689   }
690 
691   xim->active = FALSE;
692 }
693 
694 static void
nimf_xim_init(NimfXim * xim)695 nimf_xim_init (NimfXim *xim)
696 {
697   g_debug (G_STRLOC ": %s", G_STRFUNC);
698 
699   xim->id  = g_strdup ("nimf-xim");
700   xim->ics = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
701                                     (GDestroyNotify) g_object_unref);
702 }
703 
nimf_xim_finalize(GObject * object)704 static void nimf_xim_finalize (GObject *object)
705 {
706   g_debug (G_STRLOC ": %s", G_STRFUNC);
707 
708   NimfService *service = NIMF_SERVICE (object);
709   NimfXim     *xim     = NIMF_XIM (object);
710 
711   if (nimf_xim_is_active (service))
712     nimf_xim_stop (service);
713 
714   g_hash_table_unref (xim->ics);
715   g_free (xim->id);
716 
717   G_OBJECT_CLASS (nimf_xim_parent_class)->finalize (object);
718 }
719 
720 static void
nimf_xim_class_init(NimfXimClass * class)721 nimf_xim_class_init (NimfXimClass *class)
722 {
723   g_debug (G_STRLOC ": %s", G_STRFUNC);
724 
725   GObjectClass     *object_class  = G_OBJECT_CLASS (class);
726   NimfServiceClass *service_class = NIMF_SERVICE_CLASS (class);
727 
728   service_class->get_id              = nimf_xim_get_id;
729   service_class->start               = nimf_xim_start;
730   service_class->stop                = nimf_xim_stop;
731   service_class->is_active           = nimf_xim_is_active;
732   service_class->change_engine       = nimf_xim_change_engine;
733   service_class->change_engine_by_id = nimf_xim_change_engine_by_id;
734 
735   object_class->finalize = nimf_xim_finalize;
736 }
737 
738 static void
nimf_xim_class_finalize(NimfXimClass * class)739 nimf_xim_class_finalize (NimfXimClass *class)
740 {
741   g_debug (G_STRLOC ": %s", G_STRFUNC);
742 }
743 
module_register_type(GTypeModule * type_module)744 void module_register_type (GTypeModule *type_module)
745 {
746   g_debug (G_STRLOC ": %s", G_STRFUNC);
747 
748   nimf_xim_register_type (type_module);
749 }
750 
module_get_type()751 GType module_get_type ()
752 {
753   g_debug (G_STRLOC ": %s", G_STRFUNC);
754 
755   return nimf_xim_get_type ();
756 }
757