From 4ac240d28b613499c49487b9f6280108055948a4 Mon Sep 17 00:00:00 2001 From: Yaakov Selkowitz Date: Sun, 12 Jul 2015 23:48:16 -0500 Subject: [PATCH] workarounds: add app-menu and button-layout workarounds fix-app-menu: if enabled gnome-flashback will set Gtk/ShellShowsAppMenu to FALSE. fix-button-layout: if enabled gnome-flashback will set Gtk/DecorationLayout to value set in this setting. To disable this workaround set it to empty string. NOTE: It is not possible to use Gtk/ShellShowsAppMenu and Gtk/DecorationLayout as overrides in xsettings plugin in gnome-settings-daemon. gnome-flashback will override these properties. Backports of following commits to gnome-3-14: f0ec5b40f5f0cfc23e6d101428fb7d85ed5892b7 69b07b0f58494fd5a64fa9c59550544efd9b2215 018268113a857f24f9c1c7863510007266cb18c8 f6cb156d92cc1997918e66823e305bf8d5301d31 --- configure.ac | 5 + data/org.gnome.gnome-flashback.gschema.xml.in.in | 19 + gnome-flashback/Makefile.am | 10 +- gnome-flashback/flashback-application.c | 14 + gnome-flashback/libworkarounds/Makefile.am | 21 + .../libworkarounds/flashback-workarounds.c | 765 +++++++++++++++++++++ .../libworkarounds/flashback-workarounds.h | 50 ++ 7 files changed, 882 insertions(+), 2 deletions(-) create mode 100644 gnome-flashback/libworkarounds/Makefile.am create mode 100644 gnome-flashback/libworkarounds/flashback-workarounds.c create mode 100644 gnome-flashback/libworkarounds/flashback-workarounds.h diff --git a/configure.ac b/configure.ac index 412efe8..f4f853e 100644 --- a/configure.ac +++ b/configure.ac @@ -64,6 +64,10 @@ PKG_CHECK_MODULES(SOUND_APPLET, gtk+-3.0 >= $GTK_REQUIRED libcanberra-gtk3 >= $C AC_SUBST(SOUND_APPLET_CFLAGS) AC_SUBST(SOUND_APPLET_LIBS) +PKG_CHECK_MODULES(WORKAROUNDS, glib-2.0 >= $GLIB_REQUIRED gtk+-3.0 >= $GTK_REQUIRED x11) +AC_SUBST(WORKAROUNDS_CFLAGS) +AC_SUBST(WORKAROUNDS_LIBS) + AC_CONFIG_FILES([ Makefile data/Makefile @@ -75,6 +79,7 @@ gnome-flashback/libend-session-dialog/Makefile gnome-flashback/libidle-monitor/Makefile gnome-flashback/libsound-applet/Makefile gnome-flashback/libsound-applet/gvc/Makefile +gnome-flashback/libworkarounds/Makefile po/Makefile.in ]) diff --git a/data/org.gnome.gnome-flashback.gschema.xml.in.in b/data/org.gnome.gnome-flashback.gschema.xml.in.in index 5c766a2..7fe3b42 100644 --- a/data/org.gnome.gnome-flashback.gschema.xml.in.in +++ b/data/org.gnome.gnome-flashback.gschema.xml.in.in @@ -30,8 +30,14 @@ <_summary>Sound applet <_description>If set to true, then GNOME Flashback application will be used to show sound applet. This is same sound applet that used to be part of GNOME Control Center. + + true + <_summary>Workarounds + <_description>If set to true, then GNOME Flashback application will use workarounds to fix bugs. + + @@ -41,4 +47,17 @@ <_description>If set to true, then fade effect will be used to change the desktop background. + + + + true + <_summary>Fix missing app menu button + <_description>If set to true, then gnome-flashback will force Gtk/ShellShowsAppMenu to FALSE. Disable if you want to use gnome-settings-daemon overrides in xsettings plugin for 'Gtk/ShellShowsAppMenu' property. + + + 'menu:minimize,maximize,close' + <_summary>Fix wrong button layout + <_description>If set to non-empty string, then gnome-flashback will force Gtk/DecorationLayout to value set by this setting. Set to empty string if you want to use gnome-settings-daemon overrides in xsettings plugin for 'Gtk/DecorationLayout' property. + + diff --git a/gnome-flashback/Makefile.am b/gnome-flashback/Makefile.am index 3a10388..c568d37 100644 --- a/gnome-flashback/Makefile.am +++ b/gnome-flashback/Makefile.am @@ -1,10 +1,14 @@ +NULL = + SUBDIRS = \ libautomount-manager \ libdesktop-background \ libdisplay-config \ libend-session-dialog \ libidle-monitor \ - libsound-applet + libsound-applet \ + libworkarounds \ + $(NULL) bin_PROGRAMS = \ gnome-flashback @@ -28,6 +32,8 @@ gnome_flashback_LDADD = \ $(top_builddir)/gnome-flashback/libdisplay-config/libdisplay-config.la \ $(top_builddir)/gnome-flashback/libend-session-dialog/libend-session-dialog.la \ $(top_builddir)/gnome-flashback/libidle-monitor/libidle-monitor.la \ - $(top_builddir)/gnome-flashback/libsound-applet/libsound-applet.la + $(top_builddir)/gnome-flashback/libsound-applet/libsound-applet.la \ + $(top_builddir)/gnome-flashback/libworkarounds/libworkarounds.la \ + $(NULL) -include $(top_srcdir)/git.mk diff --git a/gnome-flashback/flashback-application.c b/gnome-flashback/flashback-application.c index 3153128..5a9f098 100644 --- a/gnome-flashback/flashback-application.c +++ b/gnome-flashback/flashback-application.c @@ -25,6 +25,7 @@ #include "libend-session-dialog/flashback-end-session-dialog.h" #include "libidle-monitor/meta-idle-monitor-dbus.h" #include "libsound-applet/gvc-applet.h" +#include "libworkarounds/flashback-workarounds.h" #define FLASHBACK_SCHEMA "org.gnome.gnome-flashback" #define KEY_AUTOMOUNT_MANAGER "automount-manager" @@ -33,6 +34,7 @@ #define KEY_END_SESSION_DIALOG "end-session-dialog" #define KEY_IDLE_MONITOR "idle-monitor" #define KEY_SOUND_APPLET "sound-applet" +#define KEY_WORKAROUNDS "workarounds" struct _FlashbackApplicationPrivate { GSettings *settings; @@ -42,6 +44,7 @@ struct _FlashbackApplicationPrivate { FlashbackEndSessionDialog *dialog; MetaIdleMonitorDBus *idle_monitor; GvcApplet *applet; + FlashbackWorkarounds *workarounds; }; G_DEFINE_TYPE_WITH_PRIVATE (FlashbackApplication, flashback_application, G_TYPE_OBJECT); @@ -115,6 +118,16 @@ flashback_application_settings_changed (GSettings *settings, g_clear_object (&app->priv->applet); } } + + if (key == NULL || g_strcmp0 (key, KEY_WORKAROUNDS) == 0) { + if (g_settings_get_boolean (settings, KEY_WORKAROUNDS)) { + if (app->priv->workarounds == NULL) { + app->priv->workarounds = flashback_workarounds_new (); + } + } else { + g_clear_object (&app->priv->workarounds); + } + } } static void @@ -127,6 +140,7 @@ flashback_application_finalize (GObject *object) g_clear_object (&app->priv->dialog); g_clear_object (&app->priv->idle_monitor); g_clear_object (&app->priv->applet); + g_clear_object (&app->priv->workarounds); g_clear_object (&app->priv->settings); G_OBJECT_CLASS (flashback_application_parent_class)->finalize (object); diff --git a/gnome-flashback/libworkarounds/Makefile.am b/gnome-flashback/libworkarounds/Makefile.am new file mode 100644 index 0000000..6cfc453 --- /dev/null +++ b/gnome-flashback/libworkarounds/Makefile.am @@ -0,0 +1,21 @@ +NULL = + +noinst_LTLIBRARIES = \ + libworkarounds.la \ + $(NULL) + +AM_CPPFLAGS = \ + $(WORKAROUNDS_CFLAGS) \ + -I$(top_builddir)/gnome-flashback/libworkarounds \ + $(NULL) + +libworkarounds_la_SOURCES = \ + flashback-workarounds.c \ + flashback-workarounds.h \ + $(NULL) + +libworkarounds_la_LIBADD = \ + $(WORKAROUNDS_LIBS) \ + $(NULL) + +-include $(top_srcdir)/git.mk diff --git a/gnome-flashback/libworkarounds/flashback-workarounds.c b/gnome-flashback/libworkarounds/flashback-workarounds.c new file mode 100644 index 0000000..39fdfbb --- /dev/null +++ b/gnome-flashback/libworkarounds/flashback-workarounds.c @@ -0,0 +1,765 @@ +/* + * Copyright (C) 2015 Alberts Muktupāvels + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Based on code from Owen Taylor, Copyright(C) 2001, 2007 Red Hat, Inc. + * + * https://git.gnome.org/browse/gtk+/tree/gdk/x11/xsettings-client.c + * https://git.gnome.org/browse/gnome-settings-daemon/tree/plugins/xsettings/xsettings-manager.c + */ + +#include "config.h" + +#include +#include +#include +#include + +#include "flashback-workarounds.h" + +#define BYTES_LEFT(buffer) ((buffer)->data + (buffer)->len - (buffer)->pos) +#define RETURN_IF_FAIL_BYTES(buffer, n_bytes) if (BYTES_LEFT (buffer) < (n_bytes)) return FALSE; +#define XSETTINGS_PAD(n,m) ((n + m - 1) & (~(m-1))) + +struct _FlashbackWorkaroundsPrivate +{ + GSettings *g_settings; + GtkSettings *gtk_settings; + + gboolean fix_app_menu; + gchar *fix_button_layout; + + guint idle_id; + guint timeout_id; + + Display *xdisplay; + + Atom selection_atom; + Atom xsettings_atom; + + Window manager_window; + + GHashTable *xsettings; + CARD32 serial; +}; + +G_DEFINE_TYPE_WITH_PRIVATE (FlashbackWorkarounds, flashback_workarounds, G_TYPE_OBJECT) + +static void add_workarounds (FlashbackWorkarounds *workarounds); + +typedef enum +{ + XSETTINGS_TYPE_INT = 0, + XSETTINGS_TYPE_STRING = 1, + XSETTINGS_TYPE_COLOR = 2 +} XSettingsType; + +typedef struct +{ + gchar *name; + XSettingsType type; + GValue *value; + gulong last_change_serial; +} XSettingsSetting; + +typedef struct +{ + gchar byte_order; + gulong len; + guchar *data; + guchar *pos; +} XSettingsBuffer; + +static gboolean +fetch_card16 (XSettingsBuffer *buffer, + CARD16 *result) +{ + CARD16 x; + + RETURN_IF_FAIL_BYTES (buffer, 2); + + x = *(CARD16 *)buffer->pos; + buffer->pos += 2; + + if (buffer->byte_order == MSBFirst) + *result = GUINT16_FROM_BE (x); + else + *result = GUINT16_FROM_LE (x); + + return TRUE; +} + +static gboolean +fetch_ushort (XSettingsBuffer *buffer, + gushort *result) +{ + CARD16 x; + gboolean r; + + r = fetch_card16 (buffer, &x); + if (r) + *result = x; + + return r; +} + +static gboolean +fetch_card32 (XSettingsBuffer *buffer, + CARD32 *result) +{ + CARD32 x; + + RETURN_IF_FAIL_BYTES (buffer, 4); + + x = *(CARD32 *)buffer->pos; + buffer->pos += 4; + + if (buffer->byte_order == MSBFirst) + *result = GUINT32_FROM_BE (x); + else + *result = GUINT32_FROM_LE (x); + + return TRUE; +} + +static gboolean +fetch_card8 (XSettingsBuffer *buffer, + CARD8 *result) +{ + RETURN_IF_FAIL_BYTES (buffer, 1); + + *result = *(CARD8 *)buffer->pos; + buffer->pos += 1; + + return TRUE; +} + +static gboolean +fetch_string (XSettingsBuffer *buffer, + guint length, + gchar **result) +{ + guint pad_len; + + pad_len = XSETTINGS_PAD (length, 4); + if (pad_len < length) + return FALSE; + + RETURN_IF_FAIL_BYTES (buffer, pad_len); + + *result = g_strndup ((gchar *) buffer->pos, length); + buffer->pos += pad_len; + + return TRUE; +} + +static void +free_gvalue (gpointer user_data) +{ + GValue *value; + + value = (GValue *) user_data; + + g_value_unset (value); + g_free (value); +} + +static void +free_xsetting (gpointer data) +{ + XSettingsSetting *setting; + + setting = (XSettingsSetting *) data; + + g_free (setting->name); + free_gvalue (setting->value); + g_free (setting); +} + +static GHashTable * +parse_settings (FlashbackWorkarounds *workarounds, + guchar *data, + gulong n_items) +{ + XSettingsBuffer buffer; + GHashTable *settings; + CARD32 n_entries; + CARD32 i; + GValue *value; + gchar *x_name; + gulong last_change_serial; + XSettingsSetting *setting; + + buffer.pos = buffer.data = data; + buffer.len = n_items; + + if (!fetch_card8 (&buffer, (guchar *)&buffer.byte_order)) + return NULL; + + if (buffer.byte_order != MSBFirst && buffer.byte_order != LSBFirst) + return NULL; + + buffer.pos += 3; + + if (!fetch_card32 (&buffer, &workarounds->priv->serial) || + !fetch_card32 (&buffer, &n_entries)) + return NULL; + + settings = NULL; + value = NULL; + x_name = NULL; + + for (i = 0; i < n_entries; i++) + { + CARD8 type; + CARD16 name_len; + CARD32 v_int; + + if (!fetch_card8 (&buffer, &type)) + goto out; + + buffer.pos += 1; + + if (!fetch_card16 (&buffer, &name_len)) + goto out; + + if (!fetch_string (&buffer, name_len, &x_name) || !fetch_card32 (&buffer, &v_int)) + goto out; + + last_change_serial = (gulong) v_int; + + switch (type) + { + case XSETTINGS_TYPE_INT: + if (!fetch_card32 (&buffer, &v_int)) + goto out; + + value = g_new0 (GValue, 1); + g_value_init (value, G_TYPE_INT); + g_value_set_int (value, (gint32) v_int); + break; + + case XSETTINGS_TYPE_STRING: + { + gchar *s; + + if (!fetch_card32 (&buffer, &v_int) || !fetch_string (&buffer, v_int, &s)) + goto out; + + value = g_new0 (GValue, 1); + g_value_init (value, G_TYPE_STRING); + g_value_take_string (value, s); + } + break; + + case XSETTINGS_TYPE_COLOR: + /* GNOME Settings Daemon does not export settings with color type. */ + g_free (x_name); + x_name = NULL; + break; + + default: + /* Unknown type */ + g_free (x_name); + x_name = NULL; + break; + } + + if (settings == NULL) + settings = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, free_xsetting); + + if (g_hash_table_lookup (settings, x_name) != NULL) + goto out; + + if (x_name != NULL) + { + setting = g_new0 (XSettingsSetting, 1); + + setting->name = g_strdup (x_name); + setting->type = type; + setting->value = value; + setting->last_change_serial = last_change_serial; + + g_hash_table_insert (settings, (gpointer) x_name, setting); + + x_name = NULL; + value = NULL; + } + } + + return settings; + +out: + + if (value) + free_gvalue (value); + + if (settings) + g_hash_table_unref (settings); + + g_free (x_name); + + return NULL; +} + +static gboolean +read_settings (FlashbackWorkarounds *workarounds) +{ + GdkDisplay *display; + gint result; + Atom type; + gint format; + gulong n_items; + gulong bytes_after; + guchar *data; + + display = gdk_x11_lookup_xdisplay (workarounds->priv->xdisplay); + + gdk_x11_display_error_trap_push (display); + result = XGetWindowProperty (workarounds->priv->xdisplay, workarounds->priv->manager_window, + workarounds->priv->xsettings_atom, 0, LONG_MAX, + False, workarounds->priv->xsettings_atom, + &type, &format, &n_items, &bytes_after, &data); + gdk_x11_display_error_trap_pop_ignored (display); + + if (result == Success && type != None) + { + if (type == workarounds->priv->xsettings_atom && format == 8) + workarounds->priv->xsettings = parse_settings (workarounds, data, n_items); + + XFree (data); + } + + if (workarounds->priv->xsettings) + return TRUE; + + return FALSE; +} + +static gchar +get_byte_order (void) +{ + CARD32 myint = 0x01020304; + return (*(gchar *)&myint == 1) ? MSBFirst : LSBFirst; +} + +static void +align_string (GString *string, + gint alignment) +{ + while ((string->len % alignment) != 0) + g_string_append_c (string, '\0'); +} + +static void +setting_store (XSettingsSetting *setting, + GString *buffer) +{ + guint16 len16; + + g_string_append_c (buffer, setting->type); + g_string_append_c (buffer, 0); + + len16 = strlen (setting->name); + g_string_append_len (buffer, (gchar *) &len16, 2); + g_string_append (buffer, setting->name); + align_string (buffer, 4); + + g_string_append_len (buffer, (gchar *) &setting->last_change_serial, 4); + + if (setting->type == XSETTINGS_TYPE_INT) + { + gint value; + + value = g_value_get_int (setting->value); + + g_string_append_len (buffer, (gchar *) &value, 4); + } + else if (setting->type == XSETTINGS_TYPE_STRING) + { + const gchar *string; + guint32 len32; + + string = g_value_get_string (setting->value); + len32 = strlen (string); + g_string_append_len (buffer, (gchar *) &len32, 4); + g_string_append (buffer, string); + align_string (buffer, 4); + } + else if (setting->type == XSETTINGS_TYPE_COLOR) + { + /* GNOME Settings Daemon does not export settings with color type. */ + } +} + +static void +write_settings (FlashbackWorkarounds *workarounds) +{ + GString *buffer; + GHashTableIter iter; + int n_settings; + gpointer value; + + n_settings = g_hash_table_size (workarounds->priv->xsettings); + + buffer = g_string_new (NULL); + g_string_append_c (buffer, get_byte_order ()); + g_string_append_c (buffer, '\0'); + g_string_append_c (buffer, '\0'); + g_string_append_c (buffer, '\0'); + + g_string_append_len (buffer, (gchar *) &workarounds->priv->serial, 4); + g_string_append_len (buffer, (gchar *) &n_settings, 4); + + g_hash_table_iter_init (&iter, workarounds->priv->xsettings); + while (g_hash_table_iter_next (&iter, NULL, &value)) + setting_store (value, buffer); + + XChangeProperty (workarounds->priv->xdisplay, workarounds->priv->manager_window, + workarounds->priv->xsettings_atom, workarounds->priv->xsettings_atom, + 8, PropModeReplace, (guchar *) buffer->str, buffer->len); + + g_string_free (buffer, TRUE); + g_hash_table_unref (workarounds->priv->xsettings); + workarounds->priv->xsettings = NULL; +} + +static void +apply_app_menu_workaround (FlashbackWorkarounds *workarounds) +{ + const gchar *key; + XSettingsSetting *setting; + + key = "Gtk/ShellShowsAppMenu"; + setting = g_hash_table_lookup (workarounds->priv->xsettings, key); + + if (setting != NULL) + { + g_hash_table_steal (workarounds->priv->xsettings, key); + free_gvalue (setting->value); + } + else + { + setting = g_new0 (XSettingsSetting, 1); + setting->name = g_strdup (key); + setting->type = XSETTINGS_TYPE_INT; + setting->last_change_serial = 0; + } + + setting->value = g_new0 (GValue, 1); + g_value_init (setting->value, G_TYPE_INT); + g_value_set_int (setting->value, 0); + + g_hash_table_insert (workarounds->priv->xsettings, g_strdup (key), setting); +} + +static void +apply_button_layout_workaround (FlashbackWorkarounds *workarounds) +{ + const gchar *key; + XSettingsSetting *setting; + + key = "Gtk/DecorationLayout"; + setting = g_hash_table_lookup (workarounds->priv->xsettings, key); + + if (setting != NULL) + { + g_hash_table_steal (workarounds->priv->xsettings, key); + free_gvalue (setting->value); + } + else + { + setting = g_new0 (XSettingsSetting, 1); + setting->name = g_strdup (key); + setting->type = XSETTINGS_TYPE_STRING; + setting->last_change_serial = 0; + } + + setting->value = g_new0 (GValue, 1); + g_value_init (setting->value, G_TYPE_STRING); + g_value_set_string (setting->value, workarounds->priv->fix_button_layout); + + g_hash_table_insert (workarounds->priv->xsettings, g_strdup (key), setting); +} + +static gboolean +apply_workarounds (FlashbackWorkarounds *workarounds) +{ + gboolean gtk_shell_shows_app_menu; + gchar *gtk_decoration_layout; + gboolean need_workarounds; + + g_object_get (workarounds->priv->gtk_settings, + "gtk-shell-shows-app-menu", >k_shell_shows_app_menu, + NULL); + + g_object_get (workarounds->priv->gtk_settings, + "gtk-decoration-layout", >k_decoration_layout, + NULL); + + need_workarounds = gtk_shell_shows_app_menu; + if (g_strcmp0 (gtk_decoration_layout, workarounds->priv->fix_button_layout) != 0) + need_workarounds = TRUE; + + g_free (gtk_decoration_layout); + + if (!need_workarounds) + return TRUE; + + workarounds->priv->manager_window = XGetSelectionOwner (workarounds->priv->xdisplay, + workarounds->priv->selection_atom); + + if (workarounds->priv->manager_window == None) + return FALSE; + + if (!read_settings (workarounds)) + return FALSE; + + if (workarounds->priv->fix_app_menu) + apply_app_menu_workaround (workarounds); + + if (g_strcmp0 (workarounds->priv->fix_button_layout, "") != 0) + apply_button_layout_workaround (workarounds); + + write_settings (workarounds); + + return TRUE; +} + +static gboolean +try_again (gpointer user_data) +{ + FlashbackWorkarounds *workarounds; + + workarounds = FLASHBACK_WORKAROUNDS (user_data); + + add_workarounds (workarounds); + + workarounds->priv->timeout_id = 0; + return G_SOURCE_REMOVE; +} + +static gboolean +add_workarounds_real (gpointer user_data) +{ + FlashbackWorkarounds *workarounds; + gboolean fix_app_menu; + gchar *fix_button_layout; + + workarounds = FLASHBACK_WORKAROUNDS (user_data); + + fix_app_menu = g_settings_get_boolean (workarounds->priv->g_settings, + "fix-app-menu"); + fix_button_layout = g_settings_get_string (workarounds->priv->g_settings, + "fix-button-layout"); + + g_free (workarounds->priv->fix_button_layout); + + workarounds->priv->fix_app_menu = fix_app_menu; + workarounds->priv->fix_button_layout = fix_button_layout; + + if (!fix_app_menu && g_strcmp0 (fix_button_layout, "") == 0) + { + workarounds->priv->idle_id = 0; + return G_SOURCE_REMOVE; + } + + if (!apply_workarounds (workarounds)) + { + if (workarounds->priv->timeout_id > 0) + g_source_remove (workarounds->priv->timeout_id); + + workarounds->priv->timeout_id = g_timeout_add (100, try_again, workarounds); + g_source_set_name_by_id (workarounds->priv->timeout_id, + "[gnome-flashback] try_again"); + } + + workarounds->priv->idle_id = 0; + return G_SOURCE_REMOVE; +} + +static void +add_workarounds (FlashbackWorkarounds *workarounds) +{ + if (workarounds->priv->idle_id > 0) + g_source_remove (workarounds->priv->idle_id); + + workarounds->priv->idle_id = g_idle_add (add_workarounds_real, workarounds); + g_source_set_name_by_id (workarounds->priv->idle_id, + "[gnome-flashback] add_workarounds_real"); +} + +static void +remove_workarounds (void) +{ + GSettings *settings; + GVariant *overrides; + + settings = g_settings_new ("org.gnome.settings-daemon.plugins.xsettings"); + + overrides = g_settings_get_value (settings, "overrides"); + g_settings_set_value (settings, "overrides", overrides); + + g_variant_unref (overrides); + g_object_unref (settings); +} + +static void +g_settings_changed (GSettings *settings, + const gchar *key, + gpointer user_data) +{ + FlashbackWorkarounds *workarounds; + gboolean fix_app_menu; + gchar *fix_button_layout; + gboolean reset; + + workarounds = FLASHBACK_WORKAROUNDS (user_data); + + fix_app_menu = g_settings_get_boolean (workarounds->priv->g_settings, + "fix-app-menu"); + fix_button_layout = g_settings_get_string (workarounds->priv->g_settings, + "fix-button-layout"); + + reset = FALSE; + + if (workarounds->priv->fix_app_menu && !fix_app_menu) + reset = TRUE; + + if (g_strcmp0 (workarounds->priv->fix_button_layout, "") != 0 && + g_strcmp0 (fix_button_layout, "") == 0) + reset = TRUE; + + g_free (fix_button_layout); + + if (reset) + { + remove_workarounds (); + return; + } + + add_workarounds (workarounds); +} + +static void +gtk_settings_changed (GtkSettings *settings, + GParamSpec *pspec, + gpointer user_data) +{ + FlashbackWorkarounds *workarounds; + + workarounds = FLASHBACK_WORKAROUNDS (user_data); + + add_workarounds (workarounds); +} + +static void +x11_init (FlashbackWorkarounds *workarounds) +{ + GdkDisplay *display; + Display *xdisplay; + GdkScreen *screen; + gint number; + gchar *atom; + + display = gdk_display_get_default (); + xdisplay = gdk_x11_display_get_xdisplay (display); + screen = gdk_display_get_default_screen (display); + number = gdk_screen_get_number (screen); + atom = g_strdup_printf ("_XSETTINGS_S%d", number); + + workarounds->priv->xdisplay = xdisplay; + workarounds->priv->selection_atom = XInternAtom (xdisplay, atom, False); + workarounds->priv->xsettings_atom = XInternAtom (xdisplay, "_XSETTINGS_SETTINGS", False); + + g_free (atom); +} + +static void +flashback_workarounds_finalize (GObject *object) +{ + FlashbackWorkarounds *workarounds; + + workarounds = FLASHBACK_WORKAROUNDS (object); + + g_clear_object (&workarounds->priv->g_settings); + g_signal_handlers_disconnect_by_func (workarounds->priv->gtk_settings, + gtk_settings_changed, + workarounds); + + g_free (workarounds->priv->fix_button_layout); + + if (workarounds->priv->idle_id > 0) + { + g_source_remove (workarounds->priv->idle_id); + workarounds->priv->idle_id = 0; + } + + if (workarounds->priv->timeout_id > 0) + { + g_source_remove (workarounds->priv->timeout_id); + workarounds->priv->timeout_id = 0; + } + + if (workarounds->priv->xsettings) + { + g_hash_table_unref (workarounds->priv->xsettings); + workarounds->priv->xsettings = NULL; + } + + remove_workarounds (); + + G_OBJECT_CLASS (flashback_workarounds_parent_class)->finalize (object); +} + +static void +flashback_workarounds_class_init (FlashbackWorkaroundsClass *class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (class); + + object_class->finalize = flashback_workarounds_finalize; +} + +static void +flashback_workarounds_init (FlashbackWorkarounds *workarounds) +{ + FlashbackWorkaroundsPrivate *priv; + + workarounds->priv = flashback_workarounds_get_instance_private (workarounds); + priv = workarounds->priv; + + x11_init (workarounds); + + workarounds->priv->g_settings = g_settings_new ("org.gnome.gnome-flashback.workarounds"); + workarounds->priv->gtk_settings = gtk_settings_get_default (); + + g_signal_connect (workarounds->priv->g_settings, "changed", + G_CALLBACK (g_settings_changed), workarounds); + g_signal_connect (workarounds->priv->gtk_settings, "notify::gtk-shell-shows-app-menu", + G_CALLBACK (gtk_settings_changed), workarounds); + g_signal_connect (workarounds->priv->gtk_settings, "notify::gtk-decoration-layout", + G_CALLBACK (gtk_settings_changed), workarounds); + + add_workarounds (workarounds); +} + +FlashbackWorkarounds * +flashback_workarounds_new (void) +{ + return FLASHBACK_WORKAROUNDS (g_object_new (FLASHBACK_TYPE_WORKAROUNDS, NULL)); +} diff --git a/gnome-flashback/libworkarounds/flashback-workarounds.h b/gnome-flashback/libworkarounds/flashback-workarounds.h new file mode 100644 index 0000000..328c38d --- /dev/null +++ b/gnome-flashback/libworkarounds/flashback-workarounds.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015 Alberts Muktupāvels + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef FLASHBACK_WORKAROUNDS_H +#define FLASHBACK_WORKAROUNDS_H + +#include + +G_BEGIN_DECLS + +#define FLASHBACK_TYPE_WORKAROUNDS (flashback_workarounds_get_type ()) +#define FLASHBACK_WORKAROUNDS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), FLASHBACK_TYPE_WORKAROUNDS, FlashbackWorkarounds)) +#define FLASHBACK_WORKAROUNDS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), FLASHBACK_TYPE_WORKAROUNDS, FlashbackWorkaroundsClass)) +#define FLASHBACK_IS_WORKAROUNDS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), FLASHBACK_TYPE_WORKAROUNDS)) +#define FLASHBACK_IS_WORKAROUNDS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), FLASHBACK_TYPE_WORKAROUNDS)) +#define FLASHBACK_WORKAROUNDS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), FLASHBACK_TYPE_WORKAROUNDS, FlashbackWorkaroundsClass)) + +typedef struct _FlashbackWorkarounds FlashbackWorkarounds; +typedef struct _FlashbackWorkaroundsClass FlashbackWorkaroundsClass; +typedef struct _FlashbackWorkaroundsPrivate FlashbackWorkaroundsPrivate; + +struct _FlashbackWorkarounds { + GObject parent; + FlashbackWorkaroundsPrivate *priv; +}; + +struct _FlashbackWorkaroundsClass { + GObjectClass parent_class; +}; + +GType flashback_workarounds_get_type (void); +FlashbackWorkarounds *flashback_workarounds_new (void); + +G_END_DECLS + +#endif -- 2.1.0