1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 #include "unix/ibus/gtk_candidate_window_handler.h"
31
32 #include <gio/gio.h>
33 #include <unistd.h>
34
35 #include "base/logging.h"
36 #include "protocol/renderer_command.pb.h"
37 #include "renderer/renderer_interface.h"
38 #include "renderer/unix/const.h"
39
40 namespace mozc {
41 namespace ibus {
42
43 namespace {
44
45 const char kDefaultFont[] = "SansSerif 11";
46 const gchar kIBusPanelSchema[] = "org.freedesktop.ibus.panel";
47 const gchar kIBusPanelUseCustomFont[] = "use-custom-font";
48 const gchar kIBusPanelCustomFont[] = "custom-font";
49
GetString(GVariant * value,string * out_string)50 bool GetString(GVariant *value, string *out_string) {
51 if (g_variant_classify(value) != G_VARIANT_CLASS_STRING) {
52 return false;
53 }
54 *out_string = static_cast<const char *>(g_variant_get_string(value, NULL));
55 return true;
56 }
57
GetBoolean(GVariant * value,bool * out_boolean)58 bool GetBoolean(GVariant *value, bool *out_boolean) {
59 if (g_variant_classify(value) != G_VARIANT_CLASS_BOOLEAN) {
60 return false;
61 }
62 *out_boolean = (g_variant_get_boolean(value) != FALSE);
63 return true;
64 }
65
HasScheme(const char * schema_name)66 bool HasScheme(const char *schema_name) {
67 GSettingsSchemaSource *schema_source = g_settings_schema_source_get_default();
68 if (schema_source == nullptr) {
69 return false;
70 }
71 GSettingsSchema *schema = g_settings_schema_source_lookup(
72 schema_source, schema_name, TRUE);
73 if (schema == nullptr) {
74 return false;
75 }
76 g_settings_schema_unref(schema);
77 return true;
78 }
79
OpenIBusPanelSettings()80 GSettings *OpenIBusPanelSettings() {
81 if (!HasScheme(kIBusPanelSchema)) {
82 return nullptr;
83 }
84 return g_settings_new(kIBusPanelSchema);
85 }
86
87 // The callback function to the "changed" signal to GSettings object.
GSettingsChangedCallback(GSettings * settings,const gchar * key,gpointer user_data)88 void GSettingsChangedCallback(GSettings *settings,
89 const gchar *key,
90 gpointer user_data) {
91 GtkCandidateWindowHandler *handler =
92 reinterpret_cast<GtkCandidateWindowHandler *>(user_data);
93 if (g_strcmp0(key, kIBusPanelUseCustomFont) == 0) {
94 GVariant *use_custom_font_value =
95 g_settings_get_value(settings, kIBusPanelUseCustomFont);
96 bool use_custom_font = false;
97 if (GetBoolean(use_custom_font_value, &use_custom_font)) {
98 handler->OnIBusUseCustomFontDescriptionChanged(use_custom_font);
99 } else {
100 LOG(ERROR) << "Cannot get panel:use_custom_font configuration.";
101 }
102 } else if (g_strcmp0(key, kIBusPanelCustomFont) == 0) {
103 GVariant *custom_font_value = g_settings_get_value(settings,
104 kIBusPanelCustomFont);
105 string font_description;
106 if (GetString(custom_font_value, &font_description)) {
107 handler->OnIBusCustomFontDescriptionChanged(font_description);
108 } else {
109 LOG(ERROR) << "Cannot get panel:custom_font configuration.";
110 }
111 }
112 }
113
114 } // namespace
115
116 class GSettingsObserver {
117 public:
GSettingsObserver(GtkCandidateWindowHandler * handler)118 explicit GSettingsObserver(GtkCandidateWindowHandler *handler)
119 : settings_(OpenIBusPanelSettings()),
120 settings_observer_id_(0) {
121 if (settings_ != nullptr) {
122 gpointer ptr = reinterpret_cast<gpointer>(handler);
123 settings_observer_id_ = g_signal_connect(
124 settings_,
125 "changed",
126 G_CALLBACK(GSettingsChangedCallback),
127 ptr);
128 // Emulate state changes to set the initial values to the renderer.
129 GSettingsChangedCallback(settings_, kIBusPanelUseCustomFont, ptr);
130 GSettingsChangedCallback(settings_, kIBusPanelCustomFont, ptr);
131 }
132 }
133
~GSettingsObserver()134 ~GSettingsObserver() {
135 if (settings_ != nullptr) {
136 if (settings_observer_id_ != 0) {
137 g_signal_handler_disconnect(settings_, settings_observer_id_);
138 }
139 g_object_unref(settings_);
140 }
141 }
142
143 private:
144 GSettings *settings_;
145 gulong settings_observer_id_;
146 };
147
GtkCandidateWindowHandler(renderer::RendererInterface * renderer)148 GtkCandidateWindowHandler::GtkCandidateWindowHandler(
149 renderer::RendererInterface *renderer)
150 : renderer_(renderer),
151 last_update_output_(new commands::Output()),
152 use_custom_font_description_(false) {
153 }
154
~GtkCandidateWindowHandler()155 GtkCandidateWindowHandler::~GtkCandidateWindowHandler() {
156 }
157
SendUpdateCommand(IBusEngine * engine,const commands::Output & output,bool visibility) const158 bool GtkCandidateWindowHandler::SendUpdateCommand(
159 IBusEngine *engine,
160 const commands::Output &output,
161 bool visibility) const {
162 using commands::RendererCommand;
163 RendererCommand command;
164
165 *command.mutable_output() = output;
166 command.set_type(RendererCommand::UPDATE);
167 command.set_visible(visibility);
168 RendererCommand::ApplicationInfo *appinfo
169 = command.mutable_application_info();
170
171 auto *preedit_rectangle = command.mutable_preedit_rectangle();
172 const auto &cursor_area = engine->cursor_area;
173 preedit_rectangle->set_left(cursor_area.x);
174 preedit_rectangle->set_top(cursor_area.y);
175 preedit_rectangle->set_right(cursor_area.x + cursor_area.width);
176 preedit_rectangle->set_bottom(cursor_area.y + cursor_area.height);
177
178 // Set pid
179 static_assert(sizeof(::getpid()) <= sizeof(appinfo->process_id()),
180 "|appinfo->process_id()| must have sufficient room.");
181 appinfo->set_process_id(::getpid());
182
183 // Do not set thread_id returned from ::pthread_self() because:
184 // 1. the returned value is valid only in the caller process.
185 // 2. the returned value may exceed uint32.
186 // TODO(team): Consider to use ::gettid()
187
188 // Set InputFramework type
189 appinfo->set_input_framework(RendererCommand::ApplicationInfo::IBus);
190 appinfo->set_pango_font_description(GetFontDescription());
191
192 return renderer_->ExecCommand(command);
193 }
194
Update(IBusEngine * engine,const commands::Output & output)195 void GtkCandidateWindowHandler::Update(IBusEngine *engine,
196 const commands::Output &output) {
197 *last_update_output_ = output;
198
199 UpdateCursorRect(engine);
200 }
201
UpdateCursorRect(IBusEngine * engine)202 void GtkCandidateWindowHandler::UpdateCursorRect(IBusEngine *engine) {
203 const bool has_candidates =
204 last_update_output_->has_candidates() &&
205 last_update_output_->candidates().candidate_size() > 0;
206 SendUpdateCommand(engine, *last_update_output_, has_candidates);
207 }
208
Hide(IBusEngine * engine)209 void GtkCandidateWindowHandler::Hide(IBusEngine *engine) {
210 SendUpdateCommand(engine, *last_update_output_, false);
211 }
212
Show(IBusEngine * engine)213 void GtkCandidateWindowHandler::Show(IBusEngine *engine) {
214 SendUpdateCommand(engine, *last_update_output_, true);
215 }
216
OnIBusCustomFontDescriptionChanged(const string & custom_font_description)217 void GtkCandidateWindowHandler::OnIBusCustomFontDescriptionChanged(
218 const string &custom_font_description) {
219 custom_font_description_.assign(custom_font_description);
220 }
221
OnIBusUseCustomFontDescriptionChanged(bool use_custom_font_description)222 void GtkCandidateWindowHandler::OnIBusUseCustomFontDescriptionChanged(
223 bool use_custom_font_description) {
224 use_custom_font_description_ = use_custom_font_description;
225 }
226
RegisterGSettingsObserver()227 void GtkCandidateWindowHandler::RegisterGSettingsObserver() {
228 settings_observer_.reset(new GSettingsObserver(this));
229 }
230
GetFontDescription() const231 string GtkCandidateWindowHandler::GetFontDescription() const {
232 if (!use_custom_font_description_) {
233 // TODO(nona): Load application default font settings.
234 return kDefaultFont;
235 }
236 DCHECK(!custom_font_description_.empty());
237 return custom_font_description_;
238 }
239
240 } // namespace ibus
241 } // namespace mozc
242