1 /*
2 * * Copyright (C) 2009-2011 Ali <aliov@xfce.org>
3 * * Copyright (C) 2012-2017 Simon Steinbeiß <ochosi@xfce.org>
4 * * Copyright (C) 2012-2020 Sean Davis <bluesabre@xfce.org>
5 *
6 * Licensed under the GNU General Public License Version 2
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23
24 /*
25 * Based on code from gpm-button (gnome power manager)
26 * Copyright (C) 2006-2007 Richard Hughes <richard@hughsie.com>
27 *
28 */
29
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33
34 #ifdef HAVE_XF86_KEYSYM
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39
40 #include <X11/X.h>
41 #include <X11/XF86keysym.h>
42
43 #include <gdk/gdkx.h>
44 #include <gtk/gtk.h>
45
46 #include <glib.h>
47
48 #include <libxfce4util/libxfce4util.h>
49
50 #include "src/misc/parole-debug.h"
51
52 #include "src/enum-gtypes.h"
53 #include "src/parole-button.h"
54
55 static void parole_button_finalize(GObject *object);
56
57 static struct {
58 ParoleButtonKey key;
59 guint key_code;
60 } parole_key_map[PAROLE_KEY_NUMBERS] = { {0, 0}, };
61
62 struct ParoleButtonPrivate {
63 GdkScreen *screen;
64 GdkWindow *window;
65 };
66
67 enum {
68 BUTTON_PRESSED,
69 LAST_SIGNAL
70 };
71
72 #define DUPLICATE_SHUTDOWN_TIMEOUT 4.0f
73
74 static guint signals[LAST_SIGNAL] = { 0 };
75
G_DEFINE_TYPE_WITH_PRIVATE(ParoleButton,parole_button,G_TYPE_OBJECT)76 G_DEFINE_TYPE_WITH_PRIVATE(ParoleButton, parole_button, G_TYPE_OBJECT)
77
78 /**
79 * parole_button_get_key:
80 * @keycode : an #int representing a key on a keyboard
81 *
82 * Check if the pressed key is mapped to a function in Parole.
83 **/
84 static guint
85 parole_button_get_key(unsigned int keycode) {
86 ParoleButtonKey key = PAROLE_KEY_UNKNOWN;
87
88 guint i;
89
90 for (i = 0; i < G_N_ELEMENTS(parole_key_map); i++) {
91 if ( parole_key_map[i].key_code == keycode )
92 key = parole_key_map[i].key;
93 }
94
95 return key;
96 }
97
98 /**
99 * parole_button_filter_x_events:
100 * @xevent : a #GdkXEvent to filter
101 * @ev : the #GdkEvent passed by the callback function
102 * @data : user-data passed by the callback function
103 *
104 * Filter X events for keypresses, and pass the keypresses on to be processed.
105 **/
106 static GdkFilterReturn
parole_button_filter_x_events(GdkXEvent * xevent,GdkEvent * ev,gpointer data)107 parole_button_filter_x_events(GdkXEvent *xevent, GdkEvent *ev, gpointer data) {
108 ParoleButtonKey key;
109 ParoleButton *button;
110
111 XEvent *xev = (XEvent *) xevent;
112
113 if ( xev->type != KeyPress )
114 return GDK_FILTER_CONTINUE;
115
116 key = parole_button_get_key(xev->xkey.keycode);
117
118 if ( key != PAROLE_KEY_UNKNOWN ) {
119 button = (ParoleButton *) data;
120
121 PAROLE_DEBUG_ENUM("Key press", key, ENUM_GTYPE_BUTTON_KEY);
122
123 g_signal_emit(G_OBJECT(button), signals[BUTTON_PRESSED], 0, key);
124 return GDK_FILTER_REMOVE;
125 }
126
127 return GDK_FILTER_CONTINUE;
128 }
129
130 /**
131 * parole_button_grab_keystring:
132 * @button : the #ParoleButton instance to handle keypresses
133 * @keycode : the #int representing the pressed key on the keyboard
134 *
135 * Attempt to get the pressed key and modifier keys.
136 *
137 * Return value: %TRUE on success, else %FALSE
138 **/
139 static gboolean
parole_button_grab_keystring(ParoleButton * button,guint keycode)140 parole_button_grab_keystring(ParoleButton *button, guint keycode) {
141 GdkDisplay *display;
142 guint ret;
143 guint modmask = 0;
144
145 display = gdk_display_get_default();
146
147 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
148 gdk_error_trap_push();
149 G_GNUC_END_IGNORE_DEPRECATIONS
150
151 ret = XGrabKey(GDK_DISPLAY_XDISPLAY(display), keycode, modmask,
152 gdk_x11_window_get_xid(button->priv->window), True,
153 GrabModeAsync, GrabModeAsync);
154
155 if ( ret == BadAccess ) {
156 g_warning("Failed to grab modmask=%u, keycode=%li",
157 modmask, (long int) keycode);
158 return FALSE;
159 }
160
161 ret = XGrabKey(GDK_DISPLAY_XDISPLAY(display), keycode, LockMask | modmask,
162 gdk_x11_window_get_xid(button->priv->window), True,
163 GrabModeAsync, GrabModeAsync);
164
165 if (ret == BadAccess) {
166 g_warning("Failed to grab modmask=%u, keycode=%li",
167 LockMask | modmask, (long int) keycode);
168 return FALSE;
169 }
170
171 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
172 gdk_flush();
173 gdk_error_trap_pop_ignored();
174 G_GNUC_END_IGNORE_DEPRECATIONS
175
176 return TRUE;
177 }
178
179 /**
180 * parole_button_xevent_key:
181 * @button : the #ParoleButton instance to handle keypresses
182 * @keysym : an #int representing the keysym to be converted to a keycode
183 * @key : the #ParoleButtonKey that represents the pressed key
184 *
185 * Attempt to map the key and keycode to the parole_key_map.
186 *
187 * Return value: %TRUE on success, else %FALSE
188 **/
189 static gboolean
parole_button_xevent_key(ParoleButton * button,guint keysym,ParoleButtonKey key)190 parole_button_xevent_key(ParoleButton *button, guint keysym , ParoleButtonKey key) {
191 guint keycode = XKeysymToKeycode(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), keysym);
192
193 if ( keycode == 0 ) {
194 g_warning("could not map keysym %x to keycode\n", keysym);
195 return FALSE;
196 }
197
198 if (!parole_button_grab_keystring(button, keycode)) {
199 g_warning("Failed to grab %i\n", keycode);
200 return FALSE;
201 }
202
203 PAROLE_DEBUG_ENUM_FULL(key, ENUM_GTYPE_BUTTON_KEY, "Grabbed key %li ", (long int)keycode);
204
205 parole_key_map[key].key_code = keycode;
206 parole_key_map[key].key = key;
207
208 return TRUE;
209 }
210
211 /**
212 * parole_button_setup:
213 * @button : the #ParoleButton instance to handle keypresses
214 *
215 * Setup Parole's keyboard mappings.
216 **/
217 static void
parole_button_setup(ParoleButton * button)218 parole_button_setup(ParoleButton *button) {
219 button->priv->screen = gdk_screen_get_default();
220 button->priv->window = gdk_screen_get_root_window(button->priv->screen);
221
222 parole_button_xevent_key(button, XF86XK_AudioPlay, PAROLE_KEY_AUDIO_PLAY);
223 parole_button_xevent_key(button, XF86XK_AudioStop, PAROLE_KEY_AUDIO_STOP);
224 parole_button_xevent_key(button, XF86XK_AudioPrev, PAROLE_KEY_AUDIO_PREV);
225 parole_button_xevent_key(button, XF86XK_AudioNext, PAROLE_KEY_AUDIO_NEXT);
226
227 gdk_window_add_filter(button->priv->window,
228 parole_button_filter_x_events, button);
229 }
230
231 /**
232 * parole_button_class_init:
233 * @klass: a #ParoleButtonClass instance
234 *
235 * Initialize a #ParoleButtonClass instance.
236 **/
237 static void
parole_button_class_init(ParoleButtonClass * klass)238 parole_button_class_init(ParoleButtonClass *klass) {
239 GObjectClass *object_class = G_OBJECT_CLASS(klass);
240
241 signals[BUTTON_PRESSED] =
242 g_signal_new("button-pressed",
243 PAROLE_TYPE_BUTTON,
244 G_SIGNAL_RUN_LAST,
245 G_STRUCT_OFFSET(ParoleButtonClass, button_pressed),
246 NULL, NULL,
247 g_cclosure_marshal_VOID__ENUM,
248 G_TYPE_NONE, 1, ENUM_GTYPE_BUTTON_KEY);
249
250 object_class->finalize = parole_button_finalize;
251 }
252
253 /**
254 * parole_button_init:
255 * @button : a #ParoleButton instance
256 *
257 * Initialize a #ParoleButton instance.
258 **/
259 static void
parole_button_init(ParoleButton * button)260 parole_button_init(ParoleButton *button) {
261 button->priv = parole_button_get_instance_private(button);
262
263 button->priv->screen = NULL;
264 button->priv->window = NULL;
265
266 parole_button_setup(button);
267 }
268
269 /**
270 * parole_button_finalize:
271 * @object : a base #GObject to be made into a #ParoleButton object
272 *
273 * Finalize a #ParoleButton object.
274 **/
275 static void
parole_button_finalize(GObject * object)276 parole_button_finalize(GObject *object) {
277 G_OBJECT_CLASS(parole_button_parent_class)->finalize(object);
278 }
279
280 /**
281 * parole_button_new:
282 *
283 * Create a new #ParoleButton instance.
284 **/
285 ParoleButton *
parole_button_new(void)286 parole_button_new(void) {
287 ParoleButton *button = NULL;
288
289 button = g_object_new(PAROLE_TYPE_BUTTON, NULL);
290
291 return button;
292 }
293
294 #endif /*HAVE_XF86_KEYSYM*/
295