1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2 *
3 * Copyright © 2001 Ximian, Inc.
4 * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
5 * Written by Sergey V. Oudaltsov <svu@users.sourceforge.net>
6 *
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.
11 *
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.
16 *
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 - Suite 500, Boston, MA 02110-1335, USA.
20 *
21 */
22
23 #include "config.h"
24
25 #include <sys/types.h>
26 #include <sys/wait.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <errno.h>
32
33 #include <locale.h>
34
35 #include <glib.h>
36 #include <glib/gi18n.h>
37 #include <gdk/gdk.h>
38 #include <gdk/gdkx.h>
39 #include <gtk/gtk.h>
40
41 #include <X11/XKBlib.h>
42 #include <X11/keysym.h>
43
44 #include "cinnamon-settings-profile.h"
45 #include "csd-keyboard-manager.h"
46 #include "csd-enums.h"
47
48 #include "csd-keyboard-xkb.h"
49
50 #define CSD_KEYBOARD_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CSD_TYPE_KEYBOARD_MANAGER, CsdKeyboardManagerPrivate))
51
52 #ifndef HOST_NAME_MAX
53 # define HOST_NAME_MAX 255
54 #endif
55
56 #define CSD_KEYBOARD_DIR "org.cinnamon.settings-daemon.peripherals.keyboard"
57
58 #define KEY_REPEAT "repeat"
59 #define KEY_CLICK "click"
60 #define KEY_INTERVAL "repeat-interval"
61 #define KEY_DELAY "delay"
62 #define KEY_CLICK_VOLUME "click-volume"
63 #define KEY_NUMLOCK_STATE "numlock-state"
64
65 #define KEY_BELL_VOLUME "bell-volume"
66 #define KEY_BELL_PITCH "bell-pitch"
67 #define KEY_BELL_DURATION "bell-duration"
68 #define KEY_BELL_MODE "bell-mode"
69
70 struct CsdKeyboardManagerPrivate
71 {
72 guint start_idle_id;
73 GSettings *settings;
74 gboolean have_xkb;
75 gint xkb_event_base;
76 CsdNumLockState old_state;
77 };
78
79 static void csd_keyboard_manager_finalize (GObject *object);
80
81 G_DEFINE_TYPE (CsdKeyboardManager, csd_keyboard_manager, G_TYPE_OBJECT)
82
83 static gpointer manager_object = NULL;
84
85 static gboolean
xkb_set_keyboard_autorepeat_rate(guint delay,guint interval)86 xkb_set_keyboard_autorepeat_rate (guint delay, guint interval)
87 {
88 return XkbSetAutoRepeatRate (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
89 XkbUseCoreKbd,
90 delay,
91 interval);
92 }
93
94 static void
numlock_xkb_init(CsdKeyboardManager * manager)95 numlock_xkb_init (CsdKeyboardManager *manager)
96 {
97 Display *dpy = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
98 gboolean have_xkb;
99 int opcode, error_base, major, minor;
100
101 have_xkb = XkbQueryExtension (dpy,
102 &opcode,
103 &manager->priv->xkb_event_base,
104 &error_base,
105 &major,
106 &minor)
107 && XkbUseExtension (dpy, &major, &minor);
108
109 if (have_xkb) {
110 XkbSelectEventDetails (dpy,
111 XkbUseCoreKbd,
112 XkbStateNotifyMask,
113 XkbModifierLockMask,
114 XkbModifierLockMask);
115 } else {
116 g_warning ("XKB extension not available");
117 }
118
119 manager->priv->have_xkb = have_xkb;
120 }
121
122 static unsigned
numlock_NumLock_modifier_mask(void)123 numlock_NumLock_modifier_mask (void)
124 {
125 Display *dpy = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
126 return XkbKeysymToModifiers (dpy, XK_Num_Lock);
127 }
128
129 static void
numlock_set_xkb_state(CsdNumLockState new_state)130 numlock_set_xkb_state (CsdNumLockState new_state)
131 {
132 unsigned int num_mask;
133 Display *dpy = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
134 if (new_state != CSD_NUM_LOCK_STATE_ON && new_state != CSD_NUM_LOCK_STATE_OFF)
135 return;
136 num_mask = numlock_NumLock_modifier_mask ();
137 XkbLockModifiers (dpy, XkbUseCoreKbd, num_mask, new_state == CSD_NUM_LOCK_STATE_ON ? num_mask : 0);
138 }
139
140 static GdkFilterReturn
numlock_xkb_callback(GdkXEvent * xev_,GdkEvent * gdkev_,gpointer user_data)141 numlock_xkb_callback (GdkXEvent *xev_,
142 GdkEvent *gdkev_,
143 gpointer user_data)
144 {
145 XEvent *xev = (XEvent *) xev_;
146 XkbEvent *xkbev = (XkbEvent *) xev;
147 CsdKeyboardManager *manager = (CsdKeyboardManager *) user_data;
148
149 if (xev->type != manager->priv->xkb_event_base)
150 return GDK_FILTER_CONTINUE;
151
152 if (xkbev->any.xkb_type != XkbStateNotify)
153 return GDK_FILTER_CONTINUE;
154
155 if (xkbev->state.changed & XkbModifierLockMask) {
156 unsigned num_mask = numlock_NumLock_modifier_mask ();
157 unsigned locked_mods = xkbev->state.locked_mods;
158 CsdNumLockState numlock_state;
159
160 numlock_state = (num_mask & locked_mods) ? CSD_NUM_LOCK_STATE_ON : CSD_NUM_LOCK_STATE_OFF;
161
162 if (numlock_state != manager->priv->old_state) {
163 g_settings_set_enum (manager->priv->settings,
164 KEY_NUMLOCK_STATE,
165 numlock_state);
166 manager->priv->old_state = numlock_state;
167 }
168 }
169
170 return GDK_FILTER_CONTINUE;
171 }
172
173 static void
numlock_install_xkb_callback(CsdKeyboardManager * manager)174 numlock_install_xkb_callback (CsdKeyboardManager *manager)
175 {
176 if (!manager->priv->have_xkb)
177 return;
178
179 gdk_window_add_filter (NULL,
180 numlock_xkb_callback,
181 manager);
182 }
183
184 static guint
_csd_settings_get_uint(GSettings * settings,const char * key)185 _csd_settings_get_uint (GSettings *settings,
186 const char *key)
187 {
188 guint value;
189
190 g_settings_get (settings, key, "u", &value);
191 return value;
192 }
193
194 static void
apply_settings(GSettings * settings,const char * key,CsdKeyboardManager * manager)195 apply_settings (GSettings *settings,
196 const char *key,
197 CsdKeyboardManager *manager)
198 {
199 XKeyboardControl kbdcontrol;
200 gboolean repeat;
201 gboolean click;
202 guint interval;
203 guint delay;
204 int click_volume;
205 int bell_volume;
206 int bell_pitch;
207 int bell_duration;
208 CsdBellMode bell_mode;
209 gboolean rnumlock;
210
211 if (g_strcmp0 (key, KEY_NUMLOCK_STATE) == 0)
212 return;
213
214 repeat = g_settings_get_boolean (settings, KEY_REPEAT);
215 click = g_settings_get_boolean (settings, KEY_CLICK);
216 interval = _csd_settings_get_uint (settings, KEY_INTERVAL);
217 delay = _csd_settings_get_uint (settings, KEY_DELAY);
218 click_volume = g_settings_get_int (settings, KEY_CLICK_VOLUME);
219 bell_pitch = g_settings_get_int (settings, KEY_BELL_PITCH);
220 bell_duration = g_settings_get_int (settings, KEY_BELL_DURATION);
221
222 bell_mode = g_settings_get_enum (settings, KEY_BELL_MODE);
223 bell_volume = (bell_mode == CSD_BELL_MODE_ON) ? 50 : 0;
224
225 gdk_x11_display_error_trap_push (gdk_display_get_default ());
226 if (repeat) {
227 gboolean rate_set = FALSE;
228
229 XAutoRepeatOn (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()));
230 /* Use XKB in preference */
231 rate_set = xkb_set_keyboard_autorepeat_rate (delay, interval);
232
233 if (!rate_set)
234 g_warning ("Neither XKeyboard not Xfree86's keyboard extensions are available,\n"
235 "no way to support keyboard autorepeat rate settings");
236 } else {
237 XAutoRepeatOff (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()));
238 }
239
240 /* as percentage from 0..100 inclusive */
241 if (click_volume < 0) {
242 click_volume = 0;
243 } else if (click_volume > 100) {
244 click_volume = 100;
245 }
246 kbdcontrol.key_click_percent = click ? click_volume : 0;
247 kbdcontrol.bell_percent = bell_volume;
248 kbdcontrol.bell_pitch = bell_pitch;
249 kbdcontrol.bell_duration = bell_duration;
250 XChangeKeyboardControl (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
251 KBKeyClickPercent | KBBellPercent | KBBellPitch | KBBellDuration,
252 &kbdcontrol);
253
254 if (g_strcmp0 (key, "remember-numlock-state") == 0 || key == NULL) {
255 rnumlock = g_settings_get_boolean (settings, "remember-numlock-state");
256
257 manager->priv->old_state = g_settings_get_enum (manager->priv->settings, KEY_NUMLOCK_STATE);
258
259 if (manager->priv->have_xkb && rnumlock)
260 numlock_set_xkb_state (manager->priv->old_state);
261 }
262
263 XSync (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), FALSE);
264 gdk_x11_display_error_trap_pop_ignored (gdk_display_get_default ());
265 }
266
267 void
csd_keyboard_manager_apply_settings(CsdKeyboardManager * manager)268 csd_keyboard_manager_apply_settings (CsdKeyboardManager *manager)
269 {
270 apply_settings (manager->priv->settings, NULL, manager);
271 }
272
273 static gboolean
start_keyboard_idle_cb(CsdKeyboardManager * manager)274 start_keyboard_idle_cb (CsdKeyboardManager *manager)
275 {
276 cinnamon_settings_profile_start (NULL);
277
278 g_debug ("Starting keyboard manager");
279
280 manager->priv->have_xkb = 0;
281 manager->priv->settings = g_settings_new (CSD_KEYBOARD_DIR);
282
283 /* Essential - xkb initialization should happen before */
284 csd_keyboard_xkb_init (manager);
285
286 numlock_xkb_init (manager);
287
288 /* apply current settings before we install the callback */
289 csd_keyboard_manager_apply_settings (manager);
290
291 g_signal_connect (G_OBJECT (manager->priv->settings), "changed",
292 G_CALLBACK (apply_settings), manager);
293
294 numlock_install_xkb_callback (manager);
295
296 cinnamon_settings_profile_end (NULL);
297
298 manager->priv->start_idle_id = 0;
299
300 return FALSE;
301 }
302
303 gboolean
csd_keyboard_manager_start(CsdKeyboardManager * manager,GError ** error)304 csd_keyboard_manager_start (CsdKeyboardManager *manager,
305 GError **error)
306 {
307 cinnamon_settings_profile_start (NULL);
308
309 manager->priv->start_idle_id = g_idle_add ((GSourceFunc) start_keyboard_idle_cb, manager);
310
311 cinnamon_settings_profile_end (NULL);
312
313 return TRUE;
314 }
315
316 void
csd_keyboard_manager_stop(CsdKeyboardManager * manager)317 csd_keyboard_manager_stop (CsdKeyboardManager *manager)
318 {
319 CsdKeyboardManagerPrivate *p = manager->priv;
320
321 g_debug ("Stopping keyboard manager");
322
323 if (p->settings != NULL) {
324 g_object_unref (p->settings);
325 p->settings = NULL;
326 }
327
328 if (p->have_xkb) {
329 gdk_window_remove_filter (NULL,
330 numlock_xkb_callback,
331 manager);
332 }
333
334 csd_keyboard_xkb_shutdown ();
335 }
336
337 static GObject *
csd_keyboard_manager_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_properties)338 csd_keyboard_manager_constructor (GType type,
339 guint n_construct_properties,
340 GObjectConstructParam *construct_properties)
341 {
342 CsdKeyboardManager *keyboard_manager;
343
344 keyboard_manager = CSD_KEYBOARD_MANAGER (G_OBJECT_CLASS (csd_keyboard_manager_parent_class)->constructor (type,
345 n_construct_properties,
346 construct_properties));
347
348 return G_OBJECT (keyboard_manager);
349 }
350
351 static void
csd_keyboard_manager_class_init(CsdKeyboardManagerClass * klass)352 csd_keyboard_manager_class_init (CsdKeyboardManagerClass *klass)
353 {
354 GObjectClass *object_class = G_OBJECT_CLASS (klass);
355
356 object_class->constructor = csd_keyboard_manager_constructor;
357 object_class->finalize = csd_keyboard_manager_finalize;
358
359 g_type_class_add_private (klass, sizeof (CsdKeyboardManagerPrivate));
360 }
361
362 static void
csd_keyboard_manager_init(CsdKeyboardManager * manager)363 csd_keyboard_manager_init (CsdKeyboardManager *manager)
364 {
365 manager->priv = CSD_KEYBOARD_MANAGER_GET_PRIVATE (manager);
366 }
367
368 static void
csd_keyboard_manager_finalize(GObject * object)369 csd_keyboard_manager_finalize (GObject *object)
370 {
371 CsdKeyboardManager *keyboard_manager;
372
373 g_return_if_fail (object != NULL);
374 g_return_if_fail (CSD_IS_KEYBOARD_MANAGER (object));
375
376 keyboard_manager = CSD_KEYBOARD_MANAGER (object);
377
378 g_return_if_fail (keyboard_manager->priv != NULL);
379
380 if (keyboard_manager->priv->start_idle_id != 0) {
381 g_source_remove (keyboard_manager->priv->start_idle_id);
382 keyboard_manager->priv->start_idle_id = 0;
383 }
384
385 G_OBJECT_CLASS (csd_keyboard_manager_parent_class)->finalize (object);
386 }
387
388 CsdKeyboardManager *
csd_keyboard_manager_new(void)389 csd_keyboard_manager_new (void)
390 {
391 if (manager_object != NULL) {
392 g_object_ref (manager_object);
393 } else {
394 manager_object = g_object_new (CSD_TYPE_KEYBOARD_MANAGER, NULL);
395 g_object_add_weak_pointer (manager_object,
396 (gpointer *) &manager_object);
397 }
398
399 return CSD_KEYBOARD_MANAGER (manager_object);
400 }
401