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