1 /*
2     SPDX-FileCopyrightText: 2011-2012 Ni Hui <shuizhuyuanluo@126.com>
3     SPDX-FileCopyrightText: 2013-2014 Weng Xuetian <wengxt@gmail.com>
4 
5     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
6 */
7 
8 #include "panel.h"
9 #include "app.h"
10 #include "enginemanager.h"
11 #include "gtkaccelparse_p.h"
12 #include "propertymanager.h"
13 #include "xkblayoutmanager.h"
14 #include <QByteArray>
15 #include <QDebug>
16 #include <QPair>
17 #include <QStringList>
18 #include <locale.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <string>
22 
23 #ifndef DBUS_ERROR_FAILED
24 #define DBUS_ERROR_FAILED "org.freedesktop.DBus.Error.Failed"
25 #endif /* DBUS_ERROR_FAILED */
26 
27 #define IBUS_SCHEMA_GENERAL "org.freedesktop.ibus.general"
28 #define IBUS_SCHEMA_HOTKEY "org.freedesktop.ibus.general.hotkey"
29 #define IBUS_SCHEMA_PANEL "org.freedesktop.ibus.panel"
30 
31 typedef struct _IBusPanelImpanelClass IBusPanelImpanelClass;
32 
33 struct _IBusPanelImpanel {
34     IBusPanelService parent;
35     IBusBus *bus;
36     GDBusConnection *conn;
37     PropertyManager *propManager;
38     EngineManager *engineManager;
39     XkbLayoutManager *xkbLayoutManager;
40     App *app;
41     gboolean useSystemKeyboardLayout;
42     int selected;
43     GSettings *settings_general;
44     GSettings *settings_hotkey;
45 };
46 
47 struct _IBusPanelImpanelClass {
48     IBusPanelServiceClass parent;
49 };
50 
51 /* functions prototype */
52 static void ibus_panel_impanel_class_init(IBusPanelImpanelClass *klass);
53 static void ibus_panel_impanel_init(IBusPanelImpanel *impanel);
54 static void ibus_panel_impanel_destroy(IBusPanelImpanel *impanel);
55 
56 static void ibus_panel_impanel_focus_in(IBusPanelService *panel, const gchar *input_context_path);
57 static void ibus_panel_impanel_focus_out(IBusPanelService *panel, const gchar *input_context_path);
58 static void ibus_panel_impanel_register_properties(IBusPanelService *panel, IBusPropList *prop_list);
59 static void ibus_panel_impanel_real_register_properties(IBusPanelImpanel *impanel);
60 static void ibus_panel_impanel_set_cursor_location(IBusPanelService *panel, gint x, gint y, gint w, gint h);
61 static void ibus_panel_impanel_update_auxiliary_text(IBusPanelService *panel, IBusText *text, gboolean visible);
62 static void ibus_panel_impanel_update_lookup_table(IBusPanelService *panel, IBusLookupTable *lookup_table, gboolean visible);
63 static void ibus_panel_impanel_update_preedit_text(IBusPanelService *panel, IBusText *text, guint cursor_pos, gboolean visible);
64 static void ibus_panel_impanel_update_property(IBusPanelService *panel, IBusProperty *prop);
65 static void ibus_panel_impanel_cursor_down_lookup_table(IBusPanelService *panel);
66 static void ibus_panel_impanel_cursor_up_lookup_table(IBusPanelService *panel);
67 static void ibus_panel_impanel_hide_auxiliary_text(IBusPanelService *panel);
68 static void ibus_panel_impanel_hide_language_bar(IBusPanelService *panel);
69 static void ibus_panel_impanel_hide_lookup_table(IBusPanelService *panel);
70 static void ibus_panel_impanel_hide_preedit_text(IBusPanelService *panel);
71 static void ibus_panel_impanel_page_down_lookup_table(IBusPanelService *panel);
72 static void ibus_panel_impanel_page_up_lookup_table(IBusPanelService *panel);
73 static void ibus_panel_impanel_reset(IBusPanelService *panel);
74 static void ibus_panel_impanel_show_auxiliary_text(IBusPanelService *panel);
75 static void ibus_panel_impanel_show_language_bar(IBusPanelService *panel);
76 static void ibus_panel_impanel_show_lookup_table(IBusPanelService *panel);
77 static void ibus_panel_impanel_show_preedit_text(IBusPanelService *panel);
78 static void ibus_panel_impanel_start_setup(IBusPanelService *panel);
79 static void ibus_panel_impanel_state_changed(IBusPanelService *panel);
80 
81 /* impanel signal handler function */
82 static void ibus_panel_impanel_exec_im_menu(IBusPanelImpanel *impanel);
83 static void ibus_panel_impanel_exec_menu(IBusPanelImpanel *impanel, IBusPropList *prop_list);
84 
85 static void impanel_set_engine(IBusPanelImpanel *impanel, const char *name);
86 
87 static QByteArray ibus_property_to_propstr(IBusProperty *property, gboolean useSymbol = FALSE);
88 
89 static QByteArray ibus_engine_desc_to_logo_propstr(IBusEngineDesc *engine);
90 
impanel_update_logo_by_engine(IBusPanelImpanel * impanel,IBusEngineDesc * engine_desc)91 void impanel_update_logo_by_engine(IBusPanelImpanel *impanel, IBusEngineDesc *engine_desc)
92 {
93     if (!impanel->conn) {
94         return;
95     }
96 
97     QByteArray propstr = ibus_engine_desc_to_logo_propstr(engine_desc);
98 
99     g_dbus_connection_emit_signal(impanel->conn,
100                                   nullptr,
101                                   "/kimpanel",
102                                   "org.kde.kimpanel.inputmethod",
103                                   "UpdateProperty",
104                                   (g_variant_new("(s)", propstr.constData())),
105                                   nullptr);
106 }
107 
ibus_panel_impanel_set_bus(IBusPanelImpanel * impanel,IBusBus * bus)108 void ibus_panel_impanel_set_bus(IBusPanelImpanel *impanel, IBusBus *bus)
109 {
110     impanel->bus = bus;
111 }
112 
ibus_panel_impanel_set_app(IBusPanelImpanel * impanel,App * app)113 void ibus_panel_impanel_set_app(IBusPanelImpanel *impanel, App *app)
114 {
115     impanel->app = app;
116 }
117 
ibus_panel_impanel_accept(IBusPanelImpanel * impanel)118 void ibus_panel_impanel_accept(IBusPanelImpanel *impanel)
119 {
120     if (impanel->selected >= 0 && static_cast<size_t>(impanel->selected) < impanel->engineManager->length()) {
121         impanel_set_engine(impanel, ibus_engine_desc_get_name(impanel->engineManager->engines()[impanel->selected]));
122         impanel->selected = -1;
123     }
124 }
125 
ibus_panel_impanel_navigate(IBusPanelImpanel * impanel,gboolean start,gboolean forward)126 void ibus_panel_impanel_navigate(IBusPanelImpanel *impanel, gboolean start, gboolean forward)
127 {
128     if (start) {
129         impanel->selected = -1;
130     }
131 
132     if (impanel->engineManager->length() < 2) {
133         return;
134     }
135 
136     IBusEngineDesc *engine_desc = nullptr;
137     if (impanel->selected < 0) {
138         engine_desc = ibus_bus_get_global_engine(impanel->bus);
139     } else if (static_cast<size_t>(impanel->selected) < impanel->engineManager->length()) {
140         engine_desc = impanel->engineManager->engines()[impanel->selected];
141         g_object_ref(engine_desc);
142     }
143 
144     if (!engine_desc) {
145         engine_desc = impanel->engineManager->engines()[0];
146         g_object_ref(engine_desc);
147     }
148 
149     if (engine_desc) {
150         const char *name = impanel->engineManager->navigate(engine_desc, forward);
151         impanel->selected = impanel->engineManager->getIndexByName(name);
152         g_object_unref(engine_desc);
153     } else {
154         return;
155     }
156 
157     if (impanel->selected >= 0 && static_cast<size_t>(impanel->selected) < impanel->engineManager->length()) {
158         ibus_panel_impanel_real_register_properties(impanel);
159     }
160 }
161 
ibus_panel_impanel_move_next(IBusPanelImpanel * impanel)162 void ibus_panel_impanel_move_next(IBusPanelImpanel *impanel)
163 {
164     if (impanel->engineManager->length() >= 2) {
165         impanel_set_engine(impanel, ibus_engine_desc_get_name(impanel->engineManager->engines()[1]));
166     }
167 }
168 
169 static GDBusNodeInfo *introspection_data = nullptr;
170 
171 static guint owner_id;
172 
173 static const gchar introspection_xml[] =
174     "<node>"
175     "  <interface name='org.kde.kimpanel.inputmethod'>"
176     "    <signal name='Enable'>"
177     "      <arg type='b' name='enable'/>"
178     "    </signal>"
179     "    <signal name='RegisterProperties'>"
180     "      <arg type='as' name='prop'/>"
181     "    </signal>"
182     "    <signal name='UpdateProperty'>"
183     "      <arg type='s' name='prop'/>"
184     "    </signal>"
185     "    <signal name='RemoveProperty'>"
186     "      <arg type='s' name='prop'/>"
187     "    </signal>"
188     "    <signal name='ShowAux'>"
189     "      <arg type='b' name='toshow'/>"
190     "    </signal>"
191     "    <signal name='ShowLookupTable'>"
192     "      <arg type='b' name='toshow'/>"
193     "    </signal>"
194     "    <signal name='ShowPreedit'>"
195     "      <arg type='b' name='toshow'/>"
196     "    </signal>"
197     "    <signal name='UpdateAux'>"
198     "      <arg type='s' name='text'/>"
199     "      <arg type='s' name='attr'/>"
200     "    </signal>"
201     "    <signal name='UpdateLookupTableCursor'>"
202     "      <arg type='i' name='pos'/>"
203     "    </signal>"
204     "    <signal name='UpdateLookupTable'>"
205     "      <arg type='as' name='labels'/>"
206     "      <arg type='as' name='candidates'/>"
207     "      <arg type='as' name='attrs'/>"
208     "      <arg type='b' name='hasprev'/>"
209     "      <arg type='b' name='hasnext'/>"
210     "    </signal>"
211     "    <signal name='UpdatePreeditCaret'>"
212     "      <arg type='i' name='pos'/>"
213     "    </signal>"
214     "    <signal name='UpdatePreeditText'>"
215     "      <arg type='s' name='text'/>"
216     "      <arg type='s' name='attr'/>"
217     "    </signal>"
218     "    <signal name='UpdateSpotLocation'>"
219     "      <arg type='i' name='x'/>"
220     "      <arg type='i' name='y'/>"
221     "    </signal>"
222     "    <signal name='ExecMenu'>"
223     "      <arg type='as' name='actions'/>"
224     "    </signal>"
225     "  </interface>"
226     "</node>";
227 
228 static const char prop_sep[] = ":";
229 
ibus_property_args_to_propstr(const char * key,const char * label,const char * icon,const char * tooltip,const char * hint="")230 static QByteArray ibus_property_args_to_propstr(const char *key, const char *label, const char *icon, const char *tooltip, const char *hint = "")
231 {
232     QByteArray propstr("/IBus/");
233     QByteArray str(key);
234     str.replace(':', '!');
235 
236     App *app = static_cast<App *>(qApp);
237 
238     propstr += str;
239     propstr += prop_sep;
240     propstr += QByteArray(label).replace(':', '-').constData();
241     propstr += prop_sep;
242     propstr += app->normalizeIconName(QByteArray(icon).replace(':', '-'));
243     propstr += prop_sep;
244     propstr += QByteArray(tooltip).replace(':', '-').constData();
245     propstr += prop_sep;
246     propstr += QByteArray(hint).replace(':', '-').constData();
247 
248     return propstr;
249 }
250 
ibus_engine_desc_to_logo_propstr(IBusEngineDesc * engine)251 static QByteArray ibus_engine_desc_to_logo_propstr(IBusEngineDesc *engine)
252 {
253     const gchar *label = "IBus";
254     const gchar *tooltip = "";
255     const gchar *icon = "input-keyboard";
256 
257     gchar xkbLabel[3];
258     if (engine) {
259         const gchar *iconname = ibus_engine_desc_get_icon(engine);
260         if (iconname && iconname[0]) {
261             icon = iconname;
262         }
263 
264         if (strncmp("xkb:", ibus_engine_desc_get_name(engine), 4) == 0) {
265             strncpy(xkbLabel, ibus_engine_desc_get_name(engine) + 4, 2);
266             xkbLabel[2] = 0;
267             int i = 0;
268             while (xkbLabel[i]) {
269                 if (xkbLabel[i] == ':') {
270                     xkbLabel[i] = 0;
271                 }
272                 i++;
273             }
274             label = xkbLabel;
275             icon = "";
276         }
277 
278         const gchar *longname = ibus_engine_desc_get_longname(engine);
279         if (longname && longname[0]) {
280             tooltip = longname;
281         }
282     }
283 
284     return ibus_property_args_to_propstr("Logo", label, icon, tooltip);
285 }
286 
ibus_property_to_propstr(IBusProperty * property,gboolean useSymbol)287 static QByteArray ibus_property_to_propstr(IBusProperty *property, gboolean useSymbol)
288 {
289     const gchar *label = nullptr;
290     const gchar *tooltip = ibus_text_get_text(ibus_property_get_tooltip(property));
291     const gchar *icon = ibus_property_get_icon(property);
292 
293     if (useSymbol) {
294         label = ibus_text_get_text(ibus_property_get_symbol(property));
295         if (!label || label[0] == '\0') {
296             label = ibus_text_get_text(ibus_property_get_label(property));
297         }
298     } else {
299         label = ibus_text_get_text(ibus_property_get_label(property));
300     }
301 
302     const char *hint = "";
303     if (ibus_property_get_prop_type(property) == PROP_TYPE_TOGGLE) {
304         if (ibus_property_get_state(property) != PROP_STATE_CHECKED) {
305             hint = "disable";
306         }
307     } else if (ibus_property_get_prop_type(property) == PROP_TYPE_RADIO) {
308         if (ibus_property_get_state(property) == PROP_STATE_CHECKED) {
309             hint = "checked";
310         }
311     }
312 
313     return ibus_property_args_to_propstr(ibus_property_get_key(property), label, icon, tooltip, hint);
314 }
315 
ibus_engine_desc_args_to_propstr(const char * name,const char * language,const char * longname,const char * icon,const char * description)316 static QByteArray ibus_engine_desc_args_to_propstr(const char *name, const char *language, const char *longname, const char *icon, const char *description)
317 {
318     QByteArray propstr("/IBus/Engine/");
319     QByteArray data(name);
320     data.replace(':', '!');
321     propstr += data;
322     propstr += prop_sep;
323     if (language) {
324         propstr += language;
325         propstr += " - ";
326     }
327     propstr += longname;
328     propstr += prop_sep;
329     propstr += icon;
330     propstr += prop_sep;
331     propstr += description;
332     return propstr;
333 }
334 
ibus_engine_desc_to_propstr(IBusEngineDesc * engine_desc)335 static QByteArray ibus_engine_desc_to_propstr(IBusEngineDesc *engine_desc)
336 {
337     return ibus_engine_desc_args_to_propstr(ibus_engine_desc_get_name(engine_desc),
338                                             ibus_engine_desc_get_language(engine_desc),
339                                             ibus_engine_desc_get_longname(engine_desc),
340                                             ibus_engine_desc_get_icon(engine_desc),
341                                             ibus_engine_desc_get_description(engine_desc));
342 }
343 
impanel_get_default_engine(IBusPanelImpanel * impanel,char *** pengine_names,gsize * plen)344 static void impanel_get_default_engine(IBusPanelImpanel *impanel, char ***pengine_names, gsize *plen)
345 {
346     GList *engines = ibus_bus_list_engines(impanel->bus);
347     if (!engines) {
348         *pengine_names = g_new0(gchar *, 2);
349         *plen = 1;
350         (*pengine_names)[0] = g_strdup("xkb:us::eng");
351         return;
352     }
353 
354     QList<QByteArray> engineList;
355     impanel->xkbLayoutManager->getLayout();
356     QStringList layouts = impanel->xkbLayoutManager->defaultLayout().split(QLatin1Char{','});
357     QStringList variants = impanel->xkbLayoutManager->defaultVariant().split(QLatin1Char{','});
358 
359     for (int i = 0; i < layouts.size(); i++) {
360         QString variant;
361         if (i < variants.size()) {
362             variant = variants[i];
363         }
364 
365         for (GList *engine = g_list_first(engines); engine != nullptr; engine = g_list_next(engine)) {
366             IBusEngineDesc *desc = IBUS_ENGINE_DESC(engine->data);
367             QByteArray name = ibus_engine_desc_get_name(desc);
368             if (!name.startsWith("xkb:")) {
369                 continue;
370             }
371 
372             if (QLatin1String(ibus_engine_desc_get_layout(desc)) == layouts[i] && QLatin1String(ibus_engine_desc_get_layout_variant(desc)) == variant) {
373                 engineList << name;
374             }
375         }
376     }
377     const char *locale = setlocale(LC_CTYPE, nullptr);
378     if (!locale) {
379         locale = "C";
380     }
381 
382     QStringList localeList = QString::fromLocal8Bit(locale).split(QLatin1Char{'.'});
383     const QString lang = localeList.size() > 0 ? localeList.at(0) : QString{};
384 
385     bool added = false;
386     for (GList *engine = g_list_first(engines); engine != nullptr; engine = g_list_next(engine)) {
387         IBusEngineDesc *desc = IBUS_ENGINE_DESC(engine->data);
388         QByteArray name = ibus_engine_desc_get_name(desc);
389         if (name.startsWith("xkb:")) {
390             continue;
391         }
392 
393         if (QLatin1String(ibus_engine_desc_get_language(desc)) == lang && ibus_engine_desc_get_rank(desc) > 0) {
394             engineList << name;
395             added = true;
396         }
397     }
398 
399     if (!added) {
400         localeList = QString(lang).split(QLatin1Char{'_'});
401         QString _lang = localeList.size() > 0 ? localeList.at(0) : QString{};
402 
403         for (GList *engine = g_list_first(engines); engine != nullptr; engine = g_list_next(engine)) {
404             IBusEngineDesc *desc = IBUS_ENGINE_DESC(engine->data);
405             QByteArray name = ibus_engine_desc_get_name(desc);
406             if (name.startsWith("xkb:")) {
407                 continue;
408             }
409 
410             if (QLatin1String(ibus_engine_desc_get_language(desc)) == _lang && ibus_engine_desc_get_rank(desc) > 0) {
411                 engineList << name;
412             }
413         }
414     }
415 
416     for (GList *engine = g_list_first(engines); engine != nullptr; engine = g_list_next(engine)) {
417         IBusEngineDesc *desc = IBUS_ENGINE_DESC(engine->data);
418         g_object_unref(desc);
419     }
420 
421     g_list_free(engines);
422 
423     if (engineList.size() == 0) {
424         *pengine_names = g_new0(gchar *, 2);
425         *plen = 1;
426         (*pengine_names)[0] = g_strdup("xkb:us::eng");
427         return;
428     } else {
429         *pengine_names = g_new0(gchar *, engineList.size() + 1);
430         *plen = engineList.size();
431         size_t i = 0;
432         Q_FOREACH (const QByteArray &name, engineList) {
433             (*pengine_names)[i] = g_strdup(name.constData());
434             i++;
435         }
436     }
437 }
438 
contains(gchar ** strlist,const gchar * str)439 bool contains(gchar **strlist, const gchar *str)
440 {
441     for (; strlist; ++strlist) {
442         if (g_strcmp0(*strlist, str) == 0)
443             return true;
444     }
445     return false;
446 }
447 
impanel_update_engines(IBusPanelImpanel * impanel,GVariant * var_engines)448 static void impanel_update_engines(IBusPanelImpanel *impanel, GVariant *var_engines)
449 {
450     gchar **engine_names = nullptr;
451     size_t len = 0;
452     if (var_engines) {
453         engine_names = g_variant_dup_strv(var_engines, &len);
454     }
455     if (len == 0) {
456         g_strfreev(engine_names);
457         engine_names = nullptr;
458     }
459 
460     if (!engine_names) {
461         impanel_get_default_engine(impanel, &engine_names, &len);
462         GVariant *var = g_variant_new_strv(engine_names, len);
463         g_settings_set_value(impanel->settings_general, "preload-engines", var);
464     }
465 
466     IBusEngineDesc **engines = ibus_bus_get_engines_by_names(impanel->bus, engine_names);
467 
468     impanel->engineManager->setEngines(engines);
469     if (engines && engines[0]
470         && (!ibus_bus_get_global_engine(impanel->bus) || !contains(engine_names, ibus_engine_desc_get_name(ibus_bus_get_global_engine(impanel->bus))))) {
471         ibus_bus_set_global_engine(impanel->bus, ibus_engine_desc_get_name(engines[0]));
472     }
473     g_strfreev(engine_names);
474 
475     impanel->app->setDoGrab(len > 1);
476 }
477 
impanel_update_engines_order(IBusPanelImpanel * impanel,GVariant * var_engines)478 static void impanel_update_engines_order(IBusPanelImpanel *impanel, GVariant *var_engines)
479 {
480     const gchar **engine_names = nullptr;
481     size_t len = 0;
482     engine_names = g_variant_get_strv(var_engines, &len);
483     if (len) {
484         impanel->engineManager->setOrder(engine_names, len);
485 
486         if (impanel->engineManager->engines()) {
487             ibus_bus_set_global_engine(impanel->bus, ibus_engine_desc_get_name(impanel->engineManager->engines()[0]));
488         }
489     }
490     g_free(engine_names);
491 }
492 
impanel_update_triggers(IBusPanelImpanel * impanel,GVariant * variant)493 static void impanel_update_triggers(IBusPanelImpanel *impanel, GVariant *variant)
494 {
495     gchar **triggers = nullptr;
496     size_t len = 0;
497     if (variant) {
498         triggers = g_variant_dup_strv(variant, &len);
499     }
500     if (len == 0) {
501         g_strfreev(triggers);
502         triggers = nullptr;
503     }
504     if (!triggers) {
505         triggers = g_new0(gchar *, 2);
506         len = 1;
507         triggers[0] = g_strdup("<Super>space");
508     }
509 
510     QList<QPair<uint, uint>> triggersList;
511     for (size_t i = 0; i < len; i++) {
512         guint key = 0;
513         GdkModifierType mod = (GdkModifierType)0;
514         _gtk_accelerator_parse(triggers[i], &key, &mod);
515         if (key) {
516             triggersList << qMakePair<uint, uint>(key, (uint)mod);
517         }
518     }
519     impanel->app->setTriggerKeys(triggersList);
520 }
521 
impanel_update_use_system_keyboard_layout(IBusPanelImpanel * impanel,GVariant * variant)522 static void impanel_update_use_system_keyboard_layout(IBusPanelImpanel *impanel, GVariant *variant)
523 {
524     impanel->useSystemKeyboardLayout = g_variant_get_boolean(variant);
525 }
impanel_update_use_global_engine(IBusPanelImpanel * impanel,GVariant * variant)526 static void impanel_update_use_global_engine(IBusPanelImpanel *impanel, GVariant *variant)
527 {
528     impanel->engineManager->setUseGlobalEngine(g_variant_get_boolean(variant));
529 }
530 
impanel_update_latin_layouts(IBusPanelImpanel * impanel,GVariant * variant)531 static void impanel_update_latin_layouts(IBusPanelImpanel *impanel, GVariant *variant)
532 {
533     if (!variant) {
534         return;
535     }
536     gsize length;
537     const gchar **variants = g_variant_get_strv(variant, &length);
538 
539     impanel->xkbLayoutManager->setLatinLayouts(variants, length);
540     g_free(variants);
541 }
542 
impanel_settings_changed_callback(GSettings * settings,const gchar * key,gpointer user_data)543 static void impanel_settings_changed_callback(GSettings *settings, const gchar *key, gpointer user_data)
544 {
545     IBusPanelImpanel *impanel = ((IBusPanelImpanel *)user_data);
546     gchar *schema = nullptr;
547     GVariant *value = g_settings_get_value(settings, key);
548 
549     g_object_get(G_OBJECT(settings), "schema", &schema, NULL);
550 
551     if (g_strcmp0(schema, IBUS_SCHEMA_GENERAL) == 0 && g_strcmp0(key, "preload-engines") == 0) {
552         impanel_update_engines(impanel, value);
553     } else if (g_strcmp0(schema, IBUS_SCHEMA_HOTKEY) == 0 && g_strcmp0(key, "triggers") == 0) {
554         impanel_update_triggers(impanel, value);
555     } else if (g_strcmp0(schema, IBUS_SCHEMA_GENERAL) == 0 && g_strcmp0(key, "use-system-keyboard-layout") == 0) {
556         impanel_update_use_system_keyboard_layout(impanel, value);
557     } else if (g_strcmp0(schema, IBUS_SCHEMA_GENERAL) == 0 && g_strcmp0(key, "use-global-engine") == 0) {
558         impanel_update_use_global_engine(impanel, value);
559     }
560     g_free(schema);
561 }
562 
impanel_exit_callback(GDBusConnection * connection,const gchar * sender_name,const gchar * object_path,const gchar * interface_name,const gchar * signal_name,GVariant * parameters,gpointer user_data)563 static void impanel_exit_callback(GDBusConnection *connection,
564                                   const gchar *sender_name,
565                                   const gchar *object_path,
566                                   const gchar *interface_name,
567                                   const gchar *signal_name,
568                                   GVariant *parameters,
569                                   gpointer user_data)
570 {
571     Q_UNUSED(connection);
572     Q_UNUSED(sender_name);
573     Q_UNUSED(object_path);
574     Q_UNUSED(interface_name);
575     Q_UNUSED(signal_name);
576     Q_UNUSED(parameters);
577     IBusPanelImpanel *impanel = ((IBusPanelImpanel *)user_data);
578     if (impanel->bus) {
579         ibus_bus_exit(impanel->bus, FALSE);
580     }
581 }
582 
impanel_panel_created_callback(GDBusConnection * connection,const gchar * sender_name,const gchar * object_path,const gchar * interface_name,const gchar * signal_name,GVariant * parameters,gpointer user_data)583 static void impanel_panel_created_callback(GDBusConnection *connection,
584                                            const gchar *sender_name,
585                                            const gchar *object_path,
586                                            const gchar *interface_name,
587                                            const gchar *signal_name,
588                                            GVariant *parameters,
589                                            gpointer user_data)
590 {
591     Q_UNUSED(connection);
592     Q_UNUSED(sender_name);
593     Q_UNUSED(object_path);
594     Q_UNUSED(interface_name);
595     Q_UNUSED(signal_name);
596     Q_UNUSED(parameters);
597     IBusPanelImpanel *impanel = ((IBusPanelImpanel *)user_data);
598     ibus_panel_impanel_real_register_properties(impanel);
599 }
600 
impanel_set_engine(IBusPanelImpanel * impanel,const char * name)601 static void impanel_set_engine(IBusPanelImpanel *impanel, const char *name)
602 {
603     if (!name || !name[0]) {
604         return;
605     }
606     if (ibus_bus_set_global_engine(impanel->bus, name)) {
607         if (!impanel->useSystemKeyboardLayout) {
608             IBusEngineDesc *engine_desc = ibus_bus_get_global_engine(impanel->bus);
609             if (engine_desc) {
610                 impanel->xkbLayoutManager->setLayout(engine_desc);
611             }
612             g_object_unref(engine_desc);
613         }
614         impanel->engineManager->setCurrentEngine(name);
615     } else {
616         qDebug() << "set engine failed.";
617     }
618 }
619 
impanel_trigger_property_callback(GDBusConnection * connection,const gchar * sender_name,const gchar * object_path,const gchar * interface_name,const gchar * signal_name,GVariant * parameters,gpointer user_data)620 static void impanel_trigger_property_callback(GDBusConnection *connection,
621                                               const gchar *sender_name,
622                                               const gchar *object_path,
623                                               const gchar *interface_name,
624                                               const gchar *signal_name,
625                                               GVariant *parameters,
626                                               gpointer user_data)
627 {
628     Q_UNUSED(connection);
629     Q_UNUSED(sender_name);
630     Q_UNUSED(object_path);
631     Q_UNUSED(interface_name);
632     Q_UNUSED(signal_name);
633     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(user_data);
634     gchar *s0 = nullptr;
635     g_variant_get(parameters, "(s)", &s0);
636     if (!s0 || strlen(s0) <= 6)
637         return;
638     QByteArray prop_key(s0 + 6); // +6 to skip "/IBus/"
639     prop_key.replace('!', ':');
640     if (g_ascii_strncasecmp(prop_key.constData(), "Logo", 4) == 0)
641         ibus_panel_impanel_exec_im_menu(impanel);
642     else if (g_ascii_strncasecmp(prop_key.constData(), "Engine/", 7) == 0) {
643         impanel_set_engine(impanel, prop_key.constData() + 7);
644     } else {
645         IBusProperty *property = impanel->propManager->property(prop_key.constData());
646         if (property) {
647             IBusPropState newstate = ibus_property_get_state(property);
648             switch (ibus_property_get_prop_type(property)) {
649             case PROP_TYPE_RADIO:
650             case PROP_TYPE_TOGGLE:
651                 if (ibus_property_get_prop_type(property) == PROP_TYPE_TOGGLE) {
652                     if (newstate == PROP_STATE_CHECKED)
653                         newstate = PROP_STATE_UNCHECKED;
654                     else if (newstate == PROP_STATE_UNCHECKED)
655                         newstate = PROP_STATE_CHECKED;
656                 } else if (ibus_property_get_prop_type(property) == PROP_TYPE_RADIO) {
657                     newstate = PROP_STATE_CHECKED;
658                 }
659                 Q_FALLTHROUGH();
660             case PROP_TYPE_NORMAL:
661                 ibus_property_set_state(property, newstate);
662                 ibus_panel_service_property_activate((IBusPanelService *)impanel, prop_key.constData(), newstate);
663                 break;
664             case PROP_TYPE_MENU:
665                 ibus_panel_impanel_exec_menu(impanel, ibus_property_get_sub_props(property));
666             case PROP_TYPE_SEPARATOR:
667                 break;
668             default:
669                 break;
670             }
671         } else {
672             ibus_panel_service_property_activate((IBusPanelService *)impanel, prop_key.constData(), PROP_STATE_CHECKED);
673         }
674     }
675     g_free(s0);
676 }
677 
impanel_select_candidate_callback(GDBusConnection * connection,const gchar * sender_name,const gchar * object_path,const gchar * interface_name,const gchar * signal_name,GVariant * parameters,gpointer user_data)678 static void impanel_select_candidate_callback(GDBusConnection *connection,
679                                               const gchar *sender_name,
680                                               const gchar *object_path,
681                                               const gchar *interface_name,
682                                               const gchar *signal_name,
683                                               GVariant *parameters,
684                                               gpointer user_data)
685 {
686     Q_UNUSED(connection);
687     Q_UNUSED(sender_name);
688     Q_UNUSED(object_path);
689     Q_UNUSED(interface_name);
690     Q_UNUSED(signal_name);
691 
692     gint i;
693     g_variant_get(parameters, "(i)", &i);
694     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(user_data);
695     ibus_panel_service_candidate_clicked((IBusPanelService *)impanel, i, 0, 0);
696 }
697 
impanel_prev_page_callback(GDBusConnection * connection,const gchar * sender_name,const gchar * object_path,const gchar * interface_name,const gchar * signal_name,GVariant * parameters,gpointer user_data)698 static void impanel_prev_page_callback(GDBusConnection *connection,
699                                        const gchar *sender_name,
700                                        const gchar *object_path,
701                                        const gchar *interface_name,
702                                        const gchar *signal_name,
703                                        GVariant *parameters,
704                                        gpointer user_data)
705 {
706     Q_UNUSED(connection);
707     Q_UNUSED(sender_name);
708     Q_UNUSED(object_path);
709     Q_UNUSED(interface_name);
710     Q_UNUSED(signal_name);
711     Q_UNUSED(parameters);
712 
713     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(user_data);
714     ibus_panel_service_page_up((IBusPanelService *)impanel);
715 }
716 
impanel_next_page_callback(GDBusConnection * connection,const gchar * sender_name,const gchar * object_path,const gchar * interface_name,const gchar * signal_name,GVariant * parameters,gpointer user_data)717 static void impanel_next_page_callback(GDBusConnection *connection,
718                                        const gchar *sender_name,
719                                        const gchar *object_path,
720                                        const gchar *interface_name,
721                                        const gchar *signal_name,
722                                        GVariant *parameters,
723                                        gpointer user_data)
724 {
725     Q_UNUSED(connection);
726     Q_UNUSED(sender_name);
727     Q_UNUSED(object_path);
728     Q_UNUSED(interface_name);
729     Q_UNUSED(signal_name);
730     Q_UNUSED(parameters);
731 
732     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(user_data);
733     ibus_panel_service_page_down((IBusPanelService *)impanel);
734 }
735 
impanel_configure_callback(GDBusConnection * connection,const gchar * sender_name,const gchar * object_path,const gchar * interface_name,const gchar * signal_name,GVariant * parameters,gpointer user_data)736 static void impanel_configure_callback(GDBusConnection *connection,
737                                        const gchar *sender_name,
738                                        const gchar *object_path,
739                                        const gchar *interface_name,
740                                        const gchar *signal_name,
741                                        GVariant *parameters,
742                                        gpointer user_data)
743 {
744     Q_UNUSED(connection);
745     Q_UNUSED(sender_name);
746     Q_UNUSED(object_path);
747     Q_UNUSED(interface_name);
748     Q_UNUSED(signal_name);
749     Q_UNUSED(parameters);
750     Q_UNUSED(user_data);
751     pid_t pid = fork();
752     if (pid == 0) {
753         execlp("ibus-setup", "ibus-setup", (char *)nullptr);
754         exit(0);
755     }
756 }
757 
on_bus_acquired(GDBusConnection * connection,const gchar * name,gpointer user_data)758 static void on_bus_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data)
759 {
760     Q_UNUSED(name);
761     IBusPanelImpanel *impanel = ((IBusPanelImpanel *)user_data);
762     impanel->conn = connection;
763 
764     g_dbus_connection_register_object(connection,
765                                       "/kimpanel",
766                                       introspection_data->interfaces[0],
767                                       nullptr, /*&interface_vtable*/
768                                       nullptr, /* user_data */
769                                       nullptr, /* user_data_free_func */
770                                       nullptr); /* GError** */
771 
772     g_dbus_connection_signal_subscribe(connection,
773                                        "org.kde.impanel",
774                                        "org.kde.impanel",
775                                        "TriggerProperty",
776                                        "/org/kde/impanel",
777                                        nullptr,
778                                        G_DBUS_SIGNAL_FLAGS_NONE,
779                                        impanel_trigger_property_callback,
780                                        user_data,
781                                        nullptr);
782     g_dbus_connection_signal_subscribe(connection,
783                                        "org.kde.impanel",
784                                        "org.kde.impanel",
785                                        "SelectCandidate",
786                                        "/org/kde/impanel",
787                                        nullptr,
788                                        G_DBUS_SIGNAL_FLAGS_NONE,
789                                        impanel_select_candidate_callback,
790                                        user_data,
791                                        nullptr);
792     g_dbus_connection_signal_subscribe(connection,
793                                        "org.kde.impanel",
794                                        "org.kde.impanel",
795                                        "LookupTablePageUp",
796                                        "/org/kde/impanel",
797                                        nullptr,
798                                        G_DBUS_SIGNAL_FLAGS_NONE,
799                                        impanel_prev_page_callback,
800                                        user_data,
801                                        nullptr);
802     g_dbus_connection_signal_subscribe(connection,
803                                        "org.kde.impanel",
804                                        "org.kde.impanel",
805                                        "LookupTablePageDown",
806                                        "/org/kde/impanel",
807                                        nullptr,
808                                        G_DBUS_SIGNAL_FLAGS_NONE,
809                                        impanel_next_page_callback,
810                                        user_data,
811                                        nullptr);
812     g_dbus_connection_signal_subscribe(connection,
813                                        "org.kde.impanel",
814                                        "org.kde.impanel",
815                                        "PanelCreated",
816                                        "/org/kde/impanel",
817                                        nullptr,
818                                        G_DBUS_SIGNAL_FLAGS_NONE,
819                                        impanel_panel_created_callback,
820                                        user_data,
821                                        nullptr);
822     g_dbus_connection_signal_subscribe(connection,
823                                        "org.kde.impanel",
824                                        "org.kde.impanel",
825                                        "Exit",
826                                        "/org/kde/impanel",
827                                        nullptr,
828                                        G_DBUS_SIGNAL_FLAGS_NONE,
829                                        impanel_exit_callback,
830                                        user_data,
831                                        nullptr);
832     g_dbus_connection_signal_subscribe(connection,
833                                        "org.kde.impanel",
834                                        "org.kde.impanel",
835                                        "Configure",
836                                        "/org/kde/impanel",
837                                        nullptr,
838                                        G_DBUS_SIGNAL_FLAGS_NONE,
839                                        impanel_configure_callback,
840                                        user_data,
841                                        nullptr);
842 
843     GVariant *var_engines = g_settings_get_value(impanel->settings_general, "preload-engines");
844     impanel_update_engines(impanel, var_engines);
845     if (var_engines) {
846         g_variant_unref(var_engines);
847     }
848 
849     var_engines = g_settings_get_value(impanel->settings_general, "engines-order");
850     if (var_engines) {
851         impanel_update_engines_order(impanel, var_engines);
852         g_variant_unref(var_engines);
853     }
854 
855     GVariant *var_triggers = g_settings_get_value(impanel->settings_hotkey, "triggers");
856     impanel_update_triggers(impanel, var_triggers);
857     if (var_triggers) {
858         g_variant_unref(var_triggers);
859     }
860 
861     GVariant *var_layouts = g_settings_get_value(impanel->settings_general, "xkb-latin-layouts");
862     if (var_layouts) {
863         impanel_update_latin_layouts(impanel, var_layouts);
864         g_variant_unref(var_layouts);
865     }
866 
867     GVariant *var = g_settings_get_value(impanel->settings_general, "use-system-keyboard-layout");
868     if (var) {
869         impanel_update_use_system_keyboard_layout(impanel, var);
870         g_variant_unref(var);
871     }
872 
873     var = g_settings_get_value(impanel->settings_general, "use-global-engine");
874     if (var) {
875         impanel_update_use_global_engine(impanel, var);
876         g_variant_unref(var);
877     }
878 
879     ibus_panel_impanel_real_register_properties(impanel);
880 }
881 
on_name_acquired(GDBusConnection * connection,const gchar * name,gpointer user_data)882 static void on_name_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data)
883 {
884     Q_UNUSED(connection);
885     Q_UNUSED(name);
886     Q_UNUSED(user_data);
887 }
888 
on_name_lost(GDBusConnection * connection,const gchar * name,gpointer user_data)889 static void on_name_lost(GDBusConnection *connection, const gchar *name, gpointer user_data)
890 {
891     Q_UNUSED(connection);
892     Q_UNUSED(name);
893     Q_UNUSED(user_data);
894     exit(1);
895 }
896 
G_DEFINE_TYPE(IBusPanelImpanel,ibus_panel_impanel,IBUS_TYPE_PANEL_SERVICE)897 G_DEFINE_TYPE(IBusPanelImpanel, ibus_panel_impanel, IBUS_TYPE_PANEL_SERVICE)
898 
899 static void ibus_panel_impanel_class_init(IBusPanelImpanelClass *klass)
900 {
901     GObjectClass *object_class = G_OBJECT_CLASS(klass);
902 
903     // clang-format off
904     IBUS_OBJECT_CLASS (object_class)->destroy = (IBusObjectDestroyFunc) ibus_panel_impanel_destroy;
905     IBUS_PANEL_SERVICE_CLASS (object_class)->focus_in                   = ibus_panel_impanel_focus_in;
906     IBUS_PANEL_SERVICE_CLASS (object_class)->focus_out                  = ibus_panel_impanel_focus_out;
907     IBUS_PANEL_SERVICE_CLASS (object_class)->register_properties        = ibus_panel_impanel_register_properties;
908     IBUS_PANEL_SERVICE_CLASS (object_class)->set_cursor_location        = ibus_panel_impanel_set_cursor_location;
909     IBUS_PANEL_SERVICE_CLASS (object_class)->update_auxiliary_text      = ibus_panel_impanel_update_auxiliary_text;
910     IBUS_PANEL_SERVICE_CLASS (object_class)->update_lookup_table        = ibus_panel_impanel_update_lookup_table;
911     IBUS_PANEL_SERVICE_CLASS (object_class)->update_preedit_text        = ibus_panel_impanel_update_preedit_text;
912     IBUS_PANEL_SERVICE_CLASS (object_class)->update_property            = ibus_panel_impanel_update_property;
913     IBUS_PANEL_SERVICE_CLASS (object_class)->cursor_down_lookup_table   = ibus_panel_impanel_cursor_down_lookup_table;
914     IBUS_PANEL_SERVICE_CLASS (object_class)->cursor_up_lookup_table     = ibus_panel_impanel_cursor_up_lookup_table;
915     IBUS_PANEL_SERVICE_CLASS (object_class)->hide_auxiliary_text        = ibus_panel_impanel_hide_auxiliary_text;
916     IBUS_PANEL_SERVICE_CLASS (object_class)->hide_language_bar          = ibus_panel_impanel_hide_language_bar;
917     IBUS_PANEL_SERVICE_CLASS (object_class)->hide_lookup_table          = ibus_panel_impanel_hide_lookup_table;
918     IBUS_PANEL_SERVICE_CLASS (object_class)->hide_preedit_text          = ibus_panel_impanel_hide_preedit_text;
919     IBUS_PANEL_SERVICE_CLASS (object_class)->page_down_lookup_table     = ibus_panel_impanel_page_down_lookup_table;
920     IBUS_PANEL_SERVICE_CLASS (object_class)->page_up_lookup_table       = ibus_panel_impanel_page_up_lookup_table;
921     IBUS_PANEL_SERVICE_CLASS (object_class)->reset                      = ibus_panel_impanel_reset;
922     IBUS_PANEL_SERVICE_CLASS (object_class)->show_auxiliary_text        = ibus_panel_impanel_show_auxiliary_text;
923     IBUS_PANEL_SERVICE_CLASS (object_class)->show_language_bar          = ibus_panel_impanel_show_language_bar;
924     IBUS_PANEL_SERVICE_CLASS (object_class)->show_lookup_table          = ibus_panel_impanel_show_lookup_table;
925     IBUS_PANEL_SERVICE_CLASS (object_class)->show_preedit_text          = ibus_panel_impanel_show_preedit_text;
926     IBUS_PANEL_SERVICE_CLASS (object_class)->start_setup                = ibus_panel_impanel_start_setup;
927     IBUS_PANEL_SERVICE_CLASS (object_class)->state_changed              = ibus_panel_impanel_state_changed;
928     // clang-format on
929 }
930 
ibus_panel_impanel_init(IBusPanelImpanel * impanel)931 static void ibus_panel_impanel_init(IBusPanelImpanel *impanel)
932 {
933     impanel->bus = nullptr;
934     impanel->app = nullptr;
935     impanel->useSystemKeyboardLayout = false;
936     impanel->selected = -1;
937 
938     introspection_data = g_dbus_node_info_new_for_xml(introspection_xml, nullptr);
939     owner_id = g_bus_own_name(G_BUS_TYPE_SESSION,
940                               "org.kde.kimpanel.inputmethod",
941                               G_BUS_NAME_OWNER_FLAGS_REPLACE,
942                               on_bus_acquired,
943                               on_name_acquired,
944                               on_name_lost,
945                               impanel,
946                               nullptr);
947 
948     impanel->propManager = new PropertyManager;
949     impanel->engineManager = new EngineManager;
950     impanel->xkbLayoutManager = new XkbLayoutManager;
951     impanel->settings_general = g_settings_new(IBUS_SCHEMA_GENERAL);
952     impanel->settings_hotkey = g_settings_new(IBUS_SCHEMA_HOTKEY);
953     g_signal_connect(impanel->settings_general, "changed", G_CALLBACK(impanel_settings_changed_callback), impanel);
954     g_signal_connect(impanel->settings_hotkey, "changed", G_CALLBACK(impanel_settings_changed_callback), impanel);
955 }
956 
ibus_panel_impanel_destroy(IBusPanelImpanel * impanel)957 static void ibus_panel_impanel_destroy(IBusPanelImpanel *impanel)
958 {
959     delete impanel->propManager;
960     impanel->propManager = nullptr;
961     delete impanel->engineManager;
962     impanel->engineManager = nullptr;
963     delete impanel->xkbLayoutManager;
964     impanel->xkbLayoutManager = nullptr;
965 
966     g_signal_handlers_disconnect_by_func(impanel->settings_general, (gpointer)impanel_settings_changed_callback, impanel);
967     g_signal_handlers_disconnect_by_func(impanel->settings_hotkey, (gpointer)impanel_settings_changed_callback, impanel);
968     g_clear_object(&impanel->settings_general);
969     g_clear_object(&impanel->settings_hotkey);
970 
971     g_bus_unown_name(owner_id);
972     g_dbus_node_info_unref(introspection_data);
973 
974     IBUS_OBJECT_CLASS(ibus_panel_impanel_parent_class)->destroy((IBusObject *)impanel);
975 }
976 
ibus_panel_impanel_focus_in(IBusPanelService * panel,const gchar * input_context_path)977 static void ibus_panel_impanel_focus_in(IBusPanelService *panel, const gchar *input_context_path)
978 {
979     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(panel);
980     if (impanel->app->keyboardGrabbed()) {
981         return;
982     }
983 
984     auto engine_desc = ibus_bus_get_global_engine(impanel->bus);
985     if (engine_desc) {
986         impanel_update_logo_by_engine(impanel, engine_desc);
987         g_object_unref(engine_desc);
988     }
989 
990     impanel->engineManager->setCurrentContext(input_context_path);
991     if (!impanel->engineManager->useGlobalEngine()) {
992         impanel_set_engine(impanel, impanel->engineManager->currentEngine().toUtf8().constData());
993     }
994 }
995 
ibus_panel_impanel_focus_out(IBusPanelService * panel,const gchar * input_context_path)996 static void ibus_panel_impanel_focus_out(IBusPanelService *panel, const gchar *input_context_path)
997 {
998     Q_UNUSED(panel);
999     Q_UNUSED(input_context_path);
1000     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(panel);
1001 
1002     if (impanel->app->keyboardGrabbed()) {
1003         return;
1004     }
1005 
1006     if (impanel->engineManager->useGlobalEngine()) {
1007         return;
1008     }
1009     impanel->engineManager->setCurrentContext("");
1010 }
1011 
ibus_panel_impanel_register_properties(IBusPanelService * panel,IBusPropList * prop_list)1012 static void ibus_panel_impanel_register_properties(IBusPanelService *panel, IBusPropList *prop_list)
1013 {
1014     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(panel);
1015     impanel->propManager->setProperties(prop_list);
1016     ibus_panel_impanel_real_register_properties(impanel);
1017 }
1018 
ibus_panel_impanel_real_register_properties(IBusPanelImpanel * impanel)1019 static void ibus_panel_impanel_real_register_properties(IBusPanelImpanel *impanel)
1020 {
1021     if (!impanel->conn)
1022         return;
1023 
1024     IBusProperty *property = nullptr;
1025     guint i = 0;
1026 
1027     GVariantBuilder builder;
1028     g_variant_builder_init(&builder, G_VARIANT_TYPE("as"));
1029 
1030     if (impanel->selected >= 0 && static_cast<size_t>(impanel->selected) < impanel->engineManager->length()) {
1031         auto engine_desc = impanel->engineManager->engines()[impanel->selected];
1032         QByteArray propstr = ibus_engine_desc_to_logo_propstr(engine_desc);
1033         g_variant_builder_add(&builder, "s", propstr.constData());
1034     } else {
1035         QByteArray propstr;
1036         auto engine_desc = ibus_bus_get_global_engine(impanel->bus);
1037         if (engine_desc) {
1038             propstr = ibus_engine_desc_to_logo_propstr(engine_desc);
1039             g_variant_builder_add(&builder, "s", propstr.constData());
1040             g_object_unref(engine_desc);
1041         }
1042 
1043         IBusPropList *prop_list = impanel->propManager->properties();
1044         if (prop_list) {
1045             while ((property = ibus_prop_list_get(prop_list, i)) != nullptr) {
1046                 propstr = ibus_property_to_propstr(property, TRUE);
1047                 g_variant_builder_add(&builder, "s", propstr.constData());
1048                 ++i;
1049             }
1050         }
1051     }
1052 
1053     g_dbus_connection_emit_signal(impanel->conn,
1054                                   nullptr,
1055                                   "/kimpanel",
1056                                   "org.kde.kimpanel.inputmethod",
1057                                   "RegisterProperties",
1058                                   (g_variant_new("(as)", &builder)),
1059                                   nullptr);
1060 }
1061 
ibus_panel_impanel_set_cursor_location(IBusPanelService * panel,gint x,gint y,gint w,gint h)1062 static void ibus_panel_impanel_set_cursor_location(IBusPanelService *panel, gint x, gint y, gint w, gint h)
1063 {
1064     g_dbus_connection_call(IBUS_PANEL_IMPANEL(panel)->conn,
1065                            "org.kde.impanel",
1066                            "/org/kde/impanel",
1067                            "org.kde.impanel2",
1068                            "SetSpotRect",
1069                            (g_variant_new("(iiii)", x, y, w, h)),
1070                            nullptr,
1071                            G_DBUS_CALL_FLAGS_NONE,
1072                            -1, /* timeout */
1073                            nullptr,
1074                            nullptr,
1075                            nullptr);
1076 }
1077 
ibus_panel_impanel_update_auxiliary_text(IBusPanelService * panel,IBusText * text,gboolean visible)1078 static void ibus_panel_impanel_update_auxiliary_text(IBusPanelService *panel, IBusText *text, gboolean visible)
1079 {
1080     const gchar *t = ibus_text_get_text(text);
1081     const gchar *attr = "";
1082     IBusPanelImpanel *impanel = (IBusPanelImpanel *)panel;
1083 
1084     if (!impanel->conn)
1085         return;
1086 
1087     g_dbus_connection_emit_signal(impanel->conn, nullptr, "/kimpanel", "org.kde.kimpanel.inputmethod", "UpdateAux", (g_variant_new("(ss)", t, attr)), nullptr);
1088 
1089     if (visible == 0)
1090         ibus_panel_impanel_hide_auxiliary_text(panel);
1091     else
1092         ibus_panel_impanel_show_auxiliary_text(panel);
1093 }
1094 
ibus_panel_impanel_update_lookup_table(IBusPanelService * panel,IBusLookupTable * lookup_table,gboolean visible)1095 static void ibus_panel_impanel_update_lookup_table(IBusPanelService *panel, IBusLookupTable *lookup_table, gboolean visible)
1096 {
1097     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(panel);
1098     if (!impanel->conn)
1099         return;
1100 
1101     guint page_size = ibus_lookup_table_get_page_size(lookup_table);
1102     guint cursor_pos = ibus_lookup_table_get_cursor_pos(lookup_table);
1103     guint page = cursor_pos / page_size;
1104     guint start = page * page_size;
1105     guint end = start + page_size;
1106     guint num = ibus_lookup_table_get_number_of_candidates(lookup_table);
1107     if (end > num) {
1108         end = num;
1109     }
1110 
1111     //     fprintf(stderr, "%d ~ %d pgsize %d num %d\n", start, end, page_size, num);
1112 
1113     guint i;
1114 
1115     gchar label[16][4]; // WARNING large enough I think --- nihui
1116     const gchar *candidate;
1117 
1118     GVariantBuilder builder_labels;
1119     GVariantBuilder builder_candidates;
1120     GVariantBuilder builder_attrs;
1121     g_variant_builder_init(&builder_labels, G_VARIANT_TYPE("as"));
1122     g_variant_builder_init(&builder_candidates, G_VARIANT_TYPE("as"));
1123     g_variant_builder_init(&builder_attrs, G_VARIANT_TYPE("as"));
1124 
1125     const gchar *attr = "";
1126     for (i = start; i < end; i++) {
1127         g_snprintf(label[i - start], 4, "%d", (i - start + 1) % 10);
1128         // NOTE ibus always return NULL for ibus_lookup_table_get_label
1129         //         label = ibus_lookup_table_get_label(lookup_table, i)->text;
1130         g_variant_builder_add(&builder_labels, "s", label[i - start]);
1131 
1132         candidate = ibus_text_get_text(ibus_lookup_table_get_candidate(lookup_table, i));
1133         g_variant_builder_add(&builder_candidates, "s", candidate);
1134 
1135         g_variant_builder_add(&builder_attrs, "s", attr);
1136     }
1137 
1138     gboolean has_prev = 1;
1139     gboolean has_next = 1;
1140 
1141     guint cursor_pos_in_page;
1142     if (ibus_lookup_table_is_cursor_visible(lookup_table))
1143         cursor_pos_in_page = cursor_pos % page_size;
1144     else
1145         cursor_pos_in_page = -1;
1146 
1147     gint orientation = ibus_lookup_table_get_orientation(lookup_table);
1148     if (orientation == IBUS_ORIENTATION_HORIZONTAL) {
1149         orientation = 2;
1150     } else if (orientation == IBUS_ORIENTATION_VERTICAL) {
1151         orientation = 1;
1152     } else {
1153         orientation = 0;
1154     }
1155 
1156     g_dbus_connection_call(
1157         impanel->conn,
1158         "org.kde.impanel",
1159         "/org/kde/impanel",
1160         "org.kde.impanel2",
1161         "SetLookupTable",
1162         (g_variant_new("(asasasbbii)", &builder_labels, &builder_candidates, &builder_attrs, has_prev, has_next, cursor_pos_in_page, orientation)),
1163         nullptr,
1164         G_DBUS_CALL_FLAGS_NONE,
1165         -1,
1166         nullptr,
1167         nullptr,
1168         nullptr);
1169 
1170     if (visible == 0)
1171         ibus_panel_impanel_hide_lookup_table(panel);
1172     else
1173         ibus_panel_impanel_show_lookup_table(panel);
1174 }
1175 
ibus_panel_impanel_update_preedit_text(IBusPanelService * panel,IBusText * text,guint cursor_pos,gboolean visible)1176 static void ibus_panel_impanel_update_preedit_text(IBusPanelService *panel, IBusText *text, guint cursor_pos, gboolean visible)
1177 {
1178     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(panel);
1179     if (!impanel->conn)
1180         return;
1181 
1182     const gchar *t = ibus_text_get_text(text);
1183     const gchar *attr = "";
1184 
1185     g_dbus_connection_emit_signal(impanel->conn,
1186                                   nullptr,
1187                                   "/kimpanel",
1188                                   "org.kde.kimpanel.inputmethod",
1189                                   "UpdatePreeditText",
1190                                   (g_variant_new("(ss)", t, attr)),
1191                                   nullptr);
1192 
1193     g_dbus_connection_emit_signal(impanel->conn,
1194                                   nullptr,
1195                                   "/kimpanel",
1196                                   "org.kde.kimpanel.inputmethod",
1197                                   "UpdatePreeditCaret",
1198                                   (g_variant_new("(i)", cursor_pos)),
1199                                   nullptr);
1200 
1201     if (visible == 0)
1202         ibus_panel_impanel_hide_preedit_text(panel);
1203     else
1204         ibus_panel_impanel_show_preedit_text(panel);
1205 }
1206 
ibus_panel_impanel_update_property(IBusPanelService * panel,IBusProperty * prop)1207 static void ibus_panel_impanel_update_property(IBusPanelService *panel, IBusProperty *prop)
1208 {
1209     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(panel);
1210     if (!impanel->conn)
1211         return;
1212 
1213     impanel->propManager->updateProperty(prop);
1214 
1215     QByteArray propstr = ibus_property_to_propstr(prop, TRUE);
1216 
1217     g_dbus_connection_emit_signal(impanel->conn,
1218                                   nullptr,
1219                                   "/kimpanel",
1220                                   "org.kde.kimpanel.inputmethod",
1221                                   "UpdateProperty",
1222                                   (g_variant_new("(s)", propstr.constData())),
1223                                   nullptr);
1224 }
1225 
ibus_panel_impanel_cursor_down_lookup_table(IBusPanelService * panel)1226 static void ibus_panel_impanel_cursor_down_lookup_table(IBusPanelService *panel)
1227 {
1228     Q_UNUSED(panel);
1229 }
1230 
ibus_panel_impanel_cursor_up_lookup_table(IBusPanelService * panel)1231 static void ibus_panel_impanel_cursor_up_lookup_table(IBusPanelService *panel)
1232 {
1233     Q_UNUSED(panel);
1234 }
1235 
ibus_panel_impanel_hide_auxiliary_text(IBusPanelService * panel)1236 static void ibus_panel_impanel_hide_auxiliary_text(IBusPanelService *panel)
1237 {
1238     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(panel);
1239     if (!impanel->conn)
1240         return;
1241     gboolean toShow = 0;
1242 
1243     g_dbus_connection_emit_signal(impanel->conn, nullptr, "/kimpanel", "org.kde.kimpanel.inputmethod", "ShowAux", (g_variant_new("(b)", toShow)), nullptr);
1244 }
1245 
ibus_panel_impanel_hide_language_bar(IBusPanelService * panel)1246 static void ibus_panel_impanel_hide_language_bar(IBusPanelService *panel)
1247 {
1248     Q_UNUSED(panel);
1249 }
1250 
ibus_panel_impanel_hide_lookup_table(IBusPanelService * panel)1251 static void ibus_panel_impanel_hide_lookup_table(IBusPanelService *panel)
1252 {
1253     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(panel);
1254     if (!impanel->conn)
1255         return;
1256     gboolean toShow = 0;
1257 
1258     g_dbus_connection_emit_signal(impanel->conn,
1259                                   nullptr,
1260                                   "/kimpanel",
1261                                   "org.kde.kimpanel.inputmethod",
1262                                   "ShowLookupTable",
1263                                   (g_variant_new("(b)", toShow)),
1264                                   nullptr);
1265 }
1266 
ibus_panel_impanel_hide_preedit_text(IBusPanelService * panel)1267 static void ibus_panel_impanel_hide_preedit_text(IBusPanelService *panel)
1268 {
1269     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(panel);
1270     if (!impanel->conn)
1271         return;
1272     gboolean toShow = 0;
1273 
1274     g_dbus_connection_emit_signal(impanel->conn, nullptr, "/kimpanel", "org.kde.kimpanel.inputmethod", "ShowPreedit", (g_variant_new("(b)", toShow)), nullptr);
1275 }
1276 
ibus_panel_impanel_page_down_lookup_table(IBusPanelService * panel)1277 static void ibus_panel_impanel_page_down_lookup_table(IBusPanelService *panel)
1278 {
1279     Q_UNUSED(panel);
1280 }
1281 
ibus_panel_impanel_page_up_lookup_table(IBusPanelService * panel)1282 static void ibus_panel_impanel_page_up_lookup_table(IBusPanelService *panel)
1283 {
1284     Q_UNUSED(panel);
1285 }
1286 
ibus_panel_impanel_reset(IBusPanelService * panel)1287 static void ibus_panel_impanel_reset(IBusPanelService *panel)
1288 {
1289     Q_UNUSED(panel);
1290 }
1291 
ibus_panel_impanel_show_auxiliary_text(IBusPanelService * panel)1292 static void ibus_panel_impanel_show_auxiliary_text(IBusPanelService *panel)
1293 {
1294     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(panel);
1295     if (!impanel->conn)
1296         return;
1297     gboolean toShow = 1;
1298 
1299     g_dbus_connection_emit_signal(impanel->conn, nullptr, "/kimpanel", "org.kde.kimpanel.inputmethod", "ShowAux", (g_variant_new("(b)", toShow)), nullptr);
1300 }
1301 
ibus_panel_impanel_show_language_bar(IBusPanelService * panel)1302 static void ibus_panel_impanel_show_language_bar(IBusPanelService *panel)
1303 {
1304     Q_UNUSED(panel);
1305 }
1306 
ibus_panel_impanel_show_lookup_table(IBusPanelService * panel)1307 static void ibus_panel_impanel_show_lookup_table(IBusPanelService *panel)
1308 {
1309     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(panel);
1310     if (!impanel->conn)
1311         return;
1312     gboolean toShow = 1;
1313 
1314     g_dbus_connection_emit_signal(impanel->conn,
1315                                   nullptr,
1316                                   "/kimpanel",
1317                                   "org.kde.kimpanel.inputmethod",
1318                                   "ShowLookupTable",
1319                                   (g_variant_new("(b)", toShow)),
1320                                   nullptr);
1321 }
1322 
ibus_panel_impanel_show_preedit_text(IBusPanelService * panel)1323 static void ibus_panel_impanel_show_preedit_text(IBusPanelService *panel)
1324 {
1325     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(panel);
1326     if (!impanel->conn)
1327         return;
1328     gboolean toShow = 1;
1329 
1330     g_dbus_connection_emit_signal(impanel->conn, nullptr, "/kimpanel", "org.kde.kimpanel.inputmethod", "ShowPreedit", (g_variant_new("(b)", toShow)), nullptr);
1331 }
1332 
ibus_panel_impanel_start_setup(IBusPanelService * panel)1333 static void ibus_panel_impanel_start_setup(IBusPanelService *panel)
1334 {
1335     Q_UNUSED(panel);
1336 }
1337 
ibus_panel_impanel_state_changed(IBusPanelService * panel)1338 static void ibus_panel_impanel_state_changed(IBusPanelService *panel)
1339 {
1340     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(panel);
1341     if (!impanel->conn)
1342         return;
1343 
1344     if (impanel->app->keyboardGrabbed()) {
1345         return;
1346     }
1347 
1348     IBusEngineDesc *engine_desc = ibus_bus_get_global_engine(impanel->bus);
1349     if (!engine_desc) {
1350         return;
1351     }
1352 
1353     impanel_update_logo_by_engine(impanel, engine_desc);
1354 
1355     g_dbus_connection_emit_signal(impanel->conn, nullptr, "/kimpanel", "org.kde.kimpanel.inputmethod", "Enable", (g_variant_new("(b)", TRUE)), nullptr);
1356 
1357     impanel->engineManager->moveToFirst(engine_desc);
1358     QStringList engineList = impanel->engineManager->engineOrder();
1359 
1360     gchar **engine_names = g_new0(gchar *, engineList.size() + 1);
1361     size_t i = 0;
1362     Q_FOREACH (const QString &name, engineList) {
1363         engine_names[i] = g_strdup(name.toUtf8().constData());
1364         i++;
1365     }
1366 
1367     GVariant *var = g_variant_new_strv(engine_names, engineList.size());
1368     g_settings_set_value(impanel->settings_general, "engines-order", var);
1369     g_strfreev(engine_names);
1370     g_object_unref(engine_desc);
1371 }
1372 
ibus_panel_impanel_exec_menu(IBusPanelImpanel * impanel,IBusPropList * prop_list)1373 static void ibus_panel_impanel_exec_menu(IBusPanelImpanel *impanel, IBusPropList *prop_list)
1374 {
1375     if (!impanel->conn)
1376         return;
1377 
1378     GVariantBuilder builder;
1379     g_variant_builder_init(&builder, G_VARIANT_TYPE("as"));
1380 
1381     int i = 0;
1382     while (true) {
1383         IBusProperty *prop = ibus_prop_list_get(prop_list, i);
1384         if (!prop)
1385             break;
1386         QByteArray propstr = ibus_property_to_propstr(prop);
1387         g_variant_builder_add(&builder, "s", propstr.constData());
1388         i++;
1389     }
1390 
1391     g_dbus_connection_emit_signal(impanel->conn, nullptr, "/kimpanel", "org.kde.kimpanel.inputmethod", "ExecMenu", (g_variant_new("(as)", &builder)), nullptr);
1392 }
1393 
ibus_panel_impanel_exec_im_menu(IBusPanelImpanel * impanel)1394 static void ibus_panel_impanel_exec_im_menu(IBusPanelImpanel *impanel)
1395 {
1396     if (!impanel->conn)
1397         return;
1398 
1399     GVariantBuilder builder;
1400     g_variant_builder_init(&builder, G_VARIANT_TYPE("as"));
1401 
1402     IBusEngineDesc **engines = impanel->engineManager->engines();
1403     if (engines) {
1404         int i = 0;
1405         while (engines[i]) {
1406             QByteArray propstr = ibus_engine_desc_to_propstr(engines[i]);
1407             g_variant_builder_add(&builder, "s", propstr.constData());
1408             i++;
1409         }
1410     }
1411 
1412     g_dbus_connection_emit_signal(impanel->conn, nullptr, "/kimpanel", "org.kde.kimpanel.inputmethod", "ExecMenu", (g_variant_new("(as)", &builder)), nullptr);
1413 }
1414 
ibus_panel_impanel_new(GDBusConnection * connection)1415 IBusPanelImpanel *ibus_panel_impanel_new(GDBusConnection *connection)
1416 {
1417     IBusPanelImpanel *panel;
1418     panel = (IBusPanelImpanel *)g_object_new(IBUS_TYPE_PANEL_IMPANEL, "object-path", IBUS_PATH_PANEL, "connection", connection, NULL);
1419     return panel;
1420 }
1421