diff options
Diffstat (limited to 'maximus/maximus-app.c')
-rw-r--r-- | maximus/maximus-app.c | 577 |
1 files changed, 577 insertions, 0 deletions
diff --git a/maximus/maximus-app.c b/maximus/maximus-app.c new file mode 100644 index 0000000..be9a2fc --- /dev/null +++ b/maximus/maximus-app.c @@ -0,0 +1,577 @@ +/* + * 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 <unistd.h> + +#include <gtk/gtk.h> +#include <gdk/gdkx.h> + +#include <mateconf/mateconf.h> +#include <mateconf/mateconf-client.h> + +#include "maximus-app.h" +#include "maximus-bind.h" +#include "xutils.h" + +G_DEFINE_TYPE (MaximusApp, maximus_app, G_TYPE_OBJECT); + +#define MAXIMUS_APP_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj),\ + MAXIMUS_TYPE_APP, \ + MaximusAppPrivate)) + +/* Gconf keys */ +#define APP_PATH "/apps/maximus" +#define APP_EXCLUDE_CLASS APP_PATH "/exclude_class" +#define APP_UNDECORATE APP_PATH "/undecorate" +#define APP_NO_MAXIMIZE APP_PATH "/no_maximize" + +/* A set of default exceptions */ +static gchar *default_exclude_classes[] = +{ + "Apport-gtk", + "Bluetooth-properties", + "Bluetooth-wizard", + "Download", /* Firefox Download Window */ + "Ekiga", + "Extension", /* Firefox Add-Ons/Extension Window */ + "Gcalctool", + "Gimp", + "Global", /* Firefox Error Console Window */ + "Mate-dictionary", + "Mate-language-selector", + "Mate-nettool", + "Mate-volume-control", + "Kiten", + "Kmplot", + "Nm-editor", + "Pidgin", + "Polkit-mate-authorization", + "Update-manager", + "Skype", + "Toplevel", /* Firefox "Clear Private Data" Window */ + "Transmission" +}; + +struct _MaximusAppPrivate +{ + MaximusBind *bind; + MatewnckScreen *screen; + + GSList *exclude_class_list; + gboolean undecorate; + gboolean no_maximize; +}; + +static GQuark was_decorated = 0; + +/* <TAKEN FROM GDK> */ +typedef struct { + unsigned long flags; + unsigned long functions; + unsigned long decorations; + long input_mode; + unsigned long status; +} MotifWmHints, MwmHints; + +#define MWM_HINTS_FUNCTIONS (1L << 0) +#define MWM_HINTS_DECORATIONS (1L << 1) +#define _XA_MOTIF_WM_HINTS "_MOTIF_WM_HINTS" + +static gboolean +matewnck_window_is_decorated (MatewnckWindow *window) +{ + GdkDisplay *display = gdk_display_get_default(); + Atom hints_atom = None; + guchar *data = NULL; + MotifWmHints *hints = NULL; + Atom type = None; + gint format; + gulong nitems; + gulong bytes_after; + gboolean retval; + + g_return_val_if_fail (MATEWNCK_IS_WINDOW (window), FALSE); + + hints_atom = gdk_x11_get_xatom_by_name_for_display (display, + _XA_MOTIF_WM_HINTS); + + XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), + matewnck_window_get_xid (window), + hints_atom, 0, sizeof (MotifWmHints)/sizeof (long), + False, AnyPropertyType, &type, &format, &nitems, + &bytes_after, &data); + + if (type == None || !data) return TRUE; + + hints = (MotifWmHints *)data; + + retval = hints->decorations; + + if (data) + XFree (data); + + return retval; +} + +static void +gdk_window_set_mwm_hints (MatewnckWindow *window, + MotifWmHints *new_hints) +{ + GdkDisplay *display = gdk_display_get_default(); + Atom hints_atom = None; + guchar *data = NULL; + MotifWmHints *hints = NULL; + Atom type = None; + gint format; + gulong nitems; + gulong bytes_after; + + g_return_if_fail (MATEWNCK_IS_WINDOW (window)); + g_return_if_fail (GDK_IS_DISPLAY (display)); + + hints_atom = gdk_x11_get_xatom_by_name_for_display (display, + _XA_MOTIF_WM_HINTS); + + XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), + matewnck_window_get_xid (window), + hints_atom, 0, sizeof (MotifWmHints)/sizeof (long), + False, AnyPropertyType, &type, &format, &nitems, + &bytes_after, &data); + + if (type != hints_atom || !data) + hints = new_hints; + else + { + hints = (MotifWmHints *)data; + + if (new_hints->flags & MWM_HINTS_FUNCTIONS) + { + hints->flags |= MWM_HINTS_FUNCTIONS; + hints->functions = new_hints->functions; + } + if (new_hints->flags & MWM_HINTS_DECORATIONS) + { + hints->flags |= MWM_HINTS_DECORATIONS; + hints->decorations = new_hints->decorations; + } + } + + _matewnck_error_trap_push (); + XChangeProperty (GDK_DISPLAY_XDISPLAY (display), + matewnck_window_get_xid (window), + hints_atom, hints_atom, 32, PropModeReplace, + (guchar *)hints, sizeof (MotifWmHints)/sizeof (long)); + gdk_flush (); + _matewnck_error_trap_pop (); + + if (data) + XFree (data); +} + +static void +_window_set_decorations (MatewnckWindow *window, + GdkWMDecoration decorations) +{ + MotifWmHints *hints; + + g_return_if_fail (MATEWNCK_IS_WINDOW (window)); + + /* initialize to zero to avoid writing uninitialized data to socket */ + hints = g_slice_new0 (MotifWmHints); + hints->flags = MWM_HINTS_DECORATIONS; + hints->decorations = decorations; + + gdk_window_set_mwm_hints (window, hints); + + g_slice_free (MotifWmHints, hints); +} + +/* </TAKEN FROM GDK> */ + +gboolean +window_is_too_large_for_screen (MatewnckWindow *window) +{ + static GdkScreen *screen = NULL; + gint x=0, y=0, w=0, h=0; + + g_return_val_if_fail (MATEWNCK_IS_WINDOW (window), FALSE); + + if (screen == NULL) + screen = gdk_screen_get_default (); + + matewnck_window_get_geometry (window, &x, &y, &w, &h); + + /* some wiggle room */ + return (screen && + (w > (gdk_screen_get_width (screen) + 20) || + h > (gdk_screen_get_height (screen)+20))); +} + +static gboolean +on_window_maximised_changed (MatewnckWindow *window) +{ + g_return_val_if_fail (MATEWNCK_IS_WINDOW (window), FALSE); + + if (window_is_too_large_for_screen (window)) + { + _window_set_decorations (window, 1); + matewnck_window_unmaximize (window); + } + else + { + _window_set_decorations (window, 0); + } + return FALSE; +} + +static gboolean +enable_window_decorations (MatewnckWindow *window) +{ + g_return_val_if_fail (MATEWNCK_IS_WINDOW (window), FALSE); + + _window_set_decorations (window, 1); + return FALSE; +} + +static void +on_window_state_changed (MatewnckWindow *window, + MatewnckWindowState change_mask, + MatewnckWindowState new_state, + MaximusApp *app) +{ + g_return_if_fail (MATEWNCK_IS_WINDOW (window)); + + if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window), "exclude"))==1) + return; + + if (change_mask & MATEWNCK_WINDOW_STATE_MAXIMIZED_HORIZONTALLY + || change_mask & MATEWNCK_WINDOW_STATE_MAXIMIZED_VERTICALLY) + { + if (matewnck_window_is_maximized (window) && app->priv->undecorate) + { + g_idle_add ((GSourceFunc)on_window_maximised_changed, window); + } + else + { + g_idle_add ((GSourceFunc)enable_window_decorations, window); + } + } +} + +static gboolean +is_excluded (MaximusApp *app, MatewnckWindow *window) +{ + MaximusAppPrivate *priv; + MatewnckWindowType type; + MatewnckWindowActions actions; + gchar *res_name; + gchar *class_name; + GSList *c; + gint i; + + g_return_val_if_fail (MAXIMUS_IS_APP (app), TRUE); + g_return_val_if_fail (MATEWNCK_IS_WINDOW (window), TRUE); + priv = app->priv; + + type = matewnck_window_get_window_type (window); + if (type != MATEWNCK_WINDOW_NORMAL) + return TRUE; + + if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window), "exclude"))==1) + return TRUE; + + /* Ignore if the window is already fullscreen */ + if (matewnck_window_is_fullscreen (window)) + { + g_debug ("Excluding (is fullscreen): %s\n",matewnck_window_get_name (window)); + return TRUE; + } + + /* Make sure the window supports maximising */ + actions = matewnck_window_get_actions (window); + if (actions & MATEWNCK_WINDOW_ACTION_RESIZE + && actions & MATEWNCK_WINDOW_ACTION_MAXIMIZE_HORIZONTALLY + && actions & MATEWNCK_WINDOW_ACTION_MAXIMIZE_VERTICALLY + && actions & MATEWNCK_WINDOW_ACTION_MAXIMIZE) + ; /* Is good to maximise */ + else + return TRUE; + + _matewnck_get_wmclass (matewnck_window_get_xid (window), &res_name, &class_name); + + g_debug ("Window opened: res_name=%s -- class_name=%s", res_name, class_name); + + /* Check internal list of class_ids */ + for (i = 0; i < G_N_ELEMENTS (default_exclude_classes); i++) + { + if ((class_name && default_exclude_classes[i] + && strstr (class_name, default_exclude_classes[i])) + || (res_name && default_exclude_classes[i] && strstr (res_name, + default_exclude_classes[i]))) + { + g_debug ("Excluding: %s\n", matewnck_window_get_name (window)); + return TRUE; + } + } + + /* Check user list */ + for (c = priv->exclude_class_list; c; c = c->next) + { + if ((class_name && c->data && strstr (class_name, c->data)) + || (res_name && c->data && strstr (res_name, c->data) )) + { + g_debug ("Excluding: %s\n", matewnck_window_get_name (window)); + return TRUE; + } + } + + g_free (res_name); + g_free (class_name); + return FALSE; +} + +extern gboolean no_maximize; + +static void +on_window_opened (MatewnckScreen *screen, + MatewnckWindow *window, + MaximusApp *app) +{ + MaximusAppPrivate *priv; + MatewnckWindowType type; + gint exclude = 0; + + g_return_if_fail (MAXIMUS_IS_APP (app)); + g_return_if_fail (MATEWNCK_IS_WINDOW (window)); + priv = app->priv; + + type = matewnck_window_get_window_type (window); + if (type != MATEWNCK_WINDOW_NORMAL) + return; + + /* Ignore undecorated windows */ + exclude = matewnck_window_is_decorated (window) ? 0 : 1; + if (matewnck_window_is_maximized (window)) + exclude = 0; + g_object_set_data (G_OBJECT (window), "exclude", GINT_TO_POINTER (exclude)); + + if (is_excluded (app, window)) + { + g_signal_connect (window, "state-changed", + G_CALLBACK (on_window_state_changed), app); + return; + } + + if (no_maximize || priv->no_maximize) + { + if (matewnck_window_is_maximized(window)) + { + _window_set_decorations (window, 0); + gdk_flush (); + } + g_signal_connect (window, "state-changed", + G_CALLBACK (on_window_state_changed), app); + return; + } + + if (priv->undecorate) + { + /* Only undecorate right now if the window is smaller than the screen */ + if (!window_is_too_large_for_screen (window)) + { + _window_set_decorations (window, 0); + gdk_flush (); + } + } + + matewnck_window_maximize (window); + + g_signal_connect (window, "state-changed", + G_CALLBACK (on_window_state_changed), app); +} + +static void +on_app_no_maximize_changed (MateConfClient *client, + guint cid, + MateConfEntry *entry, + MaximusApp *app) +{ + MaximusAppPrivate *priv; + MateConfValue* value; + + g_return_if_fail (MAXIMUS_IS_APP (app)); + priv = app->priv; + + if (entry == NULL) + { + priv->no_maximize = FALSE; + } + else + { + value = mateconf_entry_get_value(entry); + priv->no_maximize = value != NULL && mateconf_value_get_bool(value); + } +} + +/* MateConf Callbacks */ +static void +on_exclude_class_changed (MateConfClient *client, + guint cid, + MateConfEntry *entry, + MaximusApp *app) +{ + MaximusAppPrivate *priv; + + g_return_if_fail (MAXIMUS_IS_APP (app)); + priv = app->priv; + + if (priv->exclude_class_list) + g_slist_free (priv->exclude_class_list); + + priv->exclude_class_list= mateconf_client_get_list (client, + APP_EXCLUDE_CLASS, + MATECONF_VALUE_STRING, + NULL); +} + +static gboolean +show_desktop (MatewnckScreen *screen) +{ + g_return_val_if_fail (MATEWNCK_IS_SCREEN (screen), FALSE); + + matewnck_screen_toggle_showing_desktop (screen, TRUE); + return FALSE; +} + +static void +on_app_undecorate_changed (MateConfClient *client, + guint cid, + MateConfEntry *entry, + MaximusApp *app) +{ + MaximusAppPrivate *priv; + GList *windows, *w; + + g_return_if_fail (MAXIMUS_IS_APP (app)); + priv = app->priv; + g_return_if_fail (MATEWNCK_IS_SCREEN (priv->screen)); + + priv->undecorate = mateconf_client_get_bool (client, + APP_UNDECORATE, + NULL); + g_debug ("%s\n", priv->undecorate ? "Undecorating" : "Decorating"); + + windows = matewnck_screen_get_windows (priv->screen); + for (w = windows; w; w = w->next) + { + MatewnckWindow *window = w->data; + + if (!MATEWNCK_IS_WINDOW (window)) + continue; + + if (no_maximize || priv->no_maximize) + { + if (!matewnck_window_is_maximized(window)) + continue; + } + + if (!is_excluded (app, window)) + { + gdk_error_trap_push (); + _window_set_decorations (window, priv->undecorate ? 0 : 1); + matewnck_window_unmaximize (window); + matewnck_window_maximize (window); + gdk_flush (); + gdk_error_trap_pop (); + + sleep (1); + } + } + /* We want the user to be left on the launcher/desktop after switching modes*/ + g_timeout_add_seconds (1, (GSourceFunc)show_desktop, priv->screen); +} + + +/* GObject stuff */ +static void +maximus_app_class_init (MaximusAppClass *klass) +{ + GObjectClass *obj_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (obj_class, sizeof (MaximusAppPrivate)); +} + +static void +maximus_app_init (MaximusApp *app) +{ + MaximusAppPrivate *priv; + MateConfClient *client = mateconf_client_get_default (); + MatewnckScreen *screen; + + priv = app->priv = MAXIMUS_APP_GET_PRIVATE (app); + + priv->bind = maximus_bind_get_default (); + + was_decorated = g_quark_from_static_string ("was-decorated"); + + mateconf_client_add_dir (client, APP_PATH, MATECONF_CLIENT_PRELOAD_NONE, NULL); + + priv->exclude_class_list= mateconf_client_get_list (client, + APP_EXCLUDE_CLASS, + MATECONF_VALUE_STRING, + NULL); + mateconf_client_notify_add (client, APP_EXCLUDE_CLASS, + (MateConfClientNotifyFunc)on_exclude_class_changed, + app, NULL, NULL); + + priv->undecorate = mateconf_client_get_bool(client, + APP_UNDECORATE, + NULL); + mateconf_client_notify_add (client, APP_UNDECORATE, + (MateConfClientNotifyFunc)on_app_undecorate_changed, + app, NULL, NULL); + + + priv->screen = screen = matewnck_screen_get_default (); + g_signal_connect (screen, "window-opened", + G_CALLBACK (on_window_opened), app); + + priv->no_maximize = mateconf_client_get_bool(client, + APP_NO_MAXIMIZE, + NULL); + g_print ("no maximize: %s\n", priv->no_maximize ? "true" : "false"); + mateconf_client_notify_add (client, APP_NO_MAXIMIZE, + (MateConfClientNotifyFunc)on_app_no_maximize_changed, + app, NULL, NULL); +} + +MaximusApp * +maximus_app_get_default (void) + +{ + static MaximusApp *app = NULL; + + if (!app) + app = g_object_new (MAXIMUS_TYPE_APP, + NULL); + + return app; +} |