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