1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2
3 /*
4 * Copyright (C) 2001 Havoc Pennington, Copyright (C) 2002 Red Hat Inc.
5 * Copyright (C) 2006 Elijah Newren
6 * Copyright (C) 2008 Thomas Thurman
7 * Copyright (C) 2010 Milan Bouchet-Valat, Copyright (C) 2011 Red Hat Inc.
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of the
12 * License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, see <http://www.gnu.org/licenses/>.
21 */
22
23 /**
24 * SECTION:prefs
25 * @title: Preferences
26 * @short_description: Mutter preferences
27 */
28
29 #include "config.h"
30
31 #include <glib.h>
32 #include <gio/gio.h>
33 #include <string.h>
34 #include <stdlib.h>
35
36 #include "compositor/meta-plugin-manager.h"
37 #include "core/keybindings-private.h"
38 #include "core/meta-accel-parse.h"
39 #include "core/prefs-private.h"
40 #include "core/util-private.h"
41 #include "meta/prefs.h"
42 #include "x11/meta-x11-display-private.h"
43
44 /* If you add a key, it needs updating in init() and in the gsettings
45 * notify listener and of course in the .schemas file.
46 *
47 * Keys which are handled by one of the unified handlers below are
48 * not given a name here, because the purpose of the unified handlers
49 * is that keys should be referred to exactly once.
50 */
51 #define KEY_TITLEBAR_FONT "titlebar-font"
52 #define KEY_NUM_WORKSPACES "num-workspaces"
53 #define KEY_WORKSPACE_NAMES "workspace-names"
54
55 /* Keys from "foreign" schemas */
56 #define KEY_GNOME_ACCESSIBILITY "toolkit-accessibility"
57 #define KEY_GNOME_ANIMATIONS "enable-animations"
58 #define KEY_GNOME_CURSOR_THEME "cursor-theme"
59 #define KEY_GNOME_CURSOR_SIZE "cursor-size"
60 #define KEY_XKB_OPTIONS "xkb-options"
61
62 #define KEY_OVERLAY_KEY "overlay-key"
63 #define KEY_WORKSPACES_ONLY_ON_PRIMARY "workspaces-only-on-primary"
64 #define KEY_LOCATE_POINTER "locate-pointer"
65
66 /* These are the different schemas we are keeping
67 * a GSettings instance for */
68 #define SCHEMA_GENERAL "org.gnome.desktop.wm.preferences"
69 #define SCHEMA_MUTTER "org.gnome.mutter"
70 #define SCHEMA_INTERFACE "org.gnome.desktop.interface"
71 #define SCHEMA_INPUT_SOURCES "org.gnome.desktop.input-sources"
72 #define SCHEMA_MOUSE "org.gnome.desktop.peripherals.mouse"
73
74 #define SETTINGS(s) g_hash_table_lookup (settings_schemas, (s))
75
76 static GList *changes = NULL;
77 static guint changed_idle;
78 static GList *listeners = NULL;
79 static GHashTable *settings_schemas;
80
81 static gboolean use_system_font = FALSE;
82 static PangoFontDescription *titlebar_font = NULL;
83 static MetaVirtualModifier mouse_button_mods = Mod1Mask;
84 static MetaKeyCombo overlay_key_combo = { 0, 0, 0 };
85 static MetaKeyCombo locate_pointer_key_combo = { 0, 0, 0 };
86 static GDesktopFocusMode focus_mode = G_DESKTOP_FOCUS_MODE_CLICK;
87 static GDesktopFocusNewWindows focus_new_windows = G_DESKTOP_FOCUS_NEW_WINDOWS_SMART;
88 static gboolean raise_on_click = TRUE;
89 static gboolean center_new_windows = FALSE;
90 static gboolean attach_modal_dialogs = FALSE;
91 static int num_workspaces = 4;
92 static GDesktopTitlebarAction action_double_click_titlebar = G_DESKTOP_TITLEBAR_ACTION_TOGGLE_MAXIMIZE;
93 static GDesktopTitlebarAction action_middle_click_titlebar = G_DESKTOP_TITLEBAR_ACTION_LOWER;
94 static GDesktopTitlebarAction action_right_click_titlebar = G_DESKTOP_TITLEBAR_ACTION_MENU;
95 static gboolean dynamic_workspaces = FALSE;
96 static gboolean disable_workarounds = FALSE;
97 static gboolean auto_raise = FALSE;
98 static gboolean auto_raise_delay = 500;
99 static gboolean focus_change_on_pointer_rest = FALSE;
100 static gboolean bell_is_visible = FALSE;
101 static gboolean bell_is_audible = TRUE;
102 static gboolean gnome_accessibility = FALSE;
103 static gboolean gnome_animations = TRUE;
104 static gboolean locate_pointer_is_enabled = FALSE;
105 static unsigned int check_alive_timeout = 5000;
106 static char *cursor_theme = NULL;
107 /* cursor_size will, when running as an X11 compositing window manager, be the
108 * actual cursor size, multiplied with the global window scaling factor. On
109 * Wayland, it will be the actual cursor size retrieved from gsettings.
110 */
111 static int cursor_size = 24;
112 static int draggable_border_width = 10;
113 static int drag_threshold;
114 static gboolean resize_with_right_button = FALSE;
115 static gboolean edge_tiling = FALSE;
116 static gboolean force_fullscreen = TRUE;
117 static gboolean auto_maximize = TRUE;
118 static gboolean show_fallback_app_menu = TRUE;
119
120 static GDesktopVisualBellType visual_bell_type = G_DESKTOP_VISUAL_BELL_FULLSCREEN_FLASH;
121 static MetaButtonLayout button_layout;
122
123 /* NULL-terminated array */
124 static char **workspace_names = NULL;
125
126 static gboolean workspaces_only_on_primary = FALSE;
127
128 static char *iso_next_group_option = NULL;
129
130 static void handle_preference_update_enum (GSettings *settings,
131 gchar *key);
132 static gboolean update_binding (MetaKeyPref *binding,
133 gchar **strokes);
134 static gboolean update_key_binding (const char *key,
135 gchar **strokes);
136
137 static void settings_changed (GSettings *settings,
138 gchar *key,
139 gpointer data);
140 static void bindings_changed (GSettings *settings,
141 gchar *key,
142 gpointer data);
143
144 static void queue_changed (MetaPreference pref);
145
146 static void maybe_give_disable_workarounds_warning (void);
147
148 static gboolean titlebar_handler (GVariant*, gpointer*, gpointer);
149 static gboolean mouse_button_mods_handler (GVariant*, gpointer*, gpointer);
150 static gboolean button_layout_handler (GVariant*, gpointer*, gpointer);
151 static gboolean overlay_key_handler (GVariant*, gpointer*, gpointer);
152 static gboolean locate_pointer_key_handler (GVariant*, gpointer*, gpointer);
153
154 static gboolean iso_next_group_handler (GVariant*, gpointer*, gpointer);
155
156 static void init_bindings (void);
157
158 typedef struct
159 {
160 MetaPrefsChangedFunc func;
161 gpointer data;
162 } MetaPrefsListener;
163
164 typedef struct
165 {
166 const char *key;
167 const char *schema;
168 MetaPreference pref;
169 } MetaBasePreference;
170
171 typedef struct
172 {
173 MetaBasePreference base;
174 gpointer target;
175 } MetaEnumPreference;
176
177 typedef struct
178 {
179 MetaBasePreference base;
180 gboolean *target;
181 } MetaBoolPreference;
182
183
184 /**
185 * MetaStringPreference:
186 * @handler: (nullable): A handler. Many of the string preferences
187 * aren't stored as strings and need parsing; others of them have
188 * default values which can't be solved in the general case. If you
189 * include a function pointer here, it will be called instead of writing
190 * the string value out to the target variable.
191 * The function will be passed to g_settings_get_mapped() and should
192 * return %TRUE if the mapping was successful and %FALSE otherwise.
193 * In the former case the function is expected to handle the result
194 * of the conversion itself and call queue_changed() appropriately;
195 * in particular the @result (out) parameter as returned by
196 * g_settings_get_mapped() will be ignored in all cases.
197 * This may be %NULL. If it is, see "target", below.
198 * @target: (nullable): Where to write the incoming string.
199 * This must be %NULL if the handler is non-%NULL.
200 * If the incoming string is %NULL, no change will be made.
201 */
202 typedef struct
203 {
204 MetaBasePreference base;
205 GSettingsGetMapping handler;
206 gchar **target;
207 } MetaStringPreference;
208
209 typedef struct
210 {
211 MetaBasePreference base;
212 GSettingsGetMapping handler;
213 gchar ***target;
214 } MetaStringArrayPreference;
215
216 typedef struct
217 {
218 MetaBasePreference base;
219 gint *target;
220 } MetaIntPreference;
221
222 typedef struct
223 {
224 MetaBasePreference base;
225 unsigned int *target;
226 } MetaUintPreference;
227
228
229 /* All preferences that are not keybindings must be listed here,
230 * plus in the GSettings schemas and the MetaPreference enum.
231 */
232
233 /* FIXMEs: */
234 /* @@@ Don't use NULL lines at the end; glib can tell you how big it is */
235
236 static MetaEnumPreference preferences_enum[] =
237 {
238 {
239 { "focus-new-windows",
240 SCHEMA_GENERAL,
241 META_PREF_FOCUS_NEW_WINDOWS,
242 },
243 &focus_new_windows,
244 },
245 {
246 { "focus-mode",
247 SCHEMA_GENERAL,
248 META_PREF_FOCUS_MODE,
249 },
250 &focus_mode,
251 },
252 {
253 { "visual-bell-type",
254 SCHEMA_GENERAL,
255 META_PREF_VISUAL_BELL_TYPE,
256 },
257 &visual_bell_type,
258 },
259 {
260 { "action-double-click-titlebar",
261 SCHEMA_GENERAL,
262 META_PREF_ACTION_DOUBLE_CLICK_TITLEBAR,
263 },
264 &action_double_click_titlebar,
265 },
266 {
267 { "action-middle-click-titlebar",
268 SCHEMA_GENERAL,
269 META_PREF_ACTION_MIDDLE_CLICK_TITLEBAR,
270 },
271 &action_middle_click_titlebar,
272 },
273 {
274 { "action-right-click-titlebar",
275 SCHEMA_GENERAL,
276 META_PREF_ACTION_RIGHT_CLICK_TITLEBAR,
277 },
278 &action_right_click_titlebar,
279 },
280 { { NULL, 0, 0 }, NULL },
281 };
282
283 static MetaBoolPreference preferences_bool[] =
284 {
285 {
286 { "attach-modal-dialogs",
287 SCHEMA_MUTTER,
288 META_PREF_ATTACH_MODAL_DIALOGS,
289 },
290 &attach_modal_dialogs,
291 },
292 {
293 { "center-new-windows",
294 SCHEMA_MUTTER,
295 META_PREF_CENTER_NEW_WINDOWS,
296 },
297 ¢er_new_windows,
298 },
299 {
300 { "raise-on-click",
301 SCHEMA_GENERAL,
302 META_PREF_RAISE_ON_CLICK,
303 },
304 &raise_on_click,
305 },
306 {
307 { "titlebar-uses-system-font",
308 SCHEMA_GENERAL,
309 META_PREF_TITLEBAR_FONT, /* note! shares a pref */
310 },
311 &use_system_font,
312 },
313 {
314 { "dynamic-workspaces",
315 SCHEMA_MUTTER,
316 META_PREF_DYNAMIC_WORKSPACES,
317 },
318 &dynamic_workspaces,
319 },
320 {
321 { "disable-workarounds",
322 SCHEMA_GENERAL,
323 META_PREF_DISABLE_WORKAROUNDS,
324 },
325 &disable_workarounds,
326 },
327 {
328 { "auto-raise",
329 SCHEMA_GENERAL,
330 META_PREF_AUTO_RAISE,
331 },
332 &auto_raise,
333 },
334 {
335 { "focus-change-on-pointer-rest",
336 SCHEMA_MUTTER,
337 META_PREF_FOCUS_CHANGE_ON_POINTER_REST,
338 },
339 &focus_change_on_pointer_rest
340 },
341 {
342 { "visual-bell",
343 SCHEMA_GENERAL,
344 META_PREF_VISUAL_BELL,
345 },
346 &bell_is_visible, /* FIXME: change the name: it's confusing */
347 },
348 {
349 { "audible-bell",
350 SCHEMA_GENERAL,
351 META_PREF_AUDIBLE_BELL,
352 },
353 &bell_is_audible, /* FIXME: change the name: it's confusing */
354 },
355 {
356 { KEY_GNOME_ACCESSIBILITY,
357 SCHEMA_INTERFACE,
358 META_PREF_GNOME_ACCESSIBILITY,
359 },
360 &gnome_accessibility,
361 },
362 {
363 { KEY_GNOME_ANIMATIONS,
364 SCHEMA_INTERFACE,
365 META_PREF_GNOME_ANIMATIONS,
366 },
367 &gnome_animations,
368 },
369 {
370 { "resize-with-right-button",
371 SCHEMA_GENERAL,
372 META_PREF_RESIZE_WITH_RIGHT_BUTTON,
373 },
374 &resize_with_right_button,
375 },
376 {
377 { "edge-tiling",
378 SCHEMA_MUTTER,
379 META_PREF_EDGE_TILING,
380 },
381 &edge_tiling,
382 },
383 {
384 { "workspaces-only-on-primary",
385 SCHEMA_MUTTER,
386 META_PREF_WORKSPACES_ONLY_ON_PRIMARY,
387 },
388 &workspaces_only_on_primary,
389 },
390 {
391 { "auto-maximize",
392 SCHEMA_MUTTER,
393 META_PREF_AUTO_MAXIMIZE,
394 },
395 &auto_maximize,
396 },
397 {
398 { KEY_LOCATE_POINTER,
399 SCHEMA_INTERFACE,
400 META_PREF_LOCATE_POINTER,
401 },
402 &locate_pointer_is_enabled,
403 },
404 { { NULL, 0, 0 }, NULL },
405 };
406
407 static MetaStringPreference preferences_string[] =
408 {
409 {
410 { "mouse-button-modifier",
411 SCHEMA_GENERAL,
412 META_PREF_MOUSE_BUTTON_MODS,
413 },
414 mouse_button_mods_handler,
415 NULL,
416 },
417 {
418 { KEY_TITLEBAR_FONT,
419 SCHEMA_GENERAL,
420 META_PREF_TITLEBAR_FONT,
421 },
422 titlebar_handler,
423 NULL,
424 },
425 {
426 { "button-layout",
427 SCHEMA_GENERAL,
428 META_PREF_BUTTON_LAYOUT,
429 },
430 button_layout_handler,
431 NULL,
432 },
433 {
434 { "cursor-theme",
435 SCHEMA_INTERFACE,
436 META_PREF_CURSOR_THEME,
437 },
438 NULL,
439 &cursor_theme,
440 },
441 {
442 { "overlay-key",
443 SCHEMA_MUTTER,
444 META_PREF_KEYBINDINGS,
445 },
446 overlay_key_handler,
447 NULL,
448 },
449 {
450 { "locate-pointer-key",
451 SCHEMA_MUTTER,
452 META_PREF_KEYBINDINGS,
453 },
454 locate_pointer_key_handler,
455 NULL,
456 },
457 { { NULL, 0, 0 }, NULL },
458 };
459
460 static MetaStringArrayPreference preferences_string_array[] =
461 {
462 {
463 { KEY_WORKSPACE_NAMES,
464 SCHEMA_GENERAL,
465 META_PREF_WORKSPACE_NAMES,
466 },
467 NULL,
468 &workspace_names,
469 },
470 {
471 { KEY_XKB_OPTIONS,
472 SCHEMA_INPUT_SOURCES,
473 META_PREF_KEYBINDINGS,
474 },
475 iso_next_group_handler,
476 NULL,
477 },
478 { { NULL, 0, 0 }, NULL },
479 };
480
481 static MetaIntPreference preferences_int[] =
482 {
483 {
484 { KEY_NUM_WORKSPACES,
485 SCHEMA_GENERAL,
486 META_PREF_NUM_WORKSPACES,
487 },
488 &num_workspaces
489 },
490 {
491 { "auto-raise-delay",
492 SCHEMA_GENERAL,
493 META_PREF_AUTO_RAISE_DELAY,
494 },
495 &auto_raise_delay
496 },
497 {
498 { "draggable-border-width",
499 SCHEMA_MUTTER,
500 META_PREF_DRAGGABLE_BORDER_WIDTH,
501 },
502 &draggable_border_width
503 },
504 {
505 { "drag-threshold",
506 SCHEMA_MOUSE,
507 META_PREF_DRAG_THRESHOLD,
508 },
509 &drag_threshold
510 },
511 {
512 { "cursor-size",
513 SCHEMA_INTERFACE,
514 META_PREF_CURSOR_SIZE,
515 },
516 &cursor_size
517 },
518 { { NULL, 0, 0 }, NULL },
519 };
520
521 static MetaUintPreference preferences_uint[] =
522 {
523 {
524 { "check-alive-timeout",
525 SCHEMA_MUTTER,
526 META_PREF_CHECK_ALIVE_TIMEOUT,
527 },
528 &check_alive_timeout,
529 },
530 { { NULL, 0, 0 }, NULL },
531 };
532
533 static void
handle_preference_init_enum(void)534 handle_preference_init_enum (void)
535 {
536 MetaEnumPreference *cursor = preferences_enum;
537
538 while (cursor->base.key != NULL)
539 {
540 if (cursor->target==NULL)
541 continue;
542
543 *((gint *) cursor->target) =
544 g_settings_get_enum (SETTINGS (cursor->base.schema), cursor->base.key);
545
546 ++cursor;
547 }
548 }
549
550 static void
handle_preference_init_bool(void)551 handle_preference_init_bool (void)
552 {
553 MetaBoolPreference *cursor = preferences_bool;
554
555 while (cursor->base.key != NULL)
556 {
557 if (cursor->target!=NULL)
558 *cursor->target =
559 g_settings_get_boolean (SETTINGS (cursor->base.schema),
560 cursor->base.key);
561
562 ++cursor;
563 }
564
565 maybe_give_disable_workarounds_warning ();
566 }
567
568 static void
handle_preference_init_string(void)569 handle_preference_init_string (void)
570 {
571 MetaStringPreference *cursor = preferences_string;
572
573 while (cursor->base.key != NULL)
574 {
575 char *value;
576
577 /* Complex keys have a mapping function to check validity */
578 if (cursor->handler)
579 {
580 if (cursor->target)
581 meta_bug ("%s has both a target and a handler", cursor->base.key);
582
583 g_settings_get_mapped (SETTINGS (cursor->base.schema),
584 cursor->base.key, cursor->handler, NULL);
585 }
586 else
587 {
588 if (!cursor->target)
589 meta_bug ("%s must have handler or target", cursor->base.key);
590
591 g_free (*(cursor->target));
592
593 value = g_settings_get_string (SETTINGS (cursor->base.schema),
594 cursor->base.key);
595
596 *(cursor->target) = value;
597 }
598
599 ++cursor;
600 }
601 }
602
603 static void
handle_preference_init_string_array(void)604 handle_preference_init_string_array (void)
605 {
606 MetaStringArrayPreference *cursor = preferences_string_array;
607
608 while (cursor->base.key != NULL)
609 {
610 char **value;
611
612 /* Complex keys have a mapping function to check validity */
613 if (cursor->handler)
614 {
615 if (cursor->target)
616 meta_bug ("%s has both a target and a handler", cursor->base.key);
617
618 g_settings_get_mapped (SETTINGS (cursor->base.schema),
619 cursor->base.key, cursor->handler, NULL);
620 }
621 else
622 {
623 if (!cursor->target)
624 meta_bug ("%s must have handler or target", cursor->base.key);
625
626 if (*(cursor->target))
627 g_strfreev (*(cursor->target));
628
629 value = g_settings_get_strv (SETTINGS (cursor->base.schema),
630 cursor->base.key);
631
632 *(cursor->target) = value;
633 }
634
635 ++cursor;
636 }
637 }
638
639 static void
handle_preference_init_int(void)640 handle_preference_init_int (void)
641 {
642 MetaIntPreference *cursor = preferences_int;
643
644
645 while (cursor->base.key != NULL)
646 {
647 if (cursor->target)
648 *cursor->target = g_settings_get_int (SETTINGS (cursor->base.schema),
649 cursor->base.key);
650
651 ++cursor;
652 }
653 }
654
655 static void
handle_preference_init_uint(void)656 handle_preference_init_uint (void)
657 {
658 MetaUintPreference *cursor = preferences_uint;
659
660 while (cursor->base.key != NULL)
661 {
662 if (cursor->target)
663 *cursor->target = g_settings_get_uint (SETTINGS (cursor->base.schema),
664 cursor->base.key);
665
666 ++cursor;
667 }
668 }
669
670 static void
handle_preference_update_enum(GSettings * settings,gchar * key)671 handle_preference_update_enum (GSettings *settings,
672 gchar *key)
673 {
674 MetaEnumPreference *cursor = preferences_enum;
675 gint old_value;
676
677 while (cursor->base.key != NULL && strcmp (key, cursor->base.key) != 0)
678 ++cursor;
679
680 if (cursor->base.key == NULL)
681 /* Didn't recognise that key. */
682 return;
683
684 /* We need to know whether the value changes, so
685 * store the current value away.
686 */
687
688 old_value = * ((gint *)cursor->target);
689 *((gint *)cursor->target) =
690 g_settings_get_enum (SETTINGS (cursor->base.schema), key);
691
692 /* Did it change? If so, tell the listeners about it. */
693 if (old_value != *((gint *)cursor->target))
694 queue_changed (cursor->base.pref);
695 }
696
697 static void
handle_preference_update_bool(GSettings * settings,gchar * key)698 handle_preference_update_bool (GSettings *settings,
699 gchar *key)
700 {
701 MetaBoolPreference *cursor = preferences_bool;
702 gboolean old_value;
703
704 while (cursor->base.key != NULL && strcmp (key, cursor->base.key) != 0)
705 ++cursor;
706
707 if (cursor->base.key == NULL || cursor->target == NULL)
708 /* Unknown key or no work for us to do. */
709 return;
710
711 /* We need to know whether the value changes, so
712 * store the current value away.
713 */
714 old_value = *((gboolean *) cursor->target);
715
716 /* Now look it up... */
717 *((gboolean *) cursor->target) =
718 g_settings_get_boolean (SETTINGS (cursor->base.schema), key);
719
720 /* Did it change? If so, tell the listeners about it. */
721 if (old_value != *((gboolean *)cursor->target))
722 queue_changed (cursor->base.pref);
723
724 if (cursor->base.pref==META_PREF_DISABLE_WORKAROUNDS)
725 maybe_give_disable_workarounds_warning ();
726 }
727
728 static void
handle_preference_update_string(GSettings * settings,gchar * key)729 handle_preference_update_string (GSettings *settings,
730 gchar *key)
731 {
732 MetaStringPreference *cursor = preferences_string;
733 char *value;
734 gboolean inform_listeners = FALSE;
735
736 while (cursor->base.key != NULL && strcmp (key, cursor->base.key) != 0)
737 ++cursor;
738
739 if (cursor->base.key==NULL)
740 /* Didn't recognise that key. */
741 return;
742
743 /* Complex keys have a mapping function to check validity */
744 if (cursor->handler)
745 {
746 if (cursor->target)
747 meta_bug ("%s has both a target and a handler", cursor->base.key);
748
749 g_settings_get_mapped (SETTINGS (cursor->base.schema),
750 cursor->base.key, cursor->handler, NULL);
751 }
752 else
753 {
754 if (!cursor->target)
755 meta_bug ("%s must have handler or target", cursor->base.key);
756
757 value = g_settings_get_string (SETTINGS (cursor->base.schema),
758 cursor->base.key);
759
760 inform_listeners = (g_strcmp0 (value, *(cursor->target)) != 0);
761
762 g_free(*(cursor->target));
763
764 *(cursor->target) = value;
765 }
766
767 if (inform_listeners)
768 queue_changed (cursor->base.pref);
769 }
770
771 static void
handle_preference_update_string_array(GSettings * settings,gchar * key)772 handle_preference_update_string_array (GSettings *settings,
773 gchar *key)
774 {
775 MetaStringArrayPreference *cursor = preferences_string_array;
776 gboolean inform_listeners = FALSE;
777
778 while (cursor->base.key != NULL && strcmp (key, cursor->base.key) != 0)
779 ++cursor;
780
781 if (cursor->base.key==NULL)
782 /* Didn't recognise that key. */
783 return;
784
785 /* Complex keys have a mapping function to check validity */
786 if (cursor->handler)
787 {
788 if (cursor->target)
789 meta_bug ("%s has both a target and a handler", cursor->base.key);
790
791 g_settings_get_mapped (SETTINGS (cursor->base.schema),
792 cursor->base.key, cursor->handler, NULL);
793 }
794 else
795 {
796 char **values, **previous;
797 int n_values, n_previous, i;
798
799 if (!cursor->target)
800 meta_bug ("%s must have handler or target", cursor->base.key);
801
802 values = g_settings_get_strv (SETTINGS (cursor->base.schema),
803 cursor->base.key);
804 n_values = g_strv_length (values);
805 previous = *(cursor->target);
806 n_previous = previous ? g_strv_length (previous) : 0;
807
808 inform_listeners = n_previous != n_values;
809 for (i = 0; i < n_values && !inform_listeners; i++)
810 inform_listeners = g_strcmp0 (values[i], previous[i]) != 0;
811
812 if (*(cursor->target))
813 g_strfreev (*(cursor->target));
814 *(cursor->target) = values;
815 }
816
817 if (inform_listeners)
818 queue_changed (cursor->base.pref);
819 }
820
821 static void
handle_preference_update_int(GSettings * settings,gchar * key)822 handle_preference_update_int (GSettings *settings,
823 gchar *key)
824 {
825 MetaIntPreference *cursor = preferences_int;
826 gint new_value;
827
828 while (cursor->base.key != NULL && strcmp (key, cursor->base.key) != 0)
829 ++cursor;
830
831 if (cursor->base.key == NULL || cursor->target == NULL)
832 /* Unknown key or no work for us to do. */
833 return;
834
835 new_value = g_settings_get_int (SETTINGS (cursor->base.schema), key);
836
837 /* Did it change? If so, tell the listeners about it. */
838 if (*cursor->target != new_value)
839 {
840 *cursor->target = new_value;
841 queue_changed (cursor->base.pref);
842 }
843 }
844
845 static void
handle_preference_update_uint(GSettings * settings,char * key)846 handle_preference_update_uint (GSettings *settings,
847 char *key)
848 {
849 MetaUintPreference *cursor = preferences_uint;
850 unsigned int new_value;
851
852 while (cursor->base.key && strcmp (key, cursor->base.key) != 0)
853 ++cursor;
854
855 if (!cursor->base.key || !cursor->target)
856 return;
857
858 new_value = g_settings_get_uint (SETTINGS (cursor->base.schema), key);
859
860 if (*cursor->target != new_value)
861 {
862 *cursor->target = new_value;
863 queue_changed (cursor->base.pref);
864 }
865 }
866
867
868 /****************************************************************************/
869 /* Listeners. */
870 /****************************************************************************/
871
872 /**
873 * meta_prefs_add_listener: (skip)
874 * @func: a #MetaPrefsChangedFunc
875 * @user_data: data passed to the function
876 *
877 */
878 void
meta_prefs_add_listener(MetaPrefsChangedFunc func,gpointer user_data)879 meta_prefs_add_listener (MetaPrefsChangedFunc func,
880 gpointer user_data)
881 {
882 MetaPrefsListener *l;
883
884 l = g_new (MetaPrefsListener, 1);
885 l->func = func;
886 l->data = user_data;
887
888 listeners = g_list_prepend (listeners, l);
889 }
890
891 /**
892 * meta_prefs_remove_listener: (skip)
893 * @func: a #MetaPrefsChangedFunc
894 * @user_data: data passed to the function
895 *
896 */
897 void
meta_prefs_remove_listener(MetaPrefsChangedFunc func,gpointer user_data)898 meta_prefs_remove_listener (MetaPrefsChangedFunc func,
899 gpointer user_data)
900 {
901 GList *tmp;
902
903 tmp = listeners;
904 while (tmp != NULL)
905 {
906 MetaPrefsListener *l = tmp->data;
907
908 if (l->func == func &&
909 l->data == user_data)
910 {
911 g_free (l);
912 listeners = g_list_delete_link (listeners, tmp);
913
914 return;
915 }
916
917 tmp = tmp->next;
918 }
919 }
920
921 static void
emit_changed(MetaPreference pref)922 emit_changed (MetaPreference pref)
923 {
924 GList *tmp;
925 GList *copy;
926
927 meta_topic (META_DEBUG_PREFS, "Notifying listeners that pref %s changed",
928 meta_preference_to_string (pref));
929
930 copy = g_list_copy (listeners);
931
932 tmp = copy;
933
934 while (tmp != NULL)
935 {
936 MetaPrefsListener *l = tmp->data;
937
938 (* l->func) (pref, l->data);
939
940 tmp = tmp->next;
941 }
942
943 g_list_free (copy);
944 }
945
946 static gboolean
changed_idle_handler(gpointer data)947 changed_idle_handler (gpointer data)
948 {
949 GList *tmp;
950 GList *copy;
951
952 changed_idle = 0;
953
954 copy = g_list_copy (changes); /* reentrancy paranoia */
955
956 g_list_free (changes);
957 changes = NULL;
958
959 tmp = copy;
960 while (tmp != NULL)
961 {
962 MetaPreference pref = GPOINTER_TO_INT (tmp->data);
963
964 emit_changed (pref);
965
966 tmp = tmp->next;
967 }
968
969 g_list_free (copy);
970
971 return FALSE;
972 }
973
974 static void
queue_changed(MetaPreference pref)975 queue_changed (MetaPreference pref)
976 {
977 meta_topic (META_DEBUG_PREFS, "Queueing change of pref %s",
978 meta_preference_to_string (pref));
979
980 if (g_list_find (changes, GINT_TO_POINTER (pref)) == NULL)
981 changes = g_list_prepend (changes, GINT_TO_POINTER (pref));
982 else
983 meta_topic (META_DEBUG_PREFS, "Change of pref %s was already pending",
984 meta_preference_to_string (pref));
985
986 if (changed_idle == 0)
987 {
988 changed_idle = g_idle_add_full (META_PRIORITY_PREFS_NOTIFY,
989 changed_idle_handler, NULL, NULL);
990 g_source_set_name_by_id (changed_idle, "[mutter] changed_idle_handler");
991 }
992 }
993
994
995 /****************************************************************************/
996 /* Initialisation. */
997 /****************************************************************************/
998
999 void
meta_prefs_init(void)1000 meta_prefs_init (void)
1001 {
1002 GSettings *settings;
1003
1004 settings_schemas = g_hash_table_new_full (g_str_hash, g_str_equal,
1005 g_free, g_object_unref);
1006
1007 settings = g_settings_new (SCHEMA_GENERAL);
1008 g_signal_connect (settings, "changed", G_CALLBACK (settings_changed), NULL);
1009 g_hash_table_insert (settings_schemas, g_strdup (SCHEMA_GENERAL), settings);
1010
1011 settings = g_settings_new (SCHEMA_MUTTER);
1012 g_signal_connect (settings, "changed", G_CALLBACK (settings_changed), NULL);
1013 g_hash_table_insert (settings_schemas, g_strdup (SCHEMA_MUTTER), settings);
1014
1015 settings = g_settings_new (SCHEMA_MOUSE);
1016 g_signal_connect (settings, "changed", G_CALLBACK (settings_changed), NULL);
1017 g_hash_table_insert (settings_schemas, g_strdup (SCHEMA_MOUSE), settings);
1018
1019 /* Individual keys we watch outside of our schemas */
1020 settings = g_settings_new (SCHEMA_INTERFACE);
1021 g_signal_connect (settings, "changed::" KEY_GNOME_ACCESSIBILITY,
1022 G_CALLBACK (settings_changed), NULL);
1023 g_signal_connect (settings, "changed::" KEY_GNOME_ANIMATIONS,
1024 G_CALLBACK (settings_changed), NULL);
1025 g_signal_connect (settings, "changed::" KEY_GNOME_CURSOR_THEME,
1026 G_CALLBACK (settings_changed), NULL);
1027 g_signal_connect (settings, "changed::" KEY_GNOME_CURSOR_SIZE,
1028 G_CALLBACK (settings_changed), NULL);
1029 g_signal_connect (settings, "changed::" KEY_LOCATE_POINTER,
1030 G_CALLBACK (settings_changed), NULL);
1031 g_hash_table_insert (settings_schemas, g_strdup (SCHEMA_INTERFACE), settings);
1032
1033 settings = g_settings_new (SCHEMA_INPUT_SOURCES);
1034 g_signal_connect (settings, "changed::" KEY_XKB_OPTIONS,
1035 G_CALLBACK (settings_changed), NULL);
1036 g_hash_table_insert (settings_schemas, g_strdup (SCHEMA_INPUT_SOURCES), settings);
1037
1038 /* Pick up initial values. */
1039
1040 handle_preference_init_enum ();
1041 handle_preference_init_bool ();
1042 handle_preference_init_string ();
1043 handle_preference_init_string_array ();
1044 handle_preference_init_int ();
1045 handle_preference_init_uint ();
1046
1047 init_bindings ();
1048 }
1049
1050 static gboolean
find_pref(void * prefs,size_t pref_size,const char * search_key,MetaBasePreference ** pref)1051 find_pref (void *prefs,
1052 size_t pref_size,
1053 const char *search_key,
1054 MetaBasePreference **pref)
1055 {
1056 void *p = prefs;
1057
1058 while (TRUE)
1059 {
1060 char **key = p;
1061 if (*key == NULL)
1062 break;
1063
1064 if (strcmp (*key, search_key) == 0)
1065 {
1066 *pref = p;
1067 return TRUE;
1068 }
1069
1070 p = (guchar *)p + pref_size;
1071 }
1072
1073 return FALSE;
1074 }
1075
1076
1077 /****************************************************************************/
1078 /* Updates. */
1079 /****************************************************************************/
1080
1081
1082 static void
settings_changed(GSettings * settings,gchar * key,gpointer data)1083 settings_changed (GSettings *settings,
1084 gchar *key,
1085 gpointer data)
1086 {
1087 GVariant *value;
1088 const GVariantType *type;
1089 MetaEnumPreference *cursor;
1090 gboolean found_enum;
1091
1092 value = g_settings_get_value (settings, key);
1093 type = g_variant_get_type (value);
1094
1095 if (g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN))
1096 handle_preference_update_bool (settings, key);
1097 else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32))
1098 handle_preference_update_int (settings, key);
1099 else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT32))
1100 handle_preference_update_uint (settings, key);
1101 else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING_ARRAY))
1102 handle_preference_update_string_array (settings, key);
1103 else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
1104 {
1105 cursor = preferences_enum;
1106 found_enum = FALSE;
1107
1108 while (cursor->base.key != NULL)
1109 {
1110
1111 if (strcmp (key, cursor->base.key) == 0)
1112 found_enum = TRUE;
1113
1114 cursor++;
1115 }
1116
1117 if (found_enum)
1118 handle_preference_update_enum (settings, key);
1119 else
1120 handle_preference_update_string (settings, key);
1121 }
1122 else
1123 {
1124 /* Unknown preference type. This quite likely simply isn't
1125 * a preference we track changes to. */
1126 }
1127
1128 g_variant_unref (value);
1129 }
1130
1131 static void
bindings_changed(GSettings * settings,gchar * key,gpointer data)1132 bindings_changed (GSettings *settings,
1133 gchar *key,
1134 gpointer data)
1135 {
1136 gchar **strokes;
1137 strokes = g_settings_get_strv (settings, key);
1138
1139 if (update_key_binding (key, strokes))
1140 queue_changed (META_PREF_KEYBINDINGS);
1141
1142 g_strfreev (strokes);
1143 }
1144
1145 /**
1146 * maybe_give_disable_workaround_warning:
1147 *
1148 * Special case: give a warning the first time disable_workarounds
1149 * is turned on.
1150 */
1151 static void
maybe_give_disable_workarounds_warning(void)1152 maybe_give_disable_workarounds_warning (void)
1153 {
1154 static gboolean first_disable = TRUE;
1155
1156 if (first_disable && disable_workarounds)
1157 {
1158 first_disable = FALSE;
1159
1160 meta_warning ("Workarounds for broken applications disabled. "
1161 "Some applications may not behave properly.");
1162 }
1163 }
1164
1165 MetaVirtualModifier
meta_prefs_get_mouse_button_mods(void)1166 meta_prefs_get_mouse_button_mods (void)
1167 {
1168 return mouse_button_mods;
1169 }
1170
1171 GDesktopFocusMode
meta_prefs_get_focus_mode(void)1172 meta_prefs_get_focus_mode (void)
1173 {
1174 return focus_mode;
1175 }
1176
1177 GDesktopFocusNewWindows
meta_prefs_get_focus_new_windows(void)1178 meta_prefs_get_focus_new_windows (void)
1179 {
1180 return focus_new_windows;
1181 }
1182
1183 gboolean
meta_prefs_get_center_new_windows(void)1184 meta_prefs_get_center_new_windows (void)
1185 {
1186 return center_new_windows;
1187 }
1188
1189 gboolean
meta_prefs_get_attach_modal_dialogs(void)1190 meta_prefs_get_attach_modal_dialogs (void)
1191 {
1192 return attach_modal_dialogs;
1193 }
1194
1195 gboolean
meta_prefs_get_raise_on_click(void)1196 meta_prefs_get_raise_on_click (void)
1197 {
1198 return raise_on_click;
1199 }
1200
1201 gboolean
meta_prefs_get_show_fallback_app_menu(void)1202 meta_prefs_get_show_fallback_app_menu (void)
1203 {
1204 return show_fallback_app_menu;
1205 }
1206
1207 void
meta_prefs_set_show_fallback_app_menu(gboolean whether)1208 meta_prefs_set_show_fallback_app_menu (gboolean whether)
1209 {
1210 gboolean changed = FALSE;
1211
1212 changed = (show_fallback_app_menu == !whether);
1213
1214 show_fallback_app_menu = whether;
1215
1216 if (changed)
1217 queue_changed (META_PREF_BUTTON_LAYOUT);
1218 }
1219
1220 const char*
meta_prefs_get_cursor_theme(void)1221 meta_prefs_get_cursor_theme (void)
1222 {
1223 return cursor_theme;
1224 }
1225
1226 int
meta_prefs_get_cursor_size(void)1227 meta_prefs_get_cursor_size (void)
1228 {
1229 return cursor_size;
1230 }
1231
1232
1233 /****************************************************************************/
1234 /* Handlers for string preferences. */
1235 /****************************************************************************/
1236
1237 static gboolean
titlebar_handler(GVariant * value,gpointer * result,gpointer data)1238 titlebar_handler (GVariant *value,
1239 gpointer *result,
1240 gpointer data)
1241 {
1242 PangoFontDescription *desc;
1243 const gchar *string_value;
1244
1245 *result = NULL; /* ignored */
1246 string_value = g_variant_get_string (value, NULL);
1247 desc = pango_font_description_from_string (string_value);
1248
1249 if (desc == NULL)
1250 {
1251 meta_warning ("Could not parse font description "
1252 "\"%s\" from GSettings key %s",
1253 string_value ? string_value : "(null)",
1254 KEY_TITLEBAR_FONT);
1255 return FALSE;
1256 }
1257
1258 /* Is the new description the same as the old? */
1259 if (titlebar_font &&
1260 pango_font_description_equal (desc, titlebar_font))
1261 {
1262 pango_font_description_free (desc);
1263 }
1264 else
1265 {
1266 if (titlebar_font)
1267 pango_font_description_free (titlebar_font);
1268
1269 titlebar_font = desc;
1270 queue_changed (META_PREF_TITLEBAR_FONT);
1271 }
1272
1273 return TRUE;
1274 }
1275
1276 static gboolean
mouse_button_mods_handler(GVariant * value,gpointer * result,gpointer data)1277 mouse_button_mods_handler (GVariant *value,
1278 gpointer *result,
1279 gpointer data)
1280 {
1281 MetaVirtualModifier mods;
1282 const gchar *string_value;
1283
1284 *result = NULL; /* ignored */
1285 string_value = g_variant_get_string (value, NULL);
1286
1287 if (!string_value || !meta_parse_modifier (string_value, &mods))
1288 {
1289 meta_topic (META_DEBUG_KEYBINDINGS,
1290 "Failed to parse new GSettings value");
1291
1292 meta_warning ("\"%s\" found in configuration database is "
1293 "not a valid value for mouse button modifier",
1294 string_value);
1295
1296 return FALSE;
1297 }
1298
1299 meta_topic (META_DEBUG_KEYBINDINGS,
1300 "Mouse button modifier has new GSettings value \"%s\"",
1301 string_value);
1302
1303 if (mods != mouse_button_mods)
1304 {
1305 mouse_button_mods = mods;
1306 queue_changed (META_PREF_MOUSE_BUTTON_MODS);
1307 }
1308
1309 return TRUE;
1310 }
1311
1312 static gboolean
button_layout_equal(const MetaButtonLayout * a,const MetaButtonLayout * b)1313 button_layout_equal (const MetaButtonLayout *a,
1314 const MetaButtonLayout *b)
1315 {
1316 int i;
1317
1318 i = 0;
1319 while (i < MAX_BUTTONS_PER_CORNER)
1320 {
1321 if (a->left_buttons[i] != b->left_buttons[i])
1322 return FALSE;
1323 if (a->right_buttons[i] != b->right_buttons[i])
1324 return FALSE;
1325 if (a->left_buttons_has_spacer[i] != b->left_buttons_has_spacer[i])
1326 return FALSE;
1327 if (a->right_buttons_has_spacer[i] != b->right_buttons_has_spacer[i])
1328 return FALSE;
1329 ++i;
1330 }
1331
1332 return TRUE;
1333 }
1334
1335 /*
1336 * This conversion cannot be handled by GSettings since
1337 * several values are stored in the same key (as a string).
1338 */
1339 static MetaButtonFunction
button_function_from_string(const char * str)1340 button_function_from_string (const char *str)
1341 {
1342 if (strcmp (str, "menu") == 0)
1343 return META_BUTTON_FUNCTION_MENU;
1344 else if (strcmp (str, "minimize") == 0)
1345 return META_BUTTON_FUNCTION_MINIMIZE;
1346 else if (strcmp (str, "maximize") == 0)
1347 return META_BUTTON_FUNCTION_MAXIMIZE;
1348 else if (strcmp (str, "close") == 0)
1349 return META_BUTTON_FUNCTION_CLOSE;
1350 else
1351 /* don't know; give up */
1352 return META_BUTTON_FUNCTION_LAST;
1353 }
1354
1355 static gboolean
button_layout_handler(GVariant * value,gpointer * result,gpointer data)1356 button_layout_handler (GVariant *value,
1357 gpointer *result,
1358 gpointer data)
1359 {
1360 MetaButtonLayout new_layout;
1361 const gchar *string_value;
1362 char **sides = NULL;
1363 int i;
1364
1365 /* We need to ignore unknown button functions, for
1366 * compat with future versions
1367 */
1368
1369 *result = NULL; /* ignored */
1370 string_value = g_variant_get_string (value, NULL);
1371
1372 if (string_value)
1373 sides = g_strsplit (string_value, ":", 2);
1374
1375 i = 0;
1376 if (sides != NULL && sides[0] != NULL)
1377 {
1378 char **buttons;
1379 int b;
1380 gboolean used[META_BUTTON_FUNCTION_LAST];
1381
1382 while (i < META_BUTTON_FUNCTION_LAST)
1383 {
1384 used[i] = FALSE;
1385 new_layout.left_buttons_has_spacer[i] = FALSE;
1386 ++i;
1387 }
1388
1389 buttons = g_strsplit (sides[0], ",", -1);
1390 i = 0;
1391 b = 0;
1392 while (buttons[b] != NULL)
1393 {
1394 MetaButtonFunction f = button_function_from_string (buttons[b]);
1395 if (i > 0 && strcmp("spacer", buttons[b]) == 0)
1396 {
1397 new_layout.left_buttons_has_spacer[i-1] = TRUE;
1398 }
1399 else
1400 {
1401 if (f != META_BUTTON_FUNCTION_LAST && !used[f])
1402 {
1403 new_layout.left_buttons[i] = f;
1404 used[f] = TRUE;
1405 ++i;
1406 }
1407 else
1408 {
1409 meta_topic (META_DEBUG_PREFS,
1410 "Ignoring unknown or already-used button name \"%s\"",
1411 buttons[b]);
1412 }
1413 }
1414
1415 ++b;
1416 }
1417
1418 g_strfreev (buttons);
1419 }
1420
1421 for (; i < MAX_BUTTONS_PER_CORNER; i++)
1422 {
1423 new_layout.left_buttons[i] = META_BUTTON_FUNCTION_LAST;
1424 new_layout.left_buttons_has_spacer[i] = FALSE;
1425 }
1426
1427 i = 0;
1428 if (sides != NULL && sides[0] != NULL && sides[1] != NULL)
1429 {
1430 char **buttons;
1431 int b;
1432 gboolean used[META_BUTTON_FUNCTION_LAST];
1433
1434 while (i < META_BUTTON_FUNCTION_LAST)
1435 {
1436 used[i] = FALSE;
1437 new_layout.right_buttons_has_spacer[i] = FALSE;
1438 ++i;
1439 }
1440
1441 buttons = g_strsplit (sides[1], ",", -1);
1442 i = 0;
1443 b = 0;
1444 while (buttons[b] != NULL)
1445 {
1446 MetaButtonFunction f = button_function_from_string (buttons[b]);
1447 if (i > 0 && strcmp("spacer", buttons[b]) == 0)
1448 {
1449 new_layout.right_buttons_has_spacer[i-1] = TRUE;
1450 }
1451 else
1452 {
1453 if (f != META_BUTTON_FUNCTION_LAST && !used[f])
1454 {
1455 new_layout.right_buttons[i] = f;
1456 used[f] = TRUE;
1457 ++i;
1458 }
1459 else
1460 {
1461 meta_topic (META_DEBUG_PREFS,
1462 "Ignoring unknown or already-used button name \"%s\"",
1463 buttons[b]);
1464 }
1465 }
1466
1467 ++b;
1468 }
1469
1470 g_strfreev (buttons);
1471 }
1472
1473 for (; i < MAX_BUTTONS_PER_CORNER; i++)
1474 {
1475 new_layout.right_buttons[i] = META_BUTTON_FUNCTION_LAST;
1476 new_layout.right_buttons_has_spacer[i] = FALSE;
1477 }
1478
1479 g_strfreev (sides);
1480
1481 /* Invert the button layout for RTL languages */
1482 if (meta_get_locale_direction() == META_LOCALE_DIRECTION_RTL)
1483 {
1484 MetaButtonLayout rtl_layout;
1485 int j;
1486
1487 for (i = 0; new_layout.left_buttons[i] != META_BUTTON_FUNCTION_LAST; i++);
1488 for (j = 0; j < i; j++)
1489 {
1490 rtl_layout.right_buttons[j] = new_layout.left_buttons[i - j - 1];
1491 if (j == 0)
1492 rtl_layout.right_buttons_has_spacer[i - 1] = new_layout.left_buttons_has_spacer[i - j - 1];
1493 else
1494 rtl_layout.right_buttons_has_spacer[j - 1] = new_layout.left_buttons_has_spacer[i - j - 1];
1495 }
1496 for (; j < MAX_BUTTONS_PER_CORNER; j++)
1497 {
1498 rtl_layout.right_buttons[j] = META_BUTTON_FUNCTION_LAST;
1499 rtl_layout.right_buttons_has_spacer[j] = FALSE;
1500 }
1501
1502 for (i = 0; new_layout.right_buttons[i] != META_BUTTON_FUNCTION_LAST; i++);
1503 for (j = 0; j < i; j++)
1504 {
1505 rtl_layout.left_buttons[j] = new_layout.right_buttons[i - j - 1];
1506 if (j == 0)
1507 rtl_layout.left_buttons_has_spacer[i - 1] = new_layout.right_buttons_has_spacer[i - j - 1];
1508 else
1509 rtl_layout.left_buttons_has_spacer[j - 1] = new_layout.right_buttons_has_spacer[i - j - 1];
1510 }
1511 for (; j < MAX_BUTTONS_PER_CORNER; j++)
1512 {
1513 rtl_layout.left_buttons[j] = META_BUTTON_FUNCTION_LAST;
1514 rtl_layout.left_buttons_has_spacer[j] = FALSE;
1515 }
1516
1517 new_layout = rtl_layout;
1518 }
1519
1520 if (!button_layout_equal (&button_layout, &new_layout))
1521 {
1522 button_layout = new_layout;
1523 emit_changed (META_PREF_BUTTON_LAYOUT);
1524 }
1525
1526 return TRUE;
1527 }
1528
1529 static gboolean
overlay_key_handler(GVariant * value,gpointer * result,gpointer data)1530 overlay_key_handler (GVariant *value,
1531 gpointer *result,
1532 gpointer data)
1533 {
1534 MetaKeyCombo combo;
1535 const gchar *string_value;
1536
1537 *result = NULL; /* ignored */
1538 string_value = g_variant_get_string (value, NULL);
1539
1540 if (string_value && meta_parse_accelerator (string_value, &combo))
1541 ;
1542 else
1543 {
1544 meta_topic (META_DEBUG_KEYBINDINGS,
1545 "Failed to parse value for overlay-key");
1546 return FALSE;
1547 }
1548
1549 combo.modifiers = 0;
1550
1551 if (overlay_key_combo.keysym != combo.keysym ||
1552 overlay_key_combo.keycode != combo.keycode)
1553 {
1554 overlay_key_combo = combo;
1555 queue_changed (META_PREF_KEYBINDINGS);
1556 }
1557
1558 return TRUE;
1559 }
1560
1561 static gboolean
locate_pointer_key_handler(GVariant * value,gpointer * result,gpointer data)1562 locate_pointer_key_handler (GVariant *value,
1563 gpointer *result,
1564 gpointer data)
1565 {
1566 MetaKeyCombo combo;
1567 const gchar *string_value;
1568
1569 *result = NULL; /* ignored */
1570 string_value = g_variant_get_string (value, NULL);
1571
1572 if (!string_value || !meta_parse_accelerator (string_value, &combo))
1573 {
1574 meta_topic (META_DEBUG_KEYBINDINGS,
1575 "Failed to parse value for locate-pointer-key");
1576 return FALSE;
1577 }
1578
1579 combo.modifiers = 0;
1580
1581 if (locate_pointer_key_combo.keysym != combo.keysym ||
1582 locate_pointer_key_combo.keycode != combo.keycode)
1583 {
1584 locate_pointer_key_combo = combo;
1585 queue_changed (META_PREF_KEYBINDINGS);
1586 }
1587
1588 return TRUE;
1589 }
1590
1591 static gboolean
iso_next_group_handler(GVariant * value,gpointer * result,gpointer data)1592 iso_next_group_handler (GVariant *value,
1593 gpointer *result,
1594 gpointer data)
1595 {
1596 const char **xkb_options, **p;
1597 const char *option = NULL;
1598 gboolean changed = FALSE;
1599
1600 *result = NULL; /* ignored */
1601 xkb_options = g_variant_get_strv (value, NULL);
1602
1603 for (p = xkb_options; p && *p; ++p)
1604 if (g_str_has_prefix (*p, "grp:"))
1605 {
1606 option = (*p + 4);
1607 break;
1608 }
1609
1610 changed = (g_strcmp0 (option, iso_next_group_option) != 0);
1611
1612 if (changed)
1613 {
1614 g_free (iso_next_group_option);
1615 iso_next_group_option = g_strdup (option);
1616 queue_changed (META_PREF_KEYBINDINGS);
1617 }
1618
1619 g_free (xkb_options);
1620
1621 return TRUE;
1622 }
1623
1624 const PangoFontDescription*
meta_prefs_get_titlebar_font(void)1625 meta_prefs_get_titlebar_font (void)
1626 {
1627 if (use_system_font)
1628 return NULL;
1629 else
1630 return titlebar_font;
1631 }
1632
1633 int
meta_prefs_get_num_workspaces(void)1634 meta_prefs_get_num_workspaces (void)
1635 {
1636 return num_workspaces;
1637 }
1638
1639 gboolean
meta_prefs_get_dynamic_workspaces(void)1640 meta_prefs_get_dynamic_workspaces (void)
1641 {
1642 return dynamic_workspaces;
1643 }
1644
1645 gboolean
meta_prefs_get_disable_workarounds(void)1646 meta_prefs_get_disable_workarounds (void)
1647 {
1648 return disable_workarounds;
1649 }
1650
1651 #ifdef WITH_VERBOSE_MODE
1652 const char*
meta_preference_to_string(MetaPreference pref)1653 meta_preference_to_string (MetaPreference pref)
1654 {
1655 /* TODO: better handled via GLib enum nicknames */
1656 switch (pref)
1657 {
1658 case META_PREF_MOUSE_BUTTON_MODS:
1659 return "MOUSE_BUTTON_MODS";
1660
1661 case META_PREF_FOCUS_MODE:
1662 return "FOCUS_MODE";
1663
1664 case META_PREF_FOCUS_NEW_WINDOWS:
1665 return "FOCUS_NEW_WINDOWS";
1666
1667 case META_PREF_CENTER_NEW_WINDOWS:
1668 return "CENTER_NEW_WINDOWS";
1669
1670 case META_PREF_ATTACH_MODAL_DIALOGS:
1671 return "ATTACH_MODAL_DIALOGS";
1672
1673 case META_PREF_RAISE_ON_CLICK:
1674 return "RAISE_ON_CLICK";
1675
1676 case META_PREF_TITLEBAR_FONT:
1677 return "TITLEBAR_FONT";
1678
1679 case META_PREF_NUM_WORKSPACES:
1680 return "NUM_WORKSPACES";
1681
1682 case META_PREF_KEYBINDINGS:
1683 return "KEYBINDINGS";
1684
1685 case META_PREF_DISABLE_WORKAROUNDS:
1686 return "DISABLE_WORKAROUNDS";
1687
1688 case META_PREF_ACTION_DOUBLE_CLICK_TITLEBAR:
1689 return "ACTION_DOUBLE_CLICK_TITLEBAR";
1690
1691 case META_PREF_ACTION_MIDDLE_CLICK_TITLEBAR:
1692 return "ACTION_MIDDLE_CLICK_TITLEBAR";
1693
1694 case META_PREF_ACTION_RIGHT_CLICK_TITLEBAR:
1695 return "ACTION_RIGHT_CLICK_TITLEBAR";
1696
1697 case META_PREF_AUTO_RAISE:
1698 return "AUTO_RAISE";
1699
1700 case META_PREF_AUTO_RAISE_DELAY:
1701 return "AUTO_RAISE_DELAY";
1702
1703 case META_PREF_FOCUS_CHANGE_ON_POINTER_REST:
1704 return "FOCUS_CHANGE_ON_POINTER_REST";
1705
1706 case META_PREF_BUTTON_LAYOUT:
1707 return "BUTTON_LAYOUT";
1708
1709 case META_PREF_WORKSPACE_NAMES:
1710 return "WORKSPACE_NAMES";
1711
1712 case META_PREF_VISUAL_BELL:
1713 return "VISUAL_BELL";
1714
1715 case META_PREF_AUDIBLE_BELL:
1716 return "AUDIBLE_BELL";
1717
1718 case META_PREF_VISUAL_BELL_TYPE:
1719 return "VISUAL_BELL_TYPE";
1720
1721 case META_PREF_GNOME_ACCESSIBILITY:
1722 return "GNOME_ACCESSIBILTY";
1723
1724 case META_PREF_GNOME_ANIMATIONS:
1725 return "GNOME_ANIMATIONS";
1726
1727 case META_PREF_CURSOR_THEME:
1728 return "CURSOR_THEME";
1729
1730 case META_PREF_CURSOR_SIZE:
1731 return "CURSOR_SIZE";
1732
1733 case META_PREF_RESIZE_WITH_RIGHT_BUTTON:
1734 return "RESIZE_WITH_RIGHT_BUTTON";
1735
1736 case META_PREF_EDGE_TILING:
1737 return "EDGE_TILING";
1738
1739 case META_PREF_FORCE_FULLSCREEN:
1740 return "FORCE_FULLSCREEN";
1741
1742 case META_PREF_WORKSPACES_ONLY_ON_PRIMARY:
1743 return "WORKSPACES_ONLY_ON_PRIMARY";
1744
1745 case META_PREF_DRAGGABLE_BORDER_WIDTH:
1746 return "DRAGGABLE_BORDER_WIDTH";
1747
1748 case META_PREF_DRAG_THRESHOLD:
1749 return "DRAG_THRESHOLD";
1750
1751 case META_PREF_DYNAMIC_WORKSPACES:
1752 return "DYNAMIC_WORKSPACES";
1753
1754 case META_PREF_AUTO_MAXIMIZE:
1755 return "AUTO_MAXIMIZE";
1756
1757 case META_PREF_LOCATE_POINTER:
1758 return "LOCATE_POINTER";
1759
1760 case META_PREF_CHECK_ALIVE_TIMEOUT:
1761 return "CHECK_ALIVE_TIMEOUT";
1762 }
1763
1764 return "(unknown)";
1765 }
1766 #endif /* WITH_VERBOSE_MODE */
1767
1768 void
meta_prefs_set_num_workspaces(int n_workspaces)1769 meta_prefs_set_num_workspaces (int n_workspaces)
1770 {
1771 MetaBasePreference *pref;
1772
1773 if (find_pref (preferences_int, sizeof(MetaIntPreference),
1774 KEY_NUM_WORKSPACES, &pref))
1775 {
1776 g_settings_set_int (SETTINGS (pref->schema),
1777 KEY_NUM_WORKSPACES,
1778 n_workspaces);
1779 }
1780 }
1781
1782 static GHashTable *key_bindings;
1783
1784 static void
meta_key_pref_free(MetaKeyPref * pref)1785 meta_key_pref_free (MetaKeyPref *pref)
1786 {
1787 update_binding (pref, NULL);
1788
1789 g_free (pref->name);
1790 g_object_unref (pref->settings);
1791
1792 g_free (pref);
1793 }
1794
1795
1796 static void
init_bindings(void)1797 init_bindings (void)
1798 {
1799 MetaKeyPref *pref;
1800
1801 key_bindings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
1802 (GDestroyNotify)meta_key_pref_free);
1803
1804 pref = g_new0 (MetaKeyPref, 1);
1805 pref->name = g_strdup ("overlay-key");
1806 pref->action = META_KEYBINDING_ACTION_OVERLAY_KEY;
1807 pref->combos = g_slist_prepend (pref->combos, &overlay_key_combo);
1808 pref->builtin = 1;
1809
1810 g_hash_table_insert (key_bindings, g_strdup (pref->name), pref);
1811
1812 pref = g_new0 (MetaKeyPref, 1);
1813 pref->name = g_strdup ("locate-pointer-key");
1814 pref->action = META_KEYBINDING_ACTION_LOCATE_POINTER_KEY;
1815 pref->combos = g_slist_prepend (pref->combos, &locate_pointer_key_combo);
1816 pref->builtin = 1;
1817
1818 g_hash_table_insert (key_bindings, g_strdup (pref->name), pref);
1819 }
1820
1821 static gboolean
update_binding(MetaKeyPref * binding,gchar ** strokes)1822 update_binding (MetaKeyPref *binding,
1823 gchar **strokes)
1824 {
1825 GSList *old_combos, *a, *b;
1826 gboolean changed;
1827 int i;
1828
1829 meta_topic (META_DEBUG_KEYBINDINGS,
1830 "Binding \"%s\" has new GSettings value",
1831 binding->name);
1832
1833 old_combos = binding->combos;
1834 binding->combos = NULL;
1835
1836 for (i = 0; strokes && strokes[i]; i++)
1837 {
1838 MetaKeyCombo *combo;
1839
1840 combo = g_malloc0 (sizeof (MetaKeyCombo));
1841
1842 if (!meta_parse_accelerator (strokes[i], combo))
1843 {
1844 meta_topic (META_DEBUG_KEYBINDINGS,
1845 "Failed to parse new GSettings value");
1846 meta_warning ("\"%s\" found in configuration database is not a valid value for keybinding \"%s\"",
1847 strokes[i], binding->name);
1848
1849 g_free (combo);
1850
1851 /* Value is kept and will thus be removed next time we save the key.
1852 * Changing the key in response to a modification could lead to cyclic calls. */
1853 continue;
1854 }
1855
1856 binding->combos = g_slist_prepend (binding->combos, combo);
1857 }
1858
1859 binding->combos = g_slist_reverse (binding->combos);
1860
1861 a = old_combos;
1862 b = binding->combos;
1863 while (TRUE)
1864 {
1865 if ((!a && b) || (a && !b))
1866 {
1867 changed = TRUE;
1868 break;
1869 }
1870 else if (!a && !b)
1871 {
1872 changed = FALSE;
1873 break;
1874 }
1875 else if (memcmp (a->data, b->data, sizeof (MetaKeyCombo)) != 0)
1876 {
1877 changed = TRUE;
1878 break;
1879 }
1880 else
1881 {
1882 a = a->next;
1883 b = b->next;
1884 }
1885 }
1886
1887 g_slist_free_full (old_combos, g_free);
1888
1889 return changed;
1890 }
1891
1892 static gboolean
update_key_binding(const char * key,gchar ** strokes)1893 update_key_binding (const char *key,
1894 gchar **strokes)
1895 {
1896 MetaKeyPref *pref = g_hash_table_lookup (key_bindings, key);
1897
1898 if (pref)
1899 return update_binding (pref, strokes);
1900 else
1901 return FALSE;
1902 }
1903
1904 const char*
meta_prefs_get_workspace_name(int i)1905 meta_prefs_get_workspace_name (int i)
1906 {
1907 const char *name;
1908
1909 if (!workspace_names ||
1910 g_strv_length (workspace_names) < (guint)i + 1 ||
1911 !*workspace_names[i])
1912 {
1913 char *generated_name = g_strdup_printf (_("Workspace %d"), i + 1);
1914 name = g_intern_string (generated_name);
1915 g_free (generated_name);
1916 }
1917 else
1918 name = workspace_names[i];
1919
1920 meta_topic (META_DEBUG_PREFS,
1921 "Getting name of workspace %d: \"%s\"", i, name);
1922
1923 return name;
1924 }
1925
1926 void
meta_prefs_change_workspace_name(int num,const char * name)1927 meta_prefs_change_workspace_name (int num,
1928 const char *name)
1929 {
1930 GVariantBuilder builder;
1931 int n_workspace_names, i;
1932
1933 g_return_if_fail (num >= 0);
1934
1935 meta_topic (META_DEBUG_PREFS,
1936 "Changing name of workspace %d to %s",
1937 num, name ? name : "none");
1938
1939 /* NULL and empty string both mean "default" here,
1940 * and we also need to match the name against its default value
1941 * to avoid saving it literally. */
1942 if (g_strcmp0 (name, meta_prefs_get_workspace_name (num)) == 0)
1943 {
1944 if (!name || !*name)
1945 meta_topic (META_DEBUG_PREFS,
1946 "Workspace %d already uses default name", num);
1947 else
1948 meta_topic (META_DEBUG_PREFS,
1949 "Workspace %d already has name %s", num, name);
1950 return;
1951 }
1952
1953 g_variant_builder_init (&builder, G_VARIANT_TYPE_STRING_ARRAY);
1954 n_workspace_names = workspace_names ? g_strv_length (workspace_names) : 0;
1955
1956 for (i = 0; i < MAX (num + 1, n_workspace_names); i++)
1957 {
1958 const char *value;
1959
1960 if (i == num)
1961 value = name ? name : "";
1962 else if (i < n_workspace_names)
1963 value = workspace_names[i] ? workspace_names[i] : "";
1964 else
1965 value = "";
1966
1967 g_variant_builder_add (&builder, "s", value);
1968 }
1969
1970 g_settings_set_value (SETTINGS (SCHEMA_GENERAL), KEY_WORKSPACE_NAMES,
1971 g_variant_builder_end (&builder));
1972 }
1973
1974 /**
1975 * meta_prefs_get_button_layout:
1976 * @button_layout: (out):
1977 */
1978 void
meta_prefs_get_button_layout(MetaButtonLayout * button_layout_p)1979 meta_prefs_get_button_layout (MetaButtonLayout *button_layout_p)
1980 {
1981 *button_layout_p = button_layout;
1982 }
1983
1984 gboolean
meta_prefs_get_visual_bell(void)1985 meta_prefs_get_visual_bell (void)
1986 {
1987 return bell_is_visible;
1988 }
1989
1990 gboolean
meta_prefs_bell_is_audible(void)1991 meta_prefs_bell_is_audible (void)
1992 {
1993 return bell_is_audible;
1994 }
1995
1996 GDesktopVisualBellType
meta_prefs_get_visual_bell_type(void)1997 meta_prefs_get_visual_bell_type (void)
1998 {
1999 return visual_bell_type;
2000 }
2001
2002 gboolean
meta_prefs_add_keybinding(const char * name,GSettings * settings,MetaKeyBindingAction action,MetaKeyBindingFlags flags)2003 meta_prefs_add_keybinding (const char *name,
2004 GSettings *settings,
2005 MetaKeyBindingAction action,
2006 MetaKeyBindingFlags flags)
2007 {
2008 MetaKeyPref *pref;
2009 char **strokes;
2010 guint id;
2011
2012 if (g_hash_table_lookup (key_bindings, name))
2013 {
2014 meta_warning ("Trying to re-add keybinding \"%s\".", name);
2015 return FALSE;
2016 }
2017
2018 pref = g_new0 (MetaKeyPref, 1);
2019 pref->name = g_strdup (name);
2020 pref->settings = g_object_ref (settings);
2021 pref->action = action;
2022 pref->combos = NULL;
2023 pref->builtin = (flags & META_KEY_BINDING_BUILTIN) != 0;
2024
2025 if (pref->builtin)
2026 {
2027 if (g_object_get_data (G_OBJECT (settings), "changed-signal") == NULL)
2028 {
2029 id = g_signal_connect (settings, "changed",
2030 G_CALLBACK (bindings_changed), NULL);
2031 g_object_set_data (G_OBJECT (settings), "changed-signal", GUINT_TO_POINTER (id));
2032 }
2033 }
2034 else
2035 {
2036 char *changed_signal = g_strdup_printf ("changed::%s", name);
2037 id = g_signal_connect (settings, changed_signal,
2038 G_CALLBACK (bindings_changed), NULL);
2039 g_free (changed_signal);
2040
2041 g_object_set_data (G_OBJECT (settings), name, GUINT_TO_POINTER (id));
2042
2043 queue_changed (META_PREF_KEYBINDINGS);
2044 }
2045
2046 strokes = g_settings_get_strv (settings, name);
2047 update_binding (pref, strokes);
2048 g_strfreev (strokes);
2049
2050 g_hash_table_insert (key_bindings, g_strdup (name), pref);
2051
2052 return TRUE;
2053 }
2054
2055 gboolean
meta_prefs_remove_keybinding(const char * name)2056 meta_prefs_remove_keybinding (const char *name)
2057 {
2058 MetaKeyPref *pref;
2059 gulong id;
2060
2061 pref = g_hash_table_lookup (key_bindings, name);
2062 if (!pref)
2063 {
2064 meta_warning ("Trying to remove non-existent keybinding \"%s\".", name);
2065 return FALSE;
2066 }
2067
2068 if (pref->builtin)
2069 {
2070 meta_warning ("Trying to remove builtin keybinding \"%s\".", name);
2071 return FALSE;
2072 }
2073
2074 id = GPOINTER_TO_UINT (g_object_steal_data (G_OBJECT (pref->settings), name));
2075 g_clear_signal_handler (&id, pref->settings);
2076
2077 g_hash_table_remove (key_bindings, name);
2078
2079 queue_changed (META_PREF_KEYBINDINGS);
2080
2081 return TRUE;
2082 }
2083
2084 GList *
meta_prefs_get_keybindings(void)2085 meta_prefs_get_keybindings (void)
2086 {
2087 return g_hash_table_get_values (key_bindings);
2088 }
2089
2090 void
meta_prefs_get_overlay_binding(MetaKeyCombo * combo)2091 meta_prefs_get_overlay_binding (MetaKeyCombo *combo)
2092 {
2093 *combo = overlay_key_combo;
2094 }
2095
2096 void
meta_prefs_get_locate_pointer_binding(MetaKeyCombo * combo)2097 meta_prefs_get_locate_pointer_binding (MetaKeyCombo *combo)
2098 {
2099 *combo = locate_pointer_key_combo;
2100 }
2101
2102 gboolean
meta_prefs_is_locate_pointer_enabled(void)2103 meta_prefs_is_locate_pointer_enabled (void)
2104 {
2105 return locate_pointer_is_enabled;
2106 }
2107
2108 unsigned int
meta_prefs_get_check_alive_timeout(void)2109 meta_prefs_get_check_alive_timeout (void)
2110 {
2111 return check_alive_timeout;
2112 }
2113
2114 const char *
meta_prefs_get_iso_next_group_option(void)2115 meta_prefs_get_iso_next_group_option (void)
2116 {
2117 return iso_next_group_option;
2118 }
2119
2120 GDesktopTitlebarAction
meta_prefs_get_action_double_click_titlebar(void)2121 meta_prefs_get_action_double_click_titlebar (void)
2122 {
2123 return action_double_click_titlebar;
2124 }
2125
2126 GDesktopTitlebarAction
meta_prefs_get_action_middle_click_titlebar(void)2127 meta_prefs_get_action_middle_click_titlebar (void)
2128 {
2129 return action_middle_click_titlebar;
2130 }
2131
2132 GDesktopTitlebarAction
meta_prefs_get_action_right_click_titlebar(void)2133 meta_prefs_get_action_right_click_titlebar (void)
2134 {
2135 return action_right_click_titlebar;
2136 }
2137
2138 gboolean
meta_prefs_get_auto_raise(void)2139 meta_prefs_get_auto_raise (void)
2140 {
2141 return auto_raise;
2142 }
2143
2144 int
meta_prefs_get_auto_raise_delay(void)2145 meta_prefs_get_auto_raise_delay (void)
2146 {
2147 return auto_raise_delay;
2148 }
2149
2150 gboolean
meta_prefs_get_focus_change_on_pointer_rest(void)2151 meta_prefs_get_focus_change_on_pointer_rest (void)
2152 {
2153 return focus_change_on_pointer_rest;
2154 }
2155
2156 gboolean
meta_prefs_get_gnome_accessibility(void)2157 meta_prefs_get_gnome_accessibility (void)
2158 {
2159 return gnome_accessibility;
2160 }
2161
2162 gboolean
meta_prefs_get_gnome_animations(void)2163 meta_prefs_get_gnome_animations (void)
2164 {
2165 return gnome_animations;
2166 }
2167
2168 gboolean
meta_prefs_get_edge_tiling(void)2169 meta_prefs_get_edge_tiling (void)
2170 {
2171 return edge_tiling;
2172 }
2173
2174 gboolean
meta_prefs_get_auto_maximize(void)2175 meta_prefs_get_auto_maximize (void)
2176 {
2177 return auto_maximize;
2178 }
2179
2180 MetaKeyBindingAction
meta_prefs_get_keybinding_action(const char * name)2181 meta_prefs_get_keybinding_action (const char *name)
2182 {
2183 MetaKeyPref *pref = g_hash_table_lookup (key_bindings, name);
2184
2185 return pref ? pref->action
2186 : META_KEYBINDING_ACTION_NONE;
2187 }
2188
2189 gint
meta_prefs_get_mouse_button_resize(void)2190 meta_prefs_get_mouse_button_resize (void)
2191 {
2192 return resize_with_right_button ? 3: 2;
2193 }
2194
2195 gint
meta_prefs_get_mouse_button_menu(void)2196 meta_prefs_get_mouse_button_menu (void)
2197 {
2198 return resize_with_right_button ? 2: 3;
2199 }
2200
2201 gboolean
meta_prefs_get_force_fullscreen(void)2202 meta_prefs_get_force_fullscreen (void)
2203 {
2204 return force_fullscreen;
2205 }
2206
2207 gboolean
meta_prefs_get_workspaces_only_on_primary(void)2208 meta_prefs_get_workspaces_only_on_primary (void)
2209 {
2210 return workspaces_only_on_primary;
2211 }
2212
2213 int
meta_prefs_get_draggable_border_width(void)2214 meta_prefs_get_draggable_border_width (void)
2215 {
2216 return draggable_border_width;
2217 }
2218
2219 int
meta_prefs_get_drag_threshold(void)2220 meta_prefs_get_drag_threshold (void)
2221 {
2222 return drag_threshold;
2223 }
2224
2225 void
meta_prefs_set_force_fullscreen(gboolean whether)2226 meta_prefs_set_force_fullscreen (gboolean whether)
2227 {
2228 force_fullscreen = whether;
2229 }
2230