1 | /* GTK - The GIMP Toolkit |
2 | * Copyright (C) 2017, Red Hat, Inc. |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Lesser General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Lesser General Public |
15 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
16 | * |
17 | * Author(s): Carlos Garnacho <carlosg@gnome.org> |
18 | */ |
19 | |
20 | /** |
21 | * GtkEventControllerKey: |
22 | * |
23 | * `GtkEventControllerKey` is an event controller that provides access |
24 | * to key events. |
25 | */ |
26 | |
27 | #include "config.h" |
28 | |
29 | #include "gtkintl.h" |
30 | #include "gtkmarshalers.h" |
31 | #include "gtkprivate.h" |
32 | #include "gtkwidgetprivate.h" |
33 | #include "gtkeventcontrollerprivate.h" |
34 | #include "gtkeventcontrollerkey.h" |
35 | #include "gtkenums.h" |
36 | #include "gtkmain.h" |
37 | #include "gtktypebuiltins.h" |
38 | |
39 | #include <gdk/gdk.h> |
40 | |
41 | struct _GtkEventControllerKey |
42 | { |
43 | GtkEventController parent_instance; |
44 | GtkIMContext *im_context; |
45 | GHashTable *pressed_keys; |
46 | |
47 | GdkModifierType state; |
48 | |
49 | gboolean is_focus; |
50 | |
51 | GdkEvent *current_event; |
52 | }; |
53 | |
54 | struct _GtkEventControllerKeyClass |
55 | { |
56 | GtkEventControllerClass parent_class; |
57 | }; |
58 | |
59 | enum { |
60 | KEY_PRESSED, |
61 | KEY_RELEASED, |
62 | MODIFIERS, |
63 | IM_UPDATE, |
64 | N_SIGNALS |
65 | }; |
66 | |
67 | static guint signals[N_SIGNALS] = { 0 }; |
68 | |
69 | G_DEFINE_TYPE (GtkEventControllerKey, gtk_event_controller_key, |
70 | GTK_TYPE_EVENT_CONTROLLER) |
71 | |
72 | static void |
73 | gtk_event_controller_key_finalize (GObject *object) |
74 | { |
75 | GtkEventControllerKey *key = GTK_EVENT_CONTROLLER_KEY (object); |
76 | |
77 | g_hash_table_destroy (hash_table: key->pressed_keys); |
78 | g_clear_object (&key->im_context); |
79 | |
80 | G_OBJECT_CLASS (gtk_event_controller_key_parent_class)->finalize (object); |
81 | } |
82 | |
83 | static gboolean |
84 | gtk_event_controller_key_handle_event (GtkEventController *controller, |
85 | GdkEvent *event, |
86 | double x, |
87 | double y) |
88 | { |
89 | GtkEventControllerKey *key = GTK_EVENT_CONTROLLER_KEY (controller); |
90 | GdkEventType event_type = gdk_event_get_event_type (event); |
91 | GdkModifierType state; |
92 | guint16 keycode; |
93 | guint keyval; |
94 | gboolean handled = FALSE; |
95 | |
96 | if (event_type != GDK_KEY_PRESS && event_type != GDK_KEY_RELEASE) |
97 | return FALSE; |
98 | |
99 | if (key->im_context && |
100 | gtk_im_context_filter_keypress (context: key->im_context, event)) |
101 | { |
102 | g_signal_emit (instance: controller, signal_id: signals[IM_UPDATE], detail: 0); |
103 | return TRUE; |
104 | } |
105 | |
106 | key->current_event = event; |
107 | |
108 | state = gdk_event_get_modifier_state (event); |
109 | if (key->state != state) |
110 | { |
111 | gboolean unused; |
112 | |
113 | key->state = state; |
114 | g_signal_emit (instance: controller, signal_id: signals[MODIFIERS], detail: 0, state, &unused); |
115 | } |
116 | |
117 | keycode = gdk_key_event_get_keycode (event); |
118 | keyval = gdk_key_event_get_keyval (event); |
119 | |
120 | if (event_type == GDK_KEY_PRESS) |
121 | { |
122 | g_signal_emit (instance: controller, signal_id: signals[KEY_PRESSED], detail: 0, |
123 | keyval, keycode, state, &handled); |
124 | if (handled) |
125 | g_hash_table_add (hash_table: key->pressed_keys, GUINT_TO_POINTER (keyval)); |
126 | } |
127 | else if (event_type == GDK_KEY_RELEASE) |
128 | { |
129 | g_signal_emit (instance: controller, signal_id: signals[KEY_RELEASED], detail: 0, |
130 | keyval, keycode, state); |
131 | |
132 | handled = g_hash_table_lookup (hash_table: key->pressed_keys, GUINT_TO_POINTER (keyval)) != NULL; |
133 | g_hash_table_remove (hash_table: key->pressed_keys, GUINT_TO_POINTER (keyval)); |
134 | } |
135 | else |
136 | handled = FALSE; |
137 | |
138 | key->current_event = NULL; |
139 | |
140 | return handled; |
141 | } |
142 | |
143 | static void |
144 | gtk_event_controller_key_handle_crossing (GtkEventController *controller, |
145 | const GtkCrossingData *crossing, |
146 | double x, |
147 | double y) |
148 | { |
149 | GtkEventControllerKey *key = GTK_EVENT_CONTROLLER_KEY (controller); |
150 | GtkWidget *widget = gtk_event_controller_get_widget (controller); |
151 | gboolean start_crossing, end_crossing; |
152 | gboolean is_focus; |
153 | |
154 | if (crossing->type != GTK_CROSSING_FOCUS && |
155 | crossing->type != GTK_CROSSING_ACTIVE) |
156 | return; |
157 | |
158 | start_crossing = crossing->direction == GTK_CROSSING_OUT && |
159 | widget == crossing->old_target; |
160 | end_crossing = crossing->direction == GTK_CROSSING_IN && |
161 | widget == crossing->new_target; |
162 | |
163 | if (!start_crossing && !end_crossing) |
164 | return; |
165 | |
166 | is_focus = end_crossing; |
167 | |
168 | if (key->is_focus != is_focus) |
169 | { |
170 | key->is_focus = is_focus; |
171 | |
172 | if (key->im_context) |
173 | { |
174 | if (is_focus) |
175 | gtk_im_context_focus_in (context: key->im_context); |
176 | else |
177 | gtk_im_context_focus_out (context: key->im_context); |
178 | } |
179 | } |
180 | } |
181 | |
182 | static void |
183 | gtk_event_controller_key_class_init (GtkEventControllerKeyClass *klass) |
184 | { |
185 | GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (klass); |
186 | GObjectClass *object_class = G_OBJECT_CLASS (klass); |
187 | |
188 | object_class->finalize = gtk_event_controller_key_finalize; |
189 | controller_class->handle_event = gtk_event_controller_key_handle_event; |
190 | controller_class->handle_crossing = gtk_event_controller_key_handle_crossing; |
191 | |
192 | /** |
193 | * GtkEventControllerKey::key-pressed: |
194 | * @controller: the object which received the signal. |
195 | * @keyval: the pressed key. |
196 | * @keycode: the raw code of the pressed key. |
197 | * @state: the bitmask, representing the state of modifier keys and pointer buttons. See `GdkModifierType`. |
198 | * |
199 | * Emitted whenever a key is pressed. |
200 | * |
201 | * Returns: %TRUE if the key press was handled, %FALSE otherwise. |
202 | */ |
203 | signals[KEY_PRESSED] = |
204 | g_signal_new (I_("key-pressed" ), |
205 | GTK_TYPE_EVENT_CONTROLLER_KEY, |
206 | signal_flags: G_SIGNAL_RUN_LAST, |
207 | class_offset: 0, accumulator: _gtk_boolean_handled_accumulator, NULL, |
208 | c_marshaller: _gtk_marshal_BOOLEAN__UINT_UINT_FLAGS, |
209 | G_TYPE_BOOLEAN, n_params: 3, G_TYPE_UINT, G_TYPE_UINT, GDK_TYPE_MODIFIER_TYPE); |
210 | g_signal_set_va_marshaller (signal_id: signals[KEY_PRESSED], |
211 | G_TYPE_FROM_CLASS (klass), |
212 | va_marshaller: _gtk_marshal_BOOLEAN__UINT_UINT_FLAGSv); |
213 | |
214 | /** |
215 | * GtkEventControllerKey::key-released: |
216 | * @controller: the object which received the signal. |
217 | * @keyval: the released key. |
218 | * @keycode: the raw code of the released key. |
219 | * @state: the bitmask, representing the state of modifier keys and pointer buttons. See `GdkModifierType`. |
220 | * |
221 | * Emitted whenever a key is released. |
222 | */ |
223 | signals[KEY_RELEASED] = |
224 | g_signal_new (I_("key-released" ), |
225 | GTK_TYPE_EVENT_CONTROLLER_KEY, |
226 | signal_flags: G_SIGNAL_RUN_LAST, |
227 | class_offset: 0, NULL, NULL, |
228 | c_marshaller: _gtk_marshal_VOID__UINT_UINT_FLAGS, |
229 | G_TYPE_NONE, n_params: 3, G_TYPE_UINT, G_TYPE_UINT, GDK_TYPE_MODIFIER_TYPE); |
230 | g_signal_set_va_marshaller (signal_id: signals[KEY_RELEASED], |
231 | G_TYPE_FROM_CLASS (klass), |
232 | va_marshaller: _gtk_marshal_VOID__UINT_UINT_FLAGSv); |
233 | |
234 | /** |
235 | * GtkEventControllerKey::modifiers: |
236 | * @controller: the object which received the signal. |
237 | * @keyval: the released key. |
238 | * @state: the bitmask, representing the new state of modifier keys and |
239 | * pointer buttons. See `GdkModifierType`. |
240 | * |
241 | * Emitted whenever the state of modifier keys and pointer buttons change. |
242 | */ |
243 | signals[MODIFIERS] = |
244 | g_signal_new (I_("modifiers" ), |
245 | GTK_TYPE_EVENT_CONTROLLER_KEY, |
246 | signal_flags: G_SIGNAL_RUN_LAST, |
247 | class_offset: 0, NULL, |
248 | NULL, |
249 | c_marshaller: _gtk_marshal_BOOLEAN__FLAGS, |
250 | G_TYPE_BOOLEAN, n_params: 1, GDK_TYPE_MODIFIER_TYPE); |
251 | g_signal_set_va_marshaller (signal_id: signals[MODIFIERS], |
252 | G_TYPE_FROM_CLASS (klass), |
253 | va_marshaller: _gtk_marshal_BOOLEAN__FLAGSv); |
254 | |
255 | /** |
256 | * GtkEventControllerKey::im-update: |
257 | * @controller: the object which received the signal |
258 | * |
259 | * Emitted whenever the input method context filters away |
260 | * a keypress and prevents the @controller receiving it. |
261 | * |
262 | * See [method@Gtk.EventControllerKey.set_im_context] and |
263 | * [method@Gtk.IMContext.filter_keypress]. |
264 | */ |
265 | signals[IM_UPDATE] = |
266 | g_signal_new (I_("im-update" ), |
267 | GTK_TYPE_EVENT_CONTROLLER_KEY, |
268 | signal_flags: G_SIGNAL_RUN_LAST, |
269 | class_offset: 0, NULL, NULL, |
270 | NULL, |
271 | G_TYPE_NONE, n_params: 0); |
272 | } |
273 | |
274 | static void |
275 | gtk_event_controller_key_init (GtkEventControllerKey *controller) |
276 | { |
277 | controller->pressed_keys = g_hash_table_new (NULL, NULL); |
278 | } |
279 | |
280 | /** |
281 | * gtk_event_controller_key_new: |
282 | * |
283 | * Creates a new event controller that will handle key events. |
284 | * |
285 | * Returns: a new `GtkEventControllerKey` |
286 | **/ |
287 | GtkEventController * |
288 | gtk_event_controller_key_new (void) |
289 | { |
290 | return g_object_new (GTK_TYPE_EVENT_CONTROLLER_KEY, NULL); |
291 | } |
292 | |
293 | /** |
294 | * gtk_event_controller_key_set_im_context: |
295 | * @controller: a `GtkEventControllerKey` |
296 | * @im_context: (nullable): a `GtkIMContext` |
297 | * |
298 | * Sets the input method context of the key @controller. |
299 | */ |
300 | void |
301 | gtk_event_controller_key_set_im_context (GtkEventControllerKey *controller, |
302 | GtkIMContext *im_context) |
303 | { |
304 | g_return_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller)); |
305 | g_return_if_fail (!im_context || GTK_IS_IM_CONTEXT (im_context)); |
306 | |
307 | if (controller->im_context) |
308 | gtk_im_context_reset (context: controller->im_context); |
309 | |
310 | g_set_object (&controller->im_context, im_context); |
311 | } |
312 | |
313 | /** |
314 | * gtk_event_controller_key_get_im_context: |
315 | * @controller: a `GtkEventControllerKey` |
316 | * |
317 | * Gets the input method context of the key @controller. |
318 | * |
319 | * Returns: (transfer none) (nullable): the `GtkIMContext` |
320 | **/ |
321 | GtkIMContext * |
322 | gtk_event_controller_key_get_im_context (GtkEventControllerKey *controller) |
323 | { |
324 | g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), NULL); |
325 | |
326 | return controller->im_context; |
327 | } |
328 | |
329 | /** |
330 | * gtk_event_controller_key_forward: |
331 | * @controller: a `GtkEventControllerKey` |
332 | * @widget: a `GtkWidget` |
333 | * |
334 | * Forwards the current event of this @controller to a @widget. |
335 | * |
336 | * This function can only be used in handlers for the |
337 | * [signal@Gtk.EventControllerKey::key-pressed], |
338 | * [signal@Gtk.EventControllerKey::key-released] |
339 | * or [signal@Gtk.EventControllerKey::modifiers] signals. |
340 | * |
341 | * Returns: whether the @widget handled the event |
342 | */ |
343 | gboolean |
344 | gtk_event_controller_key_forward (GtkEventControllerKey *controller, |
345 | GtkWidget *widget) |
346 | { |
347 | g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), FALSE); |
348 | g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); |
349 | g_return_val_if_fail (controller->current_event != NULL, FALSE); |
350 | g_return_val_if_fail (gdk_event_get_event_type (controller->current_event) == GDK_KEY_PRESS || |
351 | gdk_event_get_event_type (controller->current_event) == GDK_KEY_RELEASE, FALSE); |
352 | |
353 | if (!gtk_widget_get_realized (widget)) |
354 | gtk_widget_realize (widget); |
355 | |
356 | if (gtk_widget_run_controllers (widget, event: controller->current_event, target: widget, x: 0, y: 0, |
357 | phase: GTK_PHASE_CAPTURE)) |
358 | return TRUE; |
359 | if (gtk_widget_run_controllers (widget, event: controller->current_event, target: widget, x: 0, y: 0, |
360 | phase: GTK_PHASE_TARGET)) |
361 | return TRUE; |
362 | if (gtk_widget_run_controllers (widget, event: controller->current_event, target: widget, x: 0, y: 0, |
363 | phase: GTK_PHASE_BUBBLE)) |
364 | return TRUE; |
365 | |
366 | return FALSE; |
367 | } |
368 | |
369 | /** |
370 | * gtk_event_controller_key_get_group: |
371 | * @controller: a `GtkEventControllerKey` |
372 | * |
373 | * Gets the key group of the current event of this @controller. |
374 | * |
375 | * See [method@Gdk.KeyEvent.get_layout]. |
376 | * |
377 | * Returns: the key group |
378 | */ |
379 | guint |
380 | gtk_event_controller_key_get_group (GtkEventControllerKey *controller) |
381 | { |
382 | g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), FALSE); |
383 | g_return_val_if_fail (controller->current_event != NULL, FALSE); |
384 | |
385 | return gdk_key_event_get_layout (event: controller->current_event); |
386 | } |
387 | |