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       &center_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