1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3
* Copyright (C) 2006-2007 Richard Hughes <richard@hughsie.com>
5
* Licensed under the GNU General Public License Version 2
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26
#include <glib/gi18n.h>
31
#include <X11/XF86keysym.h>
32
#include <devkit-power-gobject/devicekit-power.h>
34
#include "gpm-common.h"
35
#include "gpm-button.h"
37
#include "egg-debug.h"
39
static void gpm_button_finalize (GObject *object);
41
#define GPM_BUTTON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GPM_TYPE_BUTTON, GpmButtonPrivate))
43
struct GpmButtonPrivate
47
GHashTable *keysym_to_name_hash;
50
gboolean lid_is_closed;
59
static guint signals [LAST_SIGNAL] = { 0 };
60
static gpointer gpm_button_object = NULL;
62
G_DEFINE_TYPE (GpmButton, gpm_button, G_TYPE_OBJECT)
64
#define GPM_BUTTON_DUPLICATE_TIMEOUT 0.125f
67
* gpm_button_emit_type:
70
gpm_button_emit_type (GpmButton *button, const gchar *type)
72
g_return_val_if_fail (GPM_IS_BUTTON (button), FALSE);
74
/* did we just have this button before the timeout? */
75
if (g_strcmp0 (type, button->priv->last_button) == 0 &&
76
g_timer_elapsed (button->priv->timer, NULL) < GPM_BUTTON_DUPLICATE_TIMEOUT) {
77
egg_debug ("ignoring duplicate button %s", type);
81
egg_debug ("emitting button-pressed : %s", type);
82
g_signal_emit (button, signals [BUTTON_PRESSED], 0, type);
84
/* save type and last size */
85
g_free (button->priv->last_button);
86
button->priv->last_button = g_strdup (type);
87
g_timer_reset (button->priv->timer);
93
* gpm_button_filter_x_events:
95
static GdkFilterReturn
96
gpm_button_filter_x_events (GdkXEvent *xevent, GdkEvent *event, gpointer data)
98
GpmButton *button = (GpmButton *) data;
99
XEvent *xev = (XEvent *) xevent;
104
if (xev->type != KeyPress)
105
return GDK_FILTER_CONTINUE;
107
keycode = xev->xkey.keycode;
109
/* is the key string already in our DB? */
110
keycode_str = g_strdup_printf ("0x%x", keycode);
111
key = g_hash_table_lookup (button->priv->keysym_to_name_hash, (gpointer) keycode_str);
112
g_free (keycode_str);
114
/* found anything? */
116
egg_debug ("Key %i not found in hash", keycode);
117
/* pass normal keypresses on, which might help with accessibility access */
118
return GDK_FILTER_CONTINUE;
121
egg_debug ("Key %i mapped to key %s", keycode, key);
122
gpm_button_emit_type (button, key);
124
return GDK_FILTER_REMOVE;
128
* gpm_button_grab_keystring:
129
* @button: This button class instance
130
* @keystr: The key string, e.g. "<Control><Alt>F11"
131
* @hashkey: A unique key made up from the modmask and keycode suitable for
132
* referencing in a hashtable.
133
* You must free this string, or specify NULL to ignore.
135
* Grab the key specified in the key string.
137
* Return value: TRUE if we parsed and grabbed okay
140
gpm_button_grab_keystring (GpmButton *button, guint64 keycode)
142
guint modmask = AnyModifier;
146
/* get the current X Display */
147
display = GDK_DISPLAY ();
149
/* don't abort on error */
150
gdk_error_trap_push ();
152
/* grab the key if possible */
153
ret = XGrabKey (display, keycode, modmask,
154
GDK_WINDOW_XID (button->priv->window), True,
155
GrabModeAsync, GrabModeAsync);
156
if (ret == BadAccess) {
157
egg_warning ("Failed to grab modmask=%u, keycode=%li",
158
modmask, (long int) keycode);
162
/* grab the lock key if possible */
163
ret = XGrabKey (display, keycode, LockMask | modmask,
164
GDK_WINDOW_XID (button->priv->window), True,
165
GrabModeAsync, GrabModeAsync);
166
if (ret == BadAccess) {
167
egg_warning ("Failed to grab modmask=%u, keycode=%li",
168
LockMask | modmask, (long int) keycode);
172
/* we are not processing the error */
174
gdk_error_trap_pop ();
176
egg_debug ("Grabbed modmask=%x, keycode=%li", modmask, (long int) keycode);
181
* gpm_button_grab_keystring:
182
* @button: This button class instance
183
* @keystr: The key string, e.g. "<Control><Alt>F11"
184
* @hashkey: A unique key made up from the modmask and keycode suitable for
185
* referencing in a hashtable.
186
* You must free this string, or specify NULL to ignore.
188
* Grab the key specified in the key string.
190
* Return value: TRUE if we parsed and grabbed okay
193
gpm_button_xevent_key (GpmButton *button, guint keysym, const gchar *key_name)
200
/* convert from keysym to keycode */
201
keycode = XKeysymToKeycode (GDK_DISPLAY (), keysym);
203
egg_warning ("could not map keysym %x to keycode", keysym);
207
/* is the key string already in our DB? */
208
keycode_str = g_strdup_printf ("0x%x", keycode);
209
key = g_hash_table_lookup (button->priv->keysym_to_name_hash, (gpointer) keycode_str);
211
egg_warning ("found in hash %i", keycode);
212
g_free (keycode_str);
216
/* try to register X event */
217
ret = gpm_button_grab_keystring (button, keycode);
219
egg_warning ("Failed to grab %i", keycode);
220
g_free (keycode_str);
224
/* add to hash table */
225
g_hash_table_insert (button->priv->keysym_to_name_hash, (gpointer) keycode_str, (gpointer) g_strdup (key_name));
227
/* the key is freed in the hash function unref */
232
* gpm_button_class_init:
233
* @button: This class instance
236
gpm_button_class_init (GpmButtonClass *klass)
238
GObjectClass *object_class = G_OBJECT_CLASS (klass);
239
object_class->finalize = gpm_button_finalize;
240
g_type_class_add_private (klass, sizeof (GpmButtonPrivate));
242
signals [BUTTON_PRESSED] =
243
g_signal_new ("button-pressed",
244
G_TYPE_FROM_CLASS (object_class),
246
G_STRUCT_OFFSET (GpmButtonClass, button_pressed),
248
g_cclosure_marshal_VOID__STRING,
249
G_TYPE_NONE, 1, G_TYPE_STRING);
253
* gpm_button_is_lid_closed:
256
gpm_button_is_lid_closed (GpmButton *button)
258
gboolean lid_is_closed;
260
g_return_val_if_fail (GPM_IS_BUTTON (button), FALSE);
262
g_object_get (button->priv->client,
263
"lid-is-closed", &lid_is_closed,
265
return lid_is_closed;
269
* gpm_button_reset_time:
271
* We have to refresh the event time on resume to handle duplicate buttons
272
* properly when the time is significant when we suspend.
275
gpm_button_reset_time (GpmButton *button)
277
g_return_val_if_fail (GPM_IS_BUTTON (button), FALSE);
278
g_timer_reset (button->priv->timer);
283
* gpm_button_client_changed_cb
286
gpm_button_client_changed_cb (DkpClient *client, GpmButton *button)
288
gboolean lid_is_closed;
291
g_object_get (client,
292
"lid-is-closed", &lid_is_closed,
296
if (button->priv->lid_is_closed == lid_is_closed)
300
button->priv->lid_is_closed = lid_is_closed;
302
/* sent correct event */
304
gpm_button_emit_type (button, GPM_BUTTON_LID_CLOSED);
306
gpm_button_emit_type (button, GPM_BUTTON_LID_OPEN);
311
* @button: This class instance
314
gpm_button_init (GpmButton *button)
316
button->priv = GPM_BUTTON_GET_PRIVATE (button);
318
button->priv->screen = gdk_screen_get_default ();
319
button->priv->window = gdk_screen_get_root_window (button->priv->screen);
321
button->priv->keysym_to_name_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
322
button->priv->last_button = NULL;
323
button->priv->timer = g_timer_new ();
325
button->priv->lid_is_closed = FALSE;
326
button->priv->client = dkp_client_new ();
327
g_signal_connect (button->priv->client, "changed",
328
G_CALLBACK (gpm_button_client_changed_cb), button);
330
/* register the brightness keys */
331
gpm_button_xevent_key (button, XF86XK_PowerOff, GPM_BUTTON_POWER);
332
#ifdef HAVE_XF86XK_SUSPEND
333
/* The kernel messes up suspend/hibernate in some places. One of
334
* them is the key names. Unfortunately, they refuse to see the
335
* errors of their way in the name of 'compatibility'. Meh
337
gpm_button_xevent_key (button, XF86XK_Suspend, GPM_BUTTON_HIBERNATE);
339
gpm_button_xevent_key (button, XF86XK_Sleep, GPM_BUTTON_SUSPEND); /* should be configurable */
340
#ifdef HAVE_XF86XK_HIBERNATE
341
gpm_button_xevent_key (button, XF86XK_Hibernate, GPM_BUTTON_HIBERNATE);
343
gpm_button_xevent_key (button, XF86XK_MonBrightnessUp, GPM_BUTTON_BRIGHT_UP);
344
gpm_button_xevent_key (button, XF86XK_MonBrightnessDown, GPM_BUTTON_BRIGHT_DOWN);
345
gpm_button_xevent_key (button, XF86XK_ScreenSaver, GPM_BUTTON_LOCK);
346
#ifdef HAVE_XF86XK_BATTERY
347
gpm_button_xevent_key (button, XF86XK_Battery, GPM_BUTTON_BATTERY);
349
gpm_button_xevent_key (button, XF86XK_KbdBrightnessUp, GPM_BUTTON_KBD_BRIGHT_UP);
350
gpm_button_xevent_key (button, XF86XK_KbdBrightnessDown, GPM_BUTTON_KBD_BRIGHT_DOWN);
351
gpm_button_xevent_key (button, XF86XK_KbdLightOnOff, GPM_BUTTON_KBD_BRIGHT_TOGGLE);
353
/* use global filter */
354
gdk_window_add_filter (button->priv->window,
355
gpm_button_filter_x_events, (gpointer) button);
359
* gpm_button_finalize:
360
* @object: This class instance
363
gpm_button_finalize (GObject *object)
366
g_return_if_fail (object != NULL);
367
g_return_if_fail (GPM_IS_BUTTON (object));
369
button = GPM_BUTTON (object);
370
button->priv = GPM_BUTTON_GET_PRIVATE (button);
372
g_object_unref (button->priv->client);
373
g_free (button->priv->last_button);
374
g_timer_destroy (button->priv->timer);
376
g_hash_table_unref (button->priv->keysym_to_name_hash);
378
G_OBJECT_CLASS (gpm_button_parent_class)->finalize (object);
383
* Return value: new class instance.
386
gpm_button_new (void)
388
if (gpm_button_object != NULL) {
389
g_object_ref (gpm_button_object);
391
gpm_button_object = g_object_new (GPM_TYPE_BUTTON, NULL);
392
g_object_add_weak_pointer (gpm_button_object, &gpm_button_object);
394
return GPM_BUTTON (gpm_button_object);