diff options
Diffstat (limited to 'maximus/maximus-bind.c')
-rw-r--r-- | maximus/maximus-bind.c | 501 |
1 files changed, 501 insertions, 0 deletions
diff --git a/maximus/maximus-bind.c b/maximus/maximus-bind.c new file mode 100644 index 0000000..cbbcf2e --- /dev/null +++ b/maximus/maximus-bind.c @@ -0,0 +1,501 @@ +/* + * Copyright (C) 2008 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * 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 <http://www.gnu.org/licenses/>. + * + * Authored by Neil Jagdish Patel <[email protected]> + * + */ + +#include <stdio.h> +#include <string.h> + +#include <gtk/gtk.h> +#include <gdk/gdkx.h> + +#include <gdk/gdkkeysyms.h> + +#include <mateconf/mateconf.h> +#include <mateconf/mateconf-client.h> + +#include <libmatewnck/libmatewnck.h> + +#include <X11/Xlib.h> +#include <X11/Xresource.h> +#include <X11/Xutil.h> +#include <X11/extensions/XTest.h> +#include <X11/keysymdef.h> +#include <X11/keysym.h> + +#include <fakekey/fakekey.h> + +#include "maximus-bind.h" + +#include "tomboykeybinder.h" +#include "eggaccelerators.h" + +G_DEFINE_TYPE (MaximusBind, maximus_bind, G_TYPE_OBJECT); + +#define MAXIMUS_BIND_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj),\ + MAXIMUS_TYPE_BIND, \ + MaximusBindPrivate)) + +#define KEY_RELEASE_TIMEOUT 300 +#define STATE_CHANGED_SLEEP 0.5 + +/* Gconf keys */ +#define BIND_PATH "/apps/maximus" +#define BIND_EXCLUDE_CLASS BIND_PATH "/binding" + +#define SYSRULESDIR SYSCONFDIR"/maximus" + +struct _MaximusBindPrivate +{ + FakeKey *fk; + MatewnckScreen *screen; + + gchar *binding; + + GList *rules; +}; + +typedef struct +{ + gchar *wm_class; + gchar *fullscreen; + gchar *unfullscreen; +} MaximusRule; + +static const gchar * +get_fullscreen_keystroke (GList *rules, MatewnckWindow *window) +{ + MatewnckClassGroup *group; + const gchar *class_name; + GList *r; + + group = matewnck_window_get_class_group (window); + class_name = matewnck_class_group_get_name (group); + + g_debug ("Searching rules for %s:\n", matewnck_window_get_name (window)); + + for (r = rules; r; r = r->next) + { + MaximusRule *rule = r->data; + + g_debug ("\t%s ?= %s", class_name, rule->wm_class); + + if (class_name && rule->wm_class && strstr (class_name, rule->wm_class)) + { + g_debug ("\tYES!\n"); + return rule->fullscreen; + } + g_debug ("\tNO!\n"); + } + + return NULL; +} + +static const gchar * +get_unfullscreen_keystroke (GList *rules, MatewnckWindow *window) +{ + MatewnckClassGroup *group; + const gchar *class_name; + GList *r; + + group = matewnck_window_get_class_group (window); + class_name = matewnck_class_group_get_name (group); + + for (r = rules; r; r = r->next) + { + MaximusRule *rule = r->data; + + if (class_name && rule->wm_class && strstr (class_name, rule->wm_class)) + { + return rule->unfullscreen; + } + } + + return NULL; +} +static gboolean +real_fullscreen (MaximusBind *bind) +{ + MaximusBindPrivate *priv; + GdkDisplay *display; + MatewnckWindow *active; + const gchar *keystroke; + + priv = bind->priv; + + display = gdk_display_get_default (); + active = matewnck_screen_get_active_window (priv->screen); + + if (!MATEWNCK_IS_WINDOW (active) + || matewnck_window_get_window_type (active) != MATEWNCK_WINDOW_NORMAL) + return FALSE; + + keystroke = get_fullscreen_keystroke (priv->rules, active); + + if (keystroke) + { + guint keysym = 0; + EggVirtualModifierType modifiers = 0; + + if (egg_accelerator_parse_virtual (keystroke, &keysym, &modifiers)) + { + guint mods = 0; + + if (modifiers & EGG_VIRTUAL_SHIFT_MASK) + mods |= FAKEKEYMOD_SHIFT; + if (modifiers & EGG_VIRTUAL_CONTROL_MASK) + mods |= FAKEKEYMOD_CONTROL; + if (modifiers & EGG_VIRTUAL_ALT_MASK) + mods |= FAKEKEYMOD_ALT; + if (modifiers & EGG_VIRTUAL_META_MASK) + mods |= FAKEKEYMOD_META; + + g_debug ("Sending fullscreen special event: %s = %d %d", + keystroke, keysym, mods); + fakekey_press_keysym (priv->fk, keysym, mods); + fakekey_release (priv->fk); + + return FALSE; + } + } + + if (!matewnck_window_is_fullscreen (active)) + { + g_debug ("Sending fullscreen F11 event"); + fakekey_press_keysym (priv->fk, XK_F11, 0); + fakekey_release (priv->fk); + } + + sleep (STATE_CHANGED_SLEEP); + + if (!matewnck_window_is_fullscreen (active)) + { + g_debug ("Forcing fullscreen matewnck event"); + matewnck_window_set_fullscreen (active, TRUE); + } + + return FALSE; +} + +static void +fullscreen (MaximusBind *bind, MatewnckWindow *window) +{ + MaximusBindPrivate *priv; + + priv = bind->priv; + + g_timeout_add (KEY_RELEASE_TIMEOUT, (GSourceFunc)real_fullscreen, bind); +} + +static gboolean +real_unfullscreen (MaximusBind *bind) +{ + MaximusBindPrivate *priv; + GdkDisplay *display; + MatewnckWindow *active; + const gchar *keystroke; + + priv = bind->priv; + + display = gdk_display_get_default (); + active = matewnck_screen_get_active_window (priv->screen); + + if (!MATEWNCK_IS_WINDOW (active) + || matewnck_window_get_window_type (active) != MATEWNCK_WINDOW_NORMAL) + return FALSE; + + keystroke = get_unfullscreen_keystroke (priv->rules, active); + + if (keystroke) + { + guint keysym = 0; + EggVirtualModifierType modifiers = 0; + + if (egg_accelerator_parse_virtual (keystroke, &keysym, &modifiers)) + { + guint mods = 0; + + if (modifiers & EGG_VIRTUAL_SHIFT_MASK) + mods |= FAKEKEYMOD_SHIFT; + if (modifiers & EGG_VIRTUAL_CONTROL_MASK) + mods |= FAKEKEYMOD_CONTROL; + if (modifiers & EGG_VIRTUAL_ALT_MASK) + mods |= FAKEKEYMOD_ALT; + if (modifiers & EGG_VIRTUAL_META_MASK) + mods |= FAKEKEYMOD_META; + + g_debug ("Sending fullscreen special event: %s = %d %d", + keystroke, keysym, mods); + fakekey_press_keysym (priv->fk, keysym, mods); + fakekey_release (priv->fk); + + return FALSE; + } + } + if (matewnck_window_is_fullscreen (active)) + { + g_debug ("Sending un-fullscreen F11 event"); + fakekey_press_keysym (priv->fk, XK_F11, 0); + fakekey_release (priv->fk); + } + + sleep (STATE_CHANGED_SLEEP); + + if (matewnck_window_is_fullscreen (active)) + { + g_debug ("Forcing un-fullscreen matewnck event"); + matewnck_window_set_fullscreen (active, FALSE); + } + + return FALSE; +} + +static void +unfullscreen (MaximusBind *bind, MatewnckWindow *window) +{ + MaximusBindPrivate *priv; + + priv = bind->priv; + + g_timeout_add (KEY_RELEASE_TIMEOUT, (GSourceFunc)real_unfullscreen, bind); +} + + +static void +on_binding_activated (gchar *keystring, MaximusBind *bind) +{ + MaximusBindPrivate *priv; + MatewnckWindow *active; + + g_return_if_fail (MAXIMUS_IS_BIND (bind)); + priv = bind->priv; + + active = matewnck_screen_get_active_window (priv->screen); + + if (matewnck_window_get_window_type (active) != MATEWNCK_WINDOW_NORMAL) + return; + + if (matewnck_window_is_fullscreen (active)) + { + unfullscreen (bind, active); + } + else + { + fullscreen (bind, active); + } +} + +/* Callbacks */ +static gboolean +binding_is_valid (const gchar *binding) +{ + gboolean retval = TRUE; + + if (!binding || strlen (binding) < 1 || strcmp (binding, "disabled") == 0) + retval = FALSE; + + return retval; +} + +static void +on_binding_changed (MateConfClient *client, + guint cid, + MateConfEntry *entry, + MaximusBind *bind) +{ + MaximusBindPrivate *priv; + + g_return_if_fail (MAXIMUS_IS_BIND (bind)); + priv = bind->priv; + + if (binding_is_valid (priv->binding)) + tomboy_keybinder_unbind (priv->binding, + (TomboyBindkeyHandler)on_binding_changed); + g_free (priv->binding); + + priv->binding = mateconf_client_get_string (client, + BIND_EXCLUDE_CLASS, + NULL); + + if (binding_is_valid (priv->binding)) + tomboy_keybinder_bind (priv->binding, + (TomboyBindkeyHandler)on_binding_activated, + bind); + + g_print ("Binding changed: %s\n", priv->binding); +} + + +/* GObject stuff */ +static void +create_rule (MaximusBind *bind, const gchar *filename) +{ +#define RULE_GROUP "Fullscreening" +#define RULE_WMCLASS "WMClass" +#define RULE_FULLSCREEN "Fullscreen" +#define RULE_UNFULLSCREEN "Unfullscreen" + MaximusBindPrivate *priv; + GKeyFile *file; + GError *error = NULL; + MaximusRule *rule; + + priv = bind->priv; + + file = g_key_file_new (); + g_key_file_load_from_file (file, filename, 0, &error); + if (error) + { + g_warning ("Unable to load %s: %s\n", filename, error->message); + g_error_free (error); + return; + } + + rule = g_slice_new0 (MaximusRule); + + rule->wm_class = g_key_file_get_string (file, + RULE_GROUP, RULE_WMCLASS, + NULL); + rule->fullscreen = g_key_file_get_string (file, + RULE_GROUP, RULE_FULLSCREEN, + NULL); + rule->unfullscreen = g_key_file_get_string (file, + RULE_GROUP, RULE_UNFULLSCREEN, + NULL); + if (!rule->wm_class || !rule->fullscreen || !rule->unfullscreen) + { + g_free (rule->wm_class); + g_free (rule->fullscreen); + g_free (rule->unfullscreen); + g_slice_free (MaximusRule, rule); + + g_warning ("Unable to load %s, missing strings", filename); + } + else + priv->rules = g_list_append (priv->rules, rule); + + g_key_file_free (file); +} + +static void +load_rules (MaximusBind *bind, const gchar *path) +{ + MaximusBindPrivate *priv; + GDir *dir; + const gchar *name; + + priv = bind->priv; + + dir = g_dir_open (path, 0, NULL); + + if (!dir) + return; + + while ((name = g_dir_read_name (dir))) + { + gchar *filename; + + filename= g_build_filename (path, name, NULL); + + create_rule (bind, filename); + + g_free (filename); + } + + + g_dir_close (dir); +} + +static void +maximus_bind_finalize (GObject *obj) +{ + MaximusBind *bind = MAXIMUS_BIND (obj); + MaximusBindPrivate *priv; + GList *r; + + g_return_if_fail (MAXIMUS_IS_BIND (bind)); + priv = bind->priv; + + for (r = priv->rules; r; r = r->next) + { + MaximusRule *rule = r->data; + + g_free (rule->wm_class); + g_free (rule->fullscreen); + g_free (rule->unfullscreen); + + g_slice_free (MaximusRule, rule); + } + g_free (priv->binding); + + G_OBJECT_CLASS (maximus_bind_parent_class)->finalize (obj); +} + +static void +maximus_bind_class_init (MaximusBindClass *klass) +{ + GObjectClass *obj_class = G_OBJECT_CLASS (klass); + + obj_class->finalize = maximus_bind_finalize; + + g_type_class_add_private (obj_class, sizeof (MaximusBindPrivate)); +} + +static void +maximus_bind_init (MaximusBind *bind) +{ + MaximusBindPrivate *priv; + MateConfClient *client = mateconf_client_get_default (); + GdkDisplay *display = gdk_display_get_default (); + MatewnckScreen *screen; + + priv = bind->priv = MAXIMUS_BIND_GET_PRIVATE (bind); + + priv->fk = fakekey_init (GDK_DISPLAY_XDISPLAY (display)); + priv->screen = screen = matewnck_screen_get_default (); + priv->rules = NULL; + + tomboy_keybinder_init (); + + mateconf_client_add_dir (client, BIND_PATH, MATECONF_CLIENT_PRELOAD_NONE, NULL); + + priv->binding = mateconf_client_get_string (client, + BIND_EXCLUDE_CLASS, + NULL); + mateconf_client_notify_add (client, BIND_EXCLUDE_CLASS, + (MateConfClientNotifyFunc)on_binding_changed, + bind, NULL, NULL); + + if (binding_is_valid (priv->binding)) + tomboy_keybinder_bind (priv->binding, + (TomboyBindkeyHandler)on_binding_activated, + bind); + + load_rules (bind, SYSRULESDIR); +} + +MaximusBind * +maximus_bind_get_default (void) + +{ + static MaximusBind *bind = NULL; + + if (!bind) + bind = g_object_new (MAXIMUS_TYPE_BIND, + NULL); + + return bind; +} |