From 07c1003905c91714c22c2d5014a89df8581ac696 Mon Sep 17 00:00:00 2001 From: Tomas Frydrych Date: Fri, 24 Oct 2008 10:07:24 +0100 Subject: [PATCH] Live previews for hidden windows. ClutterActors for hidden windows (such windows on different than active workspaces and windows that are minimized) are available, and reflect the actual state of the window. This is intended for use in task-switchers etc. This feature is disabled by default (due to increased demand on resources), and can be enabled through the metacity/general/live_hidden_windows gconf key. A trivial sample window switcher is included in the scratch plugin (activated by clicking on the slide out panel). --- src/Makefile.am | 2 + src/compositor/compositor-private.h | 3 + src/compositor/compositor.c | 9 + src/compositor/mutter/compositor-mutter.c | 614 +++++++--- src/compositor/mutter/mutter-plugin-manager.c | 10 + src/compositor/mutter/plugins/scratch.c | 239 +++- src/compositor/mutter/tidy/tidy-grid.c | 1000 +++++++++++++++++ src/compositor/mutter/tidy/tidy-grid.h | 99 ++ src/core/prefs.c | 39 + src/core/screen.c | 11 + src/core/stack.c | 12 +- src/core/window-private.h | 10 +- src/core/window.c | 118 +- src/core/workspace-private.h | 5 - src/core/workspace.c | 14 +- src/include/compositor-mutter.h | 12 +- src/include/compositor.h | 5 + src/include/mutter-plugin.h | 3 + src/include/prefs.h | 7 +- src/include/window.h | 6 + src/include/workspace.h | 4 + src/metacity.schemas.in | 16 + 22 files changed, 2017 insertions(+), 221 deletions(-) create mode 100644 src/compositor/mutter/tidy/tidy-grid.c create mode 100644 src/compositor/mutter/tidy/tidy-grid.h diff --git a/src/Makefile.am b/src/Makefile.am index 61632df5f..1d93c6017 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -112,6 +112,8 @@ metacity_SOURCES += \ compositor/mutter/mutter-plugin-manager.h \ compositor/mutter/tidy/tidy-texture-frame.c \ compositor/mutter/tidy/tidy-texture-frame.h \ + compositor/mutter/tidy/tidy-grid.c \ + compositor/mutter/tidy/tidy-grid.h \ include/mutter-plugin.h endif diff --git a/src/compositor/compositor-private.h b/src/compositor/compositor-private.h index 037235416..8b99d851b 100644 --- a/src/compositor/compositor-private.h +++ b/src/compositor/compositor-private.h @@ -78,6 +78,9 @@ struct _MetaCompositor MetaWorkspace *from, MetaWorkspace *to, MetaMotionDirection direction); + + void (*ensure_stack_order) (MetaCompositor *compositor, + MetaScreen *screen); }; #endif diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c index a12245985..4245de065 100644 --- a/src/compositor/compositor.c +++ b/src/compositor/compositor.c @@ -242,3 +242,12 @@ meta_compositor_switch_workspace (MetaCompositor *compositor, #endif } +void +meta_compositor_ensure_stack_order (MetaCompositor *compositor, + MetaScreen *screen) +{ +#ifdef HAVE_COMPOSITE_EXTENSIONS + if (compositor && compositor->ensure_stack_order) + compositor->ensure_stack_order (compositor, screen); +#endif +} diff --git a/src/compositor/mutter/compositor-mutter.c b/src/compositor/mutter/compositor-mutter.c index dc907373a..14f3cca8f 100644 --- a/src/compositor/mutter/compositor-mutter.c +++ b/src/compositor/mutter/compositor-mutter.c @@ -1,3 +1,5 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + #define _GNU_SOURCE #define _XOPEN_SOURCE 500 /* for usleep() */ @@ -10,6 +12,7 @@ #include +#include "../../core/window-private.h" #include "display.h" #include "screen.h" #include "frame.h" @@ -20,6 +23,7 @@ #include "mutter-plugin-manager.h" #include "tidy/tidy-texture-frame.h" #include "xprops.h" +#include "prefs.h" #include "mutter-shaped-texture.h" #include #include @@ -46,6 +50,16 @@ #define TILE_WIDTH (3*MAX_TILE_SZ) #define TILE_HEIGHT (3*MAX_TILE_SZ) +#define CHECK_LIST_INTEGRITY_START(list) \ + {int len2__; int len__ = g_list_length(list); + +#define CHECK_LIST_INTEGRITY_END(list) \ + len2__ = g_list_length(list); \ + if (len__ != len2__) \ + g_warning ("Integrity check of list failed at %s:%d\n", \ + __FILE__, __LINE__); } + + /* * Register GType wrapper for XWindowAttributes, so we do not have to * query window attributes in the MutterWindow constructor but can pass @@ -447,7 +461,7 @@ find_window_for_screen (MetaScreen *screen, Window xwindow) MetaCompScreen *info = meta_screen_get_compositor_data (screen); if (info == NULL) - return NULL; + return NULL; return g_hash_table_lookup (info->windows_by_xid, (gpointer) xwindow); } @@ -690,6 +704,18 @@ mutter_window_get_x_window (MutterWindow *mcw) return mcw->priv->xwindow; } +MetaWindow * +mutter_window_get_meta_window (MutterWindow *mcw) +{ + return mcw->priv->window; +} + +ClutterActor * +mutter_window_get_texture (MutterWindow *mcw) +{ + return mcw->priv->actor; +} + MetaCompWindowType mutter_window_get_window_type (MutterWindow *mcw) { @@ -699,6 +725,15 @@ mutter_window_get_window_type (MutterWindow *mcw) return mcw->priv->type; } +gboolean +mutter_window_is_override_redirect (MutterWindow *mcw) +{ + if (!mcw->priv->window) + return TRUE; + + return FALSE; +} + gint mutter_window_get_workspace (MutterWindow *mcw) { @@ -718,6 +753,23 @@ mutter_window_get_workspace (MutterWindow *mcw) return meta_workspace_index (workspace); } +gboolean +mutter_window_is_hidden (MutterWindow *mcw) +{ + MutterWindowPrivate *priv; + + if (!mcw) + return TRUE; + + priv = mcw->priv; + + if (!priv->window) + return FALSE; + + return meta_window_is_hidden (priv->window); +} + + static void repair_win (MutterWindow *cw); static void map_win (MutterWindow *cw); static void unmap_win (MutterWindow *cw); @@ -726,54 +778,83 @@ static void mutter_finish_workspace_switch (MetaCompScreen *info) { GList *last = g_list_last (info->windows); - GList *l = last; + GList *l; + +/* printf ("FINISHING DESKTOP SWITCH\n"); */ + + if (!meta_prefs_get_live_hidden_windows ()) + { + /* When running in the traditional mode where hidden windows get + * unmapped, we need to fix up the map status for each window, since + * we are ignoring unmap requests during the effect. + */ + l = last; + + while (l) + { + MutterWindow *cw = l->data; + MutterWindowPrivate *priv = cw->priv; + + if (priv->needs_map && !priv->needs_unmap) + { + map_win (cw); + } + + if (priv->needs_unmap) + { + unmap_win (cw); + } + + l = l->prev; + } + } + + /* + * Fix up stacking order in case the plugin messed it up. + */ + l = last; while (l) { - MutterWindow *cw = l->data; - MutterWindowPrivate *priv = cw->priv; + ClutterActor *a = l->data; + MutterWindow *mw = l->data; + MetaWindow *window = mw->priv->window; - if (priv->needs_map && !priv->needs_unmap) + /* + * If this window is not marked as hidden, we raise it. + * If it has no MetaWindow associated (i.e., override redirect), we + * raise it too. Everything else we push to the bottom. + */ + if (!window || !meta_window_is_hidden (window)) { - map_win (cw); +#if 0 + printf ("raising %p [0x%x] (%s) to top\n", + a, + (guint)mw->priv->xwindow, + mw->priv->window ? mw->priv->window->desc : "unknown"); +#endif + clutter_actor_raise_top (a); } - - if (priv->needs_unmap) + else { - unmap_win (cw); +#if 0 + printf ("lowering %p [0x%x] (%s) to bottom\n", + a, + (guint)mw->priv->xwindow, + mw->priv->window ? mw->priv->window->desc : "unknown"); +#endif + clutter_actor_lower_bottom (a); } l = l->prev; } - /* - * Now fix up stacking order in case the plugin messed it up. - */ - l = last; - while (l) - { - ClutterActor *a = l->data; - GList *prev = l->prev; +/* printf ("... FINISHED DESKTOP SWITCH\n"); */ - if (prev) - { - ClutterActor *above_me = prev->data; - - clutter_actor_raise (above_me, a); - } - else - { - ClutterActor *a = l->data; - clutter_actor_raise_top (a); - } - - l = prev; - } } void -mutter_window_effect_completed (MutterWindow *cw, - gulong event) +mutter_window_effect_completed (MutterWindow *cw, gulong event) { MutterWindowPrivate *priv = cw->priv; MetaScreen *screen = priv->screen; @@ -784,8 +865,7 @@ mutter_window_effect_completed (MutterWindow *cw, { case MUTTER_PLUGIN_MINIMIZE: { - ClutterActor *a = CLUTTER_ACTOR (cw); - gint height = clutter_actor_get_height (a); + ClutterActor *a = CLUTTER_ACTOR (cw); priv->minimize_in_progress--; if (priv->minimize_in_progress < 0) @@ -797,7 +877,19 @@ mutter_window_effect_completed (MutterWindow *cw, if (!priv->minimize_in_progress) { priv->is_minimized = TRUE; - clutter_actor_set_position (a, 0, -height); + + /* + * We must ensure that the minimized actor is pushed down the stack + * (the XConfigureEvent has 'above' semantics, i.e., when a window + * is lowered, we get a bunch of 'raise' notifications, but might + * not get any notification for the window that has been lowered. + */ + clutter_actor_lower_bottom (a); + + /* Make sure that after the effect finishes, the actor is + * made visible for sake of live previews. + */ + clutter_actor_show (a); } } break; @@ -911,16 +1003,51 @@ mutter_window_detach (MutterWindow *self) } static void -destroy_win (MetaDisplay *display, Window xwindow) +destroy_win (MutterWindow *cw, gboolean no_effect) { - MutterWindow *cw; + MetaCompScreen *info; + MutterWindowPrivate *priv; + MetaScreen *screen; - cw = find_window_in_display (display, xwindow); - - if (cw == NULL) + if (!cw) return; - clutter_actor_destroy (CLUTTER_ACTOR (cw)); + priv = cw->priv; + + screen = priv->screen; + info = meta_screen_get_compositor_data (screen); + + + /* + * We remove the window from internal lookup hashes and thus any other + * unmap events etc fail + */ + info->windows = g_list_remove (info->windows, (gconstpointer) cw); + g_hash_table_remove (info->windows_by_xid, (gpointer)priv->xwindow); + + if (no_effect || priv->type == META_COMP_WINDOW_OVERRIDE) + { + /* + * No effects, just kill it. + */ + clutter_actor_destroy (CLUTTER_ACTOR (cw)); + return; + } + + /* + * If a plugin manager is present, try to run an effect; if no effect of this + * type is present, destroy the actor. + */ + priv->destroy_in_progress++; + + if (!info->plugin_mgr || + !mutter_plugin_manager_event_simple (info->plugin_mgr, + cw, + MUTTER_PLUGIN_DESTROY)) + { + priv->destroy_in_progress--; + clutter_actor_destroy (CLUTTER_ACTOR (cw)); + } } static void @@ -931,6 +1058,13 @@ restack_win (MutterWindow *cw, Window above) MetaCompScreen *info = meta_screen_get_compositor_data (screen); Window previous_above; GList *sibling, *next; + gboolean hide = FALSE; + gboolean live_mode; + + live_mode = meta_prefs_get_live_hidden_windows (); + + if (priv->window && meta_window_is_hidden (priv->window)) + hide = TRUE; sibling = g_list_find (info->windows, (gconstpointer) cw); next = g_list_next (sibling); @@ -947,45 +1081,135 @@ restack_win (MutterWindow *cw, Window above) */ if (above == None) { +#if 0 + printf ("Raising to top %p [0x%x] (%s)\n", + cw, (guint) priv->xwindow, + priv->window ? priv->window->desc : "unknown"); +#endif /* Insert at bottom of window stack */ + CHECK_LIST_INTEGRITY_START(info->windows) info->windows = g_list_delete_link (info->windows, sibling); info->windows = g_list_append (info->windows, cw); + CHECK_LIST_INTEGRITY_END(info->windows) if (!info->switch_workspace_in_progress) - clutter_actor_raise_top (CLUTTER_ACTOR (cw)); + { + clutter_actor_raise_top (CLUTTER_ACTOR (cw)); + } } else if (previous_above != above) { GList *index; - for (index = info->windows; index; index = index->next) + /* Find the window that matches 'above'; if the window is hidden (i.e., + * minimized, on a different desktop) find the first window above it tha + * is not and use it instead; if we cannot find any such window then + * fallback to raise to top (without this, we end up stacking up the act + * in the wrong place, and it probably will not be visible at all). + */ + for (index = info->windows; index; index = index->next) { MutterWindow *cw2 = (MutterWindow *) index->data; if (cw2->priv->xwindow == above) - break; + { + if (live_mode && !hide && cw2->priv->window && + meta_window_is_hidden (cw2->priv->window)) + { + index = index->prev; + + while (index) + { + MutterWindow *prev = index->data; + + if (!prev->priv->window || + !meta_window_is_hidden (prev->priv->window)) + { + break; + } + + index = index->prev; + } + } + break; + } } if (index != NULL) { - ClutterActor *above_win = index->data; + if (index != sibling) + { + ClutterActor *above_win = index->data; + MutterWindow *cw2 = index->data; - info->windows = g_list_delete_link (info->windows, sibling); - info->windows = g_list_insert_before (info->windows, index, cw); + CHECK_LIST_INTEGRITY_START(info->windows) + info->windows = g_list_delete_link (info->windows, sibling); - if (!info->switch_workspace_in_progress) - clutter_actor_raise (CLUTTER_ACTOR (cw), above_win); + info->windows = g_list_insert_before (info->windows, index, cw); + CHECK_LIST_INTEGRITY_END(info->windows) + +#if 0 + printf ("Raising %p [0x%x] (%s) hidden %d, above %p [0x%x] (%s)\n", + cw, (guint) priv->xwindow, + priv->window ? priv->window->desc : "unknown", hide, + cw2, (guint) cw2->priv->xwindow, + cw2->priv->window ? cw2->priv->window->desc : "unknown"); +#endif + if (!info->switch_workspace_in_progress) + { + clutter_actor_raise (CLUTTER_ACTOR (cw), above_win); + } + } } + else if (live_mode) + { + if (!hide) + { +#if 0 + printf ("Raising to top as fallback %p [0x%x] (%s)\n", + cw, (guint) priv->xwindow, + priv->window ? priv->window->desc : "unknown"); +#endif + /* Insert at bottom of window stack */ + CHECK_LIST_INTEGRITY_START(info->windows) + info->windows = g_list_delete_link (info->windows, sibling); + info->windows = g_list_append (info->windows, cw); + CHECK_LIST_INTEGRITY_END(info->windows) + + if (!info->switch_workspace_in_progress) + { + clutter_actor_raise_top (CLUTTER_ACTOR (cw)); + } + } + else + { +#if 0 + printf ("Lowering to bottom as fallback %p [0x%x] (%s)\n", + cw, (guint) priv->xwindow, + priv->window ? priv->window->desc : "unknown"); +#endif + /* Insert at bottom of window stack */ + CHECK_LIST_INTEGRITY_START(info->windows) + info->windows = g_list_delete_link (info->windows, sibling); + info->windows = g_list_prepend (info->windows, cw); + CHECK_LIST_INTEGRITY_END(info->windows) + + if (!info->switch_workspace_in_progress) + { + clutter_actor_lower_bottom (CLUTTER_ACTOR (cw)); + } + } + } } } static void resize_win (MutterWindow *cw, - int x, - int y, - int width, - int height, - int border_width, - gboolean override_redirect) + int x, + int y, + int width, + int height, + int border_width, + gboolean override_redirect) { MutterWindowPrivate *priv = cw->priv; @@ -1093,15 +1317,15 @@ unmap_win (MutterWindow *cw) } priv->attrs.map_state = IsUnmapped; - - if (!priv->minimize_in_progress) - { - ClutterActor *a = CLUTTER_ACTOR (cw); - clutter_actor_hide (a); - } - priv->needs_unmap = FALSE; priv->needs_map = FALSE; + + if (!priv->minimize_in_progress && + (!meta_prefs_get_live_hidden_windows () || + priv->type == META_COMP_WINDOW_OVERRIDE)) + { + clutter_actor_hide (CLUTTER_ACTOR (cw)); + } } @@ -1114,6 +1338,7 @@ add_win (MetaScreen *screen, MetaWindow *window, Window xwindow) MutterWindowPrivate *priv; Display *xdisplay = meta_display_get_xdisplay (display); XWindowAttributes attrs; + gulong events_needed; if (info == NULL) return; @@ -1122,17 +1347,19 @@ add_win (MetaScreen *screen, MetaWindow *window, Window xwindow) return; if (!XGetWindowAttributes (xdisplay, xwindow, &attrs)) - return; + return; /* * If Metacity has decided not to manage this window then the input events * won't have been set on the window */ - if (!(attrs.your_event_mask & PropertyChangeMask)) + events_needed = PropertyChangeMask | SubstructureNotifyMask; + + if (!(attrs.your_event_mask & PropertyChangeMask) || + !(attrs.your_event_mask & SubstructureNotifyMask)) { gulong event_mask; - - event_mask = attrs.your_event_mask | PropertyChangeMask; + event_mask = attrs.your_event_mask | events_needed; XSelectInput (xdisplay, xwindow, event_mask); } @@ -1159,8 +1386,8 @@ add_win (MetaScreen *screen, MetaWindow *window, Window xwindow) info->dock_windows = g_slist_append (info->dock_windows, cw); } + meta_verbose ("added 0x%x (%p) type:", (guint)xwindow, cw); #if 0 - printf ("added 0x%x (%p) type:", (guint)xwindow, cw); switch (cw->type) { @@ -1335,8 +1562,8 @@ process_create (Mutter *compositor, MetaWindow *window) { MetaScreen *screen; - MutterWindow *cw; Window xwindow = event->window; + MutterWindow *mw; screen = meta_display_screen_for_root (compositor->display, event->parent); @@ -1347,18 +1574,18 @@ process_create (Mutter *compositor, * This is quite silly as we end up creating windows as then immediatly * destroying them as they (likely) become framed and thus reparented. */ - cw = find_window_for_screen (screen, event->window); + mw = find_window_for_screen (screen, xwindow); - if (!cw && window) + if (!mw && window) { xwindow = meta_window_get_xwindow (window); - cw = find_window_for_screen (screen, xwindow); + mw = find_window_for_screen (screen, xwindow); } - if (cw) + if (mw) { - destroy_win (compositor->display, xwindow); + destroy_win (mw, TRUE); } add_win (screen, window, event->window); @@ -1369,16 +1596,24 @@ process_reparent (Mutter *compositor, XReparentEvent *event, MetaWindow *window) { - MetaScreen *screen; + MetaScreen *screen; MutterWindow *mw; Window xwindow = event->window; + gboolean viewable = FALSE; screen = meta_display_screen_for_root (compositor->display, event->parent); if (!screen) return; - mw = find_window_for_screen (screen, event->window); + mw = find_window_for_screen (screen, xwindow); + + if (!mw && window) + { + xwindow = meta_window_get_xwindow (window); + + mw = find_window_for_screen (screen, xwindow); + } if (!mw && window) { @@ -1390,19 +1625,23 @@ process_reparent (Mutter *compositor, if (mw) { - meta_verbose ("reparent: destroying a window 0%x\n", - (guint)event->window); - destroy_win (compositor->display, xwindow); + viewable = (mw->priv->attrs.map_state == IsViewable); + destroy_win (mw, TRUE); } add_win (screen, window, event->window); } static void -process_destroy (Mutter *compositor, - XDestroyWindowEvent *event) +process_destroy (Mutter *compositor, XDestroyWindowEvent *event) { - destroy_win (compositor->display, event->window); + MutterWindow *cw = + find_window_in_display (compositor->display, event->window); + + if (!cw) + return; + + destroy_win (cw, FALSE); } static void @@ -1437,7 +1676,7 @@ process_damage (Mutter *compositor, if (XCheckTypedWindowEvent (dpy, drawable, DestroyNotify, &next)) { priv->destroy_pending = TRUE; - process_destroy (compositor, (XDestroyWindowEvent *) &next); + destroy_win (cw, FALSE); return; } @@ -1501,7 +1740,18 @@ process_configure_notify (Mutter *compositor, XConfigureEvent *event) { MetaDisplay *display = compositor->display; - MutterWindow *cw = find_window_in_display (display, event->window); + MutterWindow *cw; + + /* + * We get each configure event twice; once through the WM solicitation of the + * events on the root window, and once through solicitation on the window + * itself -- we will only handle the latter, in which case the event and + * window members of the config event are identical. + */ + if (event->event != event->window) + return; + + cw = find_window_in_display (display, event->window); if (cw) { @@ -1596,25 +1846,28 @@ process_unmap (Mutter *compositor, if (XCheckTypedWindowEvent (dpy, xwin, DestroyNotify, &next)) { priv->destroy_pending = TRUE; - process_destroy (compositor, (XDestroyWindowEvent *) &next); + destroy_win (cw, FALSE); return; } - meta_verbose ("processing unmap of 0x%x (%p)\n", (guint)xwin, cw); unmap_win (cw); } } static void -process_map (Mutter *compositor, - XMapEvent *event, - MetaWindow *window) +process_map (Mutter *compositor, + XMapEvent *event, + MetaWindow *window) { - MutterWindow *cw = find_window_in_display (compositor->display, - event->window); + MutterWindow *cw; + Window xwindow = event->window; + + cw = find_window_in_display (compositor->display, xwindow); if (cw) - map_win (cw); + { + map_win (cw); + } } static void @@ -1727,6 +1980,16 @@ mutter_get_overlay_group_for_screen (MetaScreen *screen) return info->overlay_group; } +GList * +mutter_get_windows (MetaScreen *screen) +{ + MetaCompScreen *info = meta_screen_get_compositor_data (screen); + + if (!info) + return NULL; + + return info->windows; +} static void clutter_cmp_manage_screen (MetaCompositor *compositor, @@ -1843,6 +2106,7 @@ clutter_cmp_add_window (MetaCompositor *compositor, MetaScreen *screen = meta_screen_for_x_screen (attrs->screen); meta_error_trap_push (xrc->display); + add_win (screen, window, xwindow); meta_error_trap_pop (xrc->display, FALSE); #endif @@ -1883,9 +2147,8 @@ clutter_cmp_process_event (MetaCompositor *compositor, screen = meta_window_get_screen (window); info = meta_screen_get_compositor_data (screen); - if (mutter_plugin_manager_xevent_filter - (info->plugin_mgr, - event) == TRUE) + if (mutter_plugin_manager_xevent_filter (info->plugin_mgr, + event) == TRUE) return; } else @@ -2006,44 +2269,20 @@ clutter_cmp_destroy_window (MetaCompositor *compositor, MetaWindow *window) { #ifdef HAVE_COMPOSITE_EXTENSIONS - MutterWindow *cw = NULL; - MetaScreen *screen = meta_window_get_screen (window); - MetaCompScreen *info = meta_screen_get_compositor_data (screen); - MetaFrame *f = meta_window_get_frame (window); - MutterWindowPrivate *priv; + MutterWindow *cw = NULL; + MetaScreen *screen = meta_window_get_screen (window); + MetaFrame *f = meta_window_get_frame (window); + Window xwindow; /* Chances are we actually get the window frame here */ - cw = find_window_for_screen (screen, - f ? meta_frame_get_xwindow (f) : - meta_window_get_xwindow (window)); + xwindow = f ? meta_frame_get_xwindow (f) : meta_window_get_xwindow (window); + + cw = find_window_for_screen (screen, xwindow); + if (!cw) return; - priv = cw->priv; - - /* - * We remove the window from internal lookup hashes and thus any other - * unmap events etc fail - */ - info->windows = g_list_remove (info->windows, (gconstpointer) cw); - g_hash_table_remove (info->windows_by_xid, - (gpointer) (f ? meta_frame_get_xwindow (f) : - meta_window_get_xwindow (window))); - - /* - * If a plugin manager is present, try to run an effect; if no effect of this - * type is present, destroy the actor. - */ - priv->destroy_in_progress++; - - if (!info->plugin_mgr || - !mutter_plugin_manager_event_simple (info->plugin_mgr, - cw, - MUTTER_PLUGIN_DESTROY)) - { - priv->destroy_in_progress--; - clutter_actor_destroy (CLUTTER_ACTOR (cw)); - } + destroy_win (cw, FALSE); #endif } @@ -2077,12 +2316,8 @@ clutter_cmp_minimize_window (MetaCompositor *compositor, MetaWindow *window) cw, MUTTER_PLUGIN_MINIMIZE)) { - ClutterActor *a = CLUTTER_ACTOR (cw); - gint height = clutter_actor_get_height (a); - cw->priv->is_minimized = TRUE; cw->priv->minimize_in_progress--; - clutter_actor_set_position (a, 0, -height); } #endif } @@ -2181,65 +2416,109 @@ clutter_cmp_switch_workspace (MetaCompositor *compositor, { #ifdef HAVE_COMPOSITE_EXTENSIONS MetaCompScreen *info; - GList *l; gint to_indx, from_indx; info = meta_screen_get_compositor_data (screen); to_indx = meta_workspace_index (to); from_indx = meta_workspace_index (from); - printf ("Direction of switch %d\n", direction); - - l = info->windows; - while (l) + if (!meta_prefs_get_live_hidden_windows ()) { - MutterWindow *cw = l->data; - MetaWindow *mw = cw->priv->window; - gboolean sticky; - gint workspace = -1; + /* + * We are in the traditional mode where hidden windows get unmapped, + * we need to pre-calculate the map status of each window so that once + * the effect finishes we can put everything into proper order + * (we need to ignore the map notifications during the effect so that + * actors do not just disappear while the effect is running). + */ + GList *l = info->windows; - sticky = (!mw || meta_window_is_on_all_workspaces (mw)); - - if (!sticky) + while (l) { - MetaWorkspace *w; + MutterWindow *cw = l->data; + MetaWindow *mw = cw->priv->window; + gboolean sticky; + gint workspace = -1; - w = meta_window_get_workspace (cw->priv->window); - workspace = meta_workspace_index (w); + sticky = (!mw || meta_window_is_on_all_workspaces (mw)); - /* - * If the window is not on the target workspace, mark it for - * unmap. - */ - if (to_indx != workspace) + if (!sticky) { - cw->priv->needs_unmap = TRUE; - } - else - { - cw->priv->needs_map = TRUE; - cw->priv->needs_unmap = FALSE; + MetaWorkspace *w; + + w = meta_window_get_workspace (cw->priv->window); + workspace = meta_workspace_index (w); + + /* + * If the window is not on the target workspace, mark it for + * unmap. + */ + if (to_indx != workspace) + { + cw->priv->needs_unmap = TRUE; + } + else + { + cw->priv->needs_map = TRUE; + cw->priv->needs_unmap = FALSE; + } } + + l = l->next; } - - l = l->next; } info->switch_workspace_in_progress++; if (!info->plugin_mgr || - !mutter_plugin_manager_switch_workspace ( - info->plugin_mgr, - (const GList **)&info->windows, - from_indx, - to_indx, - direction)) + !mutter_plugin_manager_switch_workspace (info->plugin_mgr, + (const GList **)&info->windows, + from_indx, + to_indx, + direction)) { info->switch_workspace_in_progress--; + + /* We have to explicitely call this to fix up stacking order of the + * actors; this is because the abs stacking position of actors does not + * necessarily change during the window hiding/unhiding, only their + * relative position toward the destkop window. + */ + mutter_finish_workspace_switch (info); } #endif } +static void +clutter_cmp_ensure_stack_order (MetaCompositor *compositor, + MetaScreen *screen) +{ + MetaCompScreen *info = meta_screen_get_compositor_data (screen); + GList *l = g_list_last (info->windows); + + while (l) + { + ClutterActor *a = l->data; + MutterWindow *mw = l->data; + MetaWindow *window = mw->priv->window; + + /* + * If this window is not marked as hidden, we raise it. + * If it has no MetaWindow associated (i.e., override redirect), we + * raise it too. Everything else we push to the bottom. + */ + if (!window || !meta_window_is_hidden (window)) + { + clutter_actor_raise_top (a); + } + else + { + clutter_actor_lower_bottom (a); + } + + l = l->prev; + } +} static MetaCompositor comp_info = { clutter_cmp_destroy, @@ -2256,7 +2535,8 @@ static MetaCompositor comp_info = { clutter_cmp_maximize_window, clutter_cmp_unmaximize_window, clutter_cmp_update_workspace_geometry, - clutter_cmp_switch_workspace + clutter_cmp_switch_workspace, + clutter_cmp_ensure_stack_order, }; MetaCompositor * diff --git a/src/compositor/mutter/mutter-plugin-manager.c b/src/compositor/mutter/mutter-plugin-manager.c index 2f4fbf037..698d7e41d 100644 --- a/src/compositor/mutter/mutter-plugin-manager.c +++ b/src/compositor/mutter/mutter-plugin-manager.c @@ -884,3 +884,13 @@ mutter_plugin_set_stage_input_area (MutterPlugin *plugin, XFixesSetWindowShapeRegion (xdpy, xstage, ShapeInput, 0, 0, region); XFixesSetWindowShapeRegion (xdpy, xoverlay, ShapeInput, 0, 0, region); } + +GList * +mutter_plugin_get_windows (MutterPlugin *plugin) +{ + MutterPluginPrivate *priv = plugin->manager_private; + MutterPluginManager *plugin_mgr = priv->self; + + return mutter_get_windows (plugin_mgr->screen); +} + diff --git a/src/compositor/mutter/plugins/scratch.c b/src/compositor/mutter/plugins/scratch.c index 1bc1cf777..72ea6c5f8 100644 --- a/src/compositor/mutter/plugins/scratch.c +++ b/src/compositor/mutter/plugins/scratch.c @@ -33,6 +33,12 @@ #include #include +#include "../tidy/tidy-grid.h" + +/* For debugging only */ +#include "../../../core/window-private.h" +#include "compositor-mutter.h" + #define DESTROY_TIMEOUT 250 #define MINIMIZE_TIMEOUT 250 #define MAXIMIZE_TIMEOUT 250 @@ -43,6 +49,10 @@ #define PANEL_SLIDE_THRESHOLD 2 #define PANEL_HEIGHT 40 #define ACTOR_DATA_KEY "MCCP-scratch-actor-data" + +#define SWITCHER_CELL_WIDTH 200 +#define SWITCHER_CELL_HEIGHT 200 + static GQuark actor_data_quark = 0; typedef struct PluginPrivate PluginPrivate; @@ -119,6 +129,8 @@ struct PluginPrivate ClutterActor *d_overlay ; /* arrow indicator */ ClutterActor *panel; + ClutterActor *switcher; + gboolean debug_mode : 1; gboolean panel_out : 1; gboolean panel_out_in_progress : 1; @@ -131,6 +143,8 @@ struct PluginPrivate struct ActorPrivate { ClutterActor *orig_parent; + gint orig_x; + gint orig_y; ClutterTimeline *tml_minimize; ClutterTimeline *tml_maximize; @@ -800,36 +814,225 @@ g_module_check_init (GModule *module) } #endif +static void switcher_clone_weak_notify (gpointer data, GObject *object); + +static void +switcher_origin_weak_notify (gpointer data, GObject *object) +{ + ClutterActor *clone = data; + + /* + * The original MutterWindow destroyed; remove the weak reference the + * we added to the clone referencing the original window, then + * destroy the clone. + */ + g_object_weak_unref (G_OBJECT (clone), switcher_clone_weak_notify, object); + clutter_actor_destroy (clone); +} + +static void +switcher_clone_weak_notify (gpointer data, GObject *object) +{ + ClutterActor *origin = data; + + /* + * Clone destroyed -- this function gets only called whent the clone + * is destroyed while the original MutterWindow still exists, so remove + * the weak reference we added on the origin for sake of the clone. + */ + g_object_weak_unref (G_OBJECT (origin), switcher_origin_weak_notify, object); +} + +static gboolean +switcher_clone_input_cb (ClutterActor *clone, + ClutterEvent *event, + gpointer data) +{ + MutterWindow *mw = data; + MetaWindow *window; + MetaWorkspace *workspace; + + printf ("Actor %p (%s) clicked\n", + clone, clutter_actor_get_name (clone)); + + window = mutter_window_get_meta_window (mw); + workspace = meta_window_get_workspace (window); + + meta_workspace_activate_with_focus (workspace, window, event->any.time); + + return FALSE; +} + +/* + * This is a simple example of how a switcher might access the windows. + * + * Note that we use ClutterCloneTexture hooked up to the texture *inside* + * MutterWindow (with FBO support, we could clone the entire MutterWindow, + * although for the switcher purposes that is probably not what is wanted + * anyway). + */ +static void +hide_switcher (void) +{ + MutterPlugin *plugin = get_plugin (); + PluginPrivate *priv = plugin->plugin_private; + + if (!priv->switcher) + return; + + clutter_actor_destroy (priv->switcher); + priv->switcher = NULL; +} + +static void +show_switcher (void) +{ + MutterPlugin *plugin = get_plugin (); + PluginPrivate *priv = plugin->plugin_private; + ClutterActor *stage; + GList *l; + ClutterActor *switcher; + TidyGrid *grid; + guint panel_height; + gint panel_y; + + switcher = tidy_grid_new (); + + grid = TIDY_GRID (switcher); + + tidy_grid_set_homogenous_rows (grid, TRUE); + tidy_grid_set_homogenous_columns (grid, TRUE); + tidy_grid_set_column_major (grid, TRUE); + tidy_grid_set_row_gap (grid, CLUTTER_UNITS_FROM_INT (10)); + tidy_grid_set_column_gap (grid, CLUTTER_UNITS_FROM_INT (10)); + + l = mutter_plugin_get_windows (plugin); + while (l) + { + MutterWindow *mw = l->data; + MetaCompWindowType type = mutter_window_get_window_type (mw); + ClutterActor *a = CLUTTER_ACTOR (mw); + ClutterActor *texture; + ClutterActor *clone; + guint w, h; + gdouble s_x, s_y, s; + + /* + * Only show regular windows. + */ + if (mutter_window_is_override_redirect (mw) || + type != META_COMP_WINDOW_NORMAL) + { + l = l->next; + continue; + } + +#if 0 + printf ("Adding %p:%s\n", + mw, + mutter_window_get_meta_window (mw) ? + mutter_window_get_meta_window (mw)->desc : "unknown"); +#endif + + texture = mutter_window_get_texture (mw); + clone = clutter_clone_texture_new (CLUTTER_TEXTURE (texture)); + + clutter_actor_set_name (clone, mutter_window_get_meta_window (mw)->desc); + g_signal_connect (clone, + "button-press-event", + G_CALLBACK (switcher_clone_input_cb), mw); + + g_object_weak_ref (G_OBJECT (mw), switcher_origin_weak_notify, clone); + g_object_weak_ref (G_OBJECT (clone), switcher_clone_weak_notify, mw); + + /* + * Scale clone to fit the predefined size of the grid cell + */ + clutter_actor_get_size (a, &w, &h); + s_x = (gdouble) SWITCHER_CELL_WIDTH / (gdouble) w; + s_y = (gdouble) SWITCHER_CELL_HEIGHT / (gdouble) h; + + s = s_x < s_y ? s_x : s_y; + + if (s_x < s_y) + clutter_actor_set_size (clone, + (guint)((gdouble)w * s_x), + (guint)((gdouble)h * s_x)); + else + clutter_actor_set_size (clone, + (guint)((gdouble)w * s_y), + (guint)((gdouble)h * s_y)); + + clutter_actor_set_reactive (clone, TRUE); + + clutter_container_add_actor (CLUTTER_CONTAINER (grid), clone); + l = l->next; + } + + if (priv->switcher) + hide_switcher (); + + priv->switcher = switcher; + + panel_height = clutter_actor_get_height (priv->panel); + panel_y = clutter_actor_get_y (priv->panel); + + clutter_actor_set_position (switcher, 10, panel_height + panel_y); + + stage = mutter_plugin_get_stage (plugin); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), switcher); +} + +static void +toggle_switcher () +{ + MutterPlugin *plugin = get_plugin (); + PluginPrivate *priv = plugin->plugin_private; + + if (priv->switcher) + hide_switcher (); + else + show_switcher (); +} + static gboolean stage_input_cb (ClutterActor *stage, ClutterEvent *event, gpointer data) { - if (event->type == CLUTTER_MOTION) + gboolean capture = GPOINTER_TO_INT (data); + + if ((capture && event->type == CLUTTER_MOTION) || + (!capture && event->type == CLUTTER_BUTTON_PRESS)) { - ClutterMotionEvent *mev = (ClutterMotionEvent *) event; + gint event_y; MutterPlugin *plugin = get_plugin (); PluginPrivate *priv = plugin->plugin_private; + if (event->type == CLUTTER_MOTION) + event_y = ((ClutterMotionEvent*)event)->y; + else + event_y = ((ClutterButtonEvent*)event)->y; + if (priv->panel_out_in_progress || priv->panel_back_in_progress) return FALSE; - if (priv->panel_out) + if (priv->panel_out && + (event->type == CLUTTER_BUTTON_PRESS || !priv->switcher)) { guint height = clutter_actor_get_height (priv->panel); gint x = clutter_actor_get_x (priv->panel); - if (mev->y > (gint)height) + if (event_y > (gint)height) { priv->panel_back_in_progress = TRUE; + clutter_effect_move (priv->panel_slide_effect, priv->panel, x, -height, on_panel_effect_complete, GINT_TO_POINTER (FALSE)); priv->panel_out = FALSE; } - - return FALSE; } - else if (mev->y < PANEL_SLIDE_THRESHOLD) + else if (event_y < PANEL_SLIDE_THRESHOLD) { gint x = clutter_actor_get_x (priv->panel); @@ -840,11 +1043,20 @@ stage_input_cb (ClutterActor *stage, ClutterEvent *event, gpointer data) GINT_TO_POINTER (TRUE)); priv->panel_out = TRUE; - - return FALSE; } + } + else if (event->type == CLUTTER_KEY_RELEASE) + { + ClutterKeyEvent *kev = (ClutterKeyEvent *) event; - return FALSE; + g_print ("*** key press event (key:%c) ***\n", + clutter_key_event_symbol (kev)); + + } + + if (!capture && (event->type == CLUTTER_BUTTON_PRESS)) + { + toggle_switcher (); } return FALSE; @@ -964,7 +1176,12 @@ do_init (const char *params) * children and do not interfere with their event processing. */ g_signal_connect (mutter_plugin_get_stage (plugin), - "captured-event", G_CALLBACK (stage_input_cb), NULL); + "captured-event", G_CALLBACK (stage_input_cb), + GINT_TO_POINTER (TRUE)); + + g_signal_connect (mutter_plugin_get_stage (plugin), + "button-press-event", G_CALLBACK (stage_input_cb), + GINT_TO_POINTER (FALSE)); mutter_plugin_set_stage_input_area (plugin, 0, 0, screen_width, 1); diff --git a/src/compositor/mutter/tidy/tidy-grid.c b/src/compositor/mutter/tidy/tidy-grid.c new file mode 100644 index 000000000..c5d788a5f --- /dev/null +++ b/src/compositor/mutter/tidy/tidy-grid.c @@ -0,0 +1,1000 @@ +/* tidy-grid.h: Reflowing grid layout container for clutter. + * + * Copyright (C) 2008 Intel Corporation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Written by: Øyvind Kolås + */ + +/* TODO: + * + * - Better names for properties. + * - Caching layouted positions? (perhaps needed for huge collections) + * - More comments / overall concept on how the layouting is done. + * - Allow more layout directions than just row major / column major. + */ + +#include +#include +#include + +#include "tidy-grid.h" + +typedef struct _TidyGridActorData TidyGridActorData; + +static void tidy_grid_dispose (GObject *object); +static void tidy_grid_finalize (GObject *object); + +static void tidy_grid_finalize (GObject *object); + +static void tidy_grid_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void tidy_grid_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +static void clutter_container_iface_init (ClutterContainerIface *iface); + +static void tidy_grid_real_add (ClutterContainer *container, + ClutterActor *actor); +static void tidy_grid_real_remove (ClutterContainer *container, + ClutterActor *actor); +static void tidy_grid_real_foreach (ClutterContainer *container, + ClutterCallback callback, + gpointer user_data); +static void tidy_grid_real_raise (ClutterContainer *container, + ClutterActor *actor, + ClutterActor *sibling); +static void tidy_grid_real_lower (ClutterContainer *container, + ClutterActor *actor, + ClutterActor *sibling); +static void +tidy_grid_real_sort_depth_order (ClutterContainer *container); + +static void +tidy_grid_free_actor_data (gpointer data); + +static void tidy_grid_paint (ClutterActor *actor); + +static void tidy_grid_pick (ClutterActor *actor, + const ClutterColor *color); + +static void +tidy_grid_get_preferred_width (ClutterActor *self, + ClutterUnit for_height, + ClutterUnit *min_width_p, + ClutterUnit *natural_width_p); + +static void +tidy_grid_get_preferred_height (ClutterActor *self, + ClutterUnit for_width, + ClutterUnit *min_height_p, + ClutterUnit *natural_height_p); + +static void tidy_grid_allocate (ClutterActor *self, + const ClutterActorBox *box, + gboolean absolute_origin_changed); + +G_DEFINE_TYPE_WITH_CODE (TidyGrid, tidy_grid, + CLUTTER_TYPE_ACTOR, + G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER, + clutter_container_iface_init)); + +#define TIDY_GRID_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TIDY_TYPE_GRID, \ + TidyGridPrivate)) + +struct _TidyGridPrivate +{ + ClutterUnit for_height, for_width; + ClutterUnit pref_width, pref_height; + ClutterUnit alloc_width, alloc_height; + + gboolean absolute_origin_changed; + GHashTable *hash_table; + GList *list; + + gboolean homogenous_rows; + gboolean homogenous_columns; + gboolean end_align; + ClutterUnit column_gap, row_gap; + gdouble valign, halign; + + gboolean column_major; + + gboolean first_of_batch; + ClutterUnit a_current_sum, a_wrap; + ClutterUnit max_extent_a; + ClutterUnit max_extent_b; +}; + +enum +{ + PROP_0, + PROP_HOMOGENOUS_ROWS, + PROP_HOMOGENOUS_COLUMNS, + PROP_ROW_GAP, + PROP_COLUMN_GAP, + PROP_VALIGN, + PROP_HALIGN, + PROP_END_ALIGN, + PROP_COLUMN_MAJOR, +}; + +struct _TidyGridActorData +{ + gboolean xpos_set, ypos_set; + ClutterUnit xpos, ypos; + ClutterUnit pref_width, pref_height; +}; + +static void +tidy_grid_class_init (TidyGridClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + ClutterActorClass *actor_class = (ClutterActorClass *) klass; + + gobject_class->dispose = tidy_grid_dispose; + gobject_class->finalize = tidy_grid_finalize; + + gobject_class->set_property = tidy_grid_set_property; + gobject_class->get_property = tidy_grid_get_property; + + actor_class->paint = tidy_grid_paint; + actor_class->pick = tidy_grid_pick; + actor_class->get_preferred_width = tidy_grid_get_preferred_width; + actor_class->get_preferred_height = tidy_grid_get_preferred_height; + actor_class->allocate = tidy_grid_allocate; + + g_type_class_add_private (klass, sizeof (TidyGridPrivate)); + + + g_object_class_install_property + (gobject_class, + PROP_ROW_GAP, + clutter_param_spec_unit ("row-gap", + "Row gap", + "gap between rows in the layout", + 0, CLUTTER_MAXUNIT, + 0, + G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); + + g_object_class_install_property + (gobject_class, + PROP_COLUMN_GAP, + clutter_param_spec_unit ("column-gap", + "Column gap", + "gap between columns in the layout", + 0, CLUTTER_MAXUNIT, + 0, + G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); + + + g_object_class_install_property + (gobject_class, + PROP_HOMOGENOUS_ROWS, + g_param_spec_boolean ("homogenous-rows", + "homogenous rows", + "Should all rows have the same height?", + FALSE, + G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); + + g_object_class_install_property + (gobject_class, + PROP_HOMOGENOUS_COLUMNS, + g_param_spec_boolean ("homogenous-columns", + "homogenous columns", + "Should all columns have the same height?", + FALSE, + G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); + + g_object_class_install_property + (gobject_class, + PROP_COLUMN_MAJOR, + g_param_spec_boolean ("column-major", + "column-major", + "Do a column filling first instead of row filling first", + FALSE, + G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); + + g_object_class_install_property + (gobject_class, + PROP_END_ALIGN, + g_param_spec_boolean ("end-align", + "end-align", + "Right/bottom aligned rows/columns", + FALSE, + G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); + + g_object_class_install_property + (gobject_class, + PROP_VALIGN, + g_param_spec_double ("valign", + "Vertical align", + "Vertical alignment of items within cells", + 0.0, 1.0, 0.0, + G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); + + g_object_class_install_property + (gobject_class, + PROP_HALIGN, + g_param_spec_double ("halign", + "Horizontal align", + "Horizontal alignment of items within cells", + 0.0, 1.0, 0.0, + G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); + +} + +static void +clutter_container_iface_init (ClutterContainerIface *iface) +{ + iface->add = tidy_grid_real_add; + iface->remove = tidy_grid_real_remove; + iface->foreach = tidy_grid_real_foreach; + iface->raise = tidy_grid_real_raise; + iface->lower = tidy_grid_real_lower; + iface->sort_depth_order = tidy_grid_real_sort_depth_order; +} + +static void +tidy_grid_init (TidyGrid *self) +{ + TidyGridPrivate *priv; + + self->priv = priv = TIDY_GRID_GET_PRIVATE (self); + + priv->hash_table + = g_hash_table_new_full (g_direct_hash, + g_direct_equal, + /*g_object_unref*/NULL, + tidy_grid_free_actor_data); +} + +static void +tidy_grid_dispose (GObject *object) +{ + TidyGrid *self = (TidyGrid *) object; + TidyGridPrivate *priv; + + priv = self->priv; + + /* Destroy all of the children. This will cause them to be removed + from the container and unparented */ + clutter_container_foreach (CLUTTER_CONTAINER (object), + (ClutterCallback) clutter_actor_destroy, + NULL); + + G_OBJECT_CLASS (tidy_grid_parent_class)->dispose (object); +} + +static void +tidy_grid_finalize (GObject *object) +{ + TidyGrid *self = (TidyGrid *) object; + TidyGridPrivate *priv = self->priv; + + g_hash_table_destroy (priv->hash_table); + + G_OBJECT_CLASS (tidy_grid_parent_class)->finalize (object); +} + + +void +tidy_grid_set_end_align (TidyGrid *self, + gboolean value) +{ + TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self); + priv->end_align = value; + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); +} + +gboolean +tidy_grid_get_end_align (TidyGrid *self) +{ + TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self); + return priv->end_align; +} + +void +tidy_grid_set_homogenous_rows (TidyGrid *self, + gboolean value) +{ + TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self); + priv->homogenous_rows = value; + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); +} + +gboolean +tidy_grid_get_homogenous_rows (TidyGrid *self) +{ + TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self); + return priv->homogenous_rows; +} + + +void +tidy_grid_set_homogenous_columns (TidyGrid *self, + gboolean value) +{ + TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self); + priv->homogenous_columns = value; + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); +} + + +gboolean +tidy_grid_get_homogenous_columns (TidyGrid *self) +{ + TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self); + return priv->homogenous_columns; +} + + +void +tidy_grid_set_column_major (TidyGrid *self, + gboolean value) +{ + TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self); + priv->column_major = value; + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); +} + +gboolean +tidy_grid_get_column_major (TidyGrid *self) +{ + TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self); + return priv->column_major; +} + +void +tidy_grid_set_column_gap (TidyGrid *self, + ClutterUnit value) +{ + TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self); + priv->column_gap = value; + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); +} + +ClutterUnit +tidy_grid_get_column_gap (TidyGrid *self) +{ + TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self); + return priv->column_gap; +} + + + +void +tidy_grid_set_row_gap (TidyGrid *self, + ClutterUnit value) +{ + TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self); + priv->row_gap = value; + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); +} + +ClutterUnit +tidy_grid_get_row_gap (TidyGrid *self) +{ + TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self); + return priv->row_gap; +} + + +void +tidy_grid_set_valign (TidyGrid *self, + gdouble value) +{ + TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self); + priv->valign = value; + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); +} + +gdouble +tidy_grid_get_valign (TidyGrid *self) +{ + TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self); + return priv->valign; +} + + + +void +tidy_grid_set_halign (TidyGrid *self, + gdouble value) + +{ + TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self); + priv->halign = value; + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); +} + +gdouble +tidy_grid_get_halign (TidyGrid *self) +{ + TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self); + return priv->halign; +} + + +static void +tidy_grid_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + TidyGrid *grid = TIDY_GRID (object); + + TidyGridPrivate *priv; + + priv = TIDY_GRID_GET_PRIVATE (object); + + switch (prop_id) + { + case PROP_END_ALIGN: + tidy_grid_set_end_align (grid, g_value_get_boolean (value)); + break; + case PROP_HOMOGENOUS_ROWS: + tidy_grid_set_homogenous_rows (grid, g_value_get_boolean (value)); + break; + case PROP_HOMOGENOUS_COLUMNS: + tidy_grid_set_homogenous_columns (grid, g_value_get_boolean (value)); + break; + case PROP_COLUMN_MAJOR: + tidy_grid_set_column_major (grid, g_value_get_boolean (value)); + break; + case PROP_COLUMN_GAP: + tidy_grid_set_column_gap (grid, clutter_value_get_unit (value)); + break; + case PROP_ROW_GAP: + tidy_grid_set_row_gap (grid, clutter_value_get_unit (value)); + break; + case PROP_VALIGN: + tidy_grid_set_valign (grid, g_value_get_double (value)); + break; + case PROP_HALIGN: + tidy_grid_set_halign (grid, g_value_get_double (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +tidy_grid_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + TidyGrid *grid = TIDY_GRID (object); + + TidyGridPrivate *priv; + + priv = TIDY_GRID_GET_PRIVATE (object); + + switch (prop_id) + { + case PROP_HOMOGENOUS_ROWS: + g_value_set_boolean (value, tidy_grid_get_homogenous_rows (grid)); + break; + case PROP_HOMOGENOUS_COLUMNS: + g_value_set_boolean (value, tidy_grid_get_homogenous_columns (grid)); + break; + case PROP_END_ALIGN: + g_value_set_boolean (value, tidy_grid_get_end_align (grid)); + break; + case PROP_COLUMN_MAJOR: + g_value_set_boolean (value, tidy_grid_get_column_major (grid)); + break; + case PROP_COLUMN_GAP: + clutter_value_set_unit (value, tidy_grid_get_column_gap (grid)); + break; + case PROP_ROW_GAP: + clutter_value_set_unit (value, tidy_grid_get_row_gap (grid)); + break; + case PROP_VALIGN: + g_value_set_double (value, tidy_grid_get_valign (grid)); + break; + case PROP_HALIGN: + g_value_set_double (value, tidy_grid_get_halign (grid)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +tidy_grid_free_actor_data (gpointer data) +{ + g_slice_free (TidyGridActorData, data); +} + +ClutterActor * +tidy_grid_new (void) +{ + ClutterActor *self = g_object_new (TIDY_TYPE_GRID, NULL); + + return self; +} + +static void +tidy_grid_real_add (ClutterContainer *container, + ClutterActor *actor) +{ + TidyGridPrivate *priv; + TidyGridActorData *data; + + g_return_if_fail (TIDY_IS_GRID (container)); + + g_object_ref (actor); + + priv = TIDY_GRID (container)->priv; + + clutter_actor_set_parent (actor, CLUTTER_ACTOR (container)); + + data = g_slice_alloc0 (sizeof (TidyGridActorData)); + + priv->list = g_list_append (priv->list, actor); + g_hash_table_insert (priv->hash_table, actor, data); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (container)); + + g_object_unref (actor); +} + +static void +tidy_grid_real_remove (ClutterContainer *container, + ClutterActor *actor) +{ + TidyGrid *layout = TIDY_GRID (container); + TidyGridPrivate *priv = layout->priv; + + g_object_ref (actor); + + if (g_hash_table_remove (priv->hash_table, actor)) + { + clutter_actor_unparent (actor); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (layout)); + + g_signal_emit_by_name (container, "actor-removed", actor); + + if (CLUTTER_ACTOR_IS_VISIBLE (CLUTTER_ACTOR (layout))) + clutter_actor_queue_redraw (CLUTTER_ACTOR (layout)); + } + priv->list = g_list_remove (priv->list, actor); + + g_object_unref (actor); +} + +static void +tidy_grid_real_foreach (ClutterContainer *container, + ClutterCallback callback, + gpointer user_data) +{ + TidyGrid *layout = TIDY_GRID (container); + TidyGridPrivate *priv = layout->priv; + + g_list_foreach (priv->list, (GFunc) callback, user_data); +} + +static void +tidy_grid_real_raise (ClutterContainer *container, + ClutterActor *actor, + ClutterActor *sibling) +{ + /* STUB */ +} + +static void +tidy_grid_real_lower (ClutterContainer *container, + ClutterActor *actor, + ClutterActor *sibling) +{ + /* STUB */ +} + +static void +tidy_grid_real_sort_depth_order (ClutterContainer *container) +{ + /* STUB */ +} + +static void +tidy_grid_paint (ClutterActor *actor) +{ + TidyGrid *layout = (TidyGrid *) actor; + TidyGridPrivate *priv = layout->priv; + GList *child_item; + + for (child_item = priv->list; + child_item != NULL; + child_item = child_item->next) + { + ClutterActor *child = child_item->data; + + g_assert (child != NULL); + + if (CLUTTER_ACTOR_IS_VISIBLE (child)) + clutter_actor_paint (child); + } + +} + +static void +tidy_grid_pick (ClutterActor *actor, + const ClutterColor *color) +{ + /* Chain up so we get a bounding box pained (if we are reactive) */ + CLUTTER_ACTOR_CLASS (tidy_grid_parent_class)->pick (actor, color); + + /* Just forward to the paint call which in turn will trigger + * the child actors also getting 'picked'. + */ + if (CLUTTER_ACTOR_IS_VISIBLE (actor)) + tidy_grid_paint (actor); +} + +static void +tidy_grid_get_preferred_width (ClutterActor *self, + ClutterUnit for_height, + ClutterUnit *min_width_p, + ClutterUnit *natural_width_p) +{ + TidyGrid *layout = (TidyGrid *) self; + TidyGridPrivate *priv = layout->priv; + ClutterUnit natural_width; + + natural_width = CLUTTER_UNITS_FROM_INT (200); + if (min_width_p) + *min_width_p = natural_width; + if (natural_width_p) + *natural_width_p = natural_width; + + priv->pref_width = natural_width; +} + +static void +tidy_grid_get_preferred_height (ClutterActor *self, + ClutterUnit for_width, + ClutterUnit *min_height_p, + ClutterUnit *natural_height_p) +{ + TidyGrid *layout = (TidyGrid *) self; + TidyGridPrivate *priv = layout->priv; + ClutterUnit natural_height; + + natural_height = CLUTTER_UNITS_FROM_INT (200); + + priv->for_width = for_width; + priv->pref_height = natural_height; + + if (min_height_p) + *min_height_p = natural_height; + if (natural_height_p) + *natural_height_p = natural_height; +} + +static ClutterUnit +compute_row_height (GList *siblings, + ClutterUnit best_yet, + ClutterUnit current_a, + TidyGridPrivate *priv) +{ + GList *l; + + gboolean homogenous_a; + gboolean homogenous_b; + ClutterUnit gap; + + if (priv->column_major) + { + homogenous_b = priv->homogenous_columns; + homogenous_a = priv->homogenous_rows; + gap = priv->row_gap; + } + else + { + homogenous_a = priv->homogenous_columns; + homogenous_b = priv->homogenous_rows; + gap = priv->column_gap; + } + + for (l = siblings; l != NULL; l = l->next) + { + ClutterActor *child = l->data; + ClutterUnit natural_width, natural_height; + + /* each child will get as much space as they require */ + clutter_actor_get_preferred_size (CLUTTER_ACTOR (child), + NULL, NULL, + &natural_width, &natural_height); + + if (priv->column_major) + { + ClutterUnit temp = natural_height; + natural_height = natural_width; + natural_width = temp; + } + + /* if the primary axis is homogenous, each additional item is the same + * width */ + if (homogenous_a) + natural_width = priv->max_extent_a; + + if (natural_height > best_yet) + best_yet = natural_height; + + /* if the child is overflowing, we wrap to next line */ + if (current_a + natural_width + gap > priv->a_wrap) + { + return best_yet; + } + current_a += natural_width + gap; + } + return best_yet; +} + + + + +static ClutterUnit +compute_row_start (GList *siblings, + ClutterUnit start_x, + TidyGridPrivate *priv) +{ + ClutterUnit current_a = start_x; + GList *l; + + gboolean homogenous_a; + gboolean homogenous_b; + ClutterUnit gap; + + if (priv->column_major) + { + homogenous_b = priv->homogenous_columns; + homogenous_a = priv->homogenous_rows; + gap = priv->row_gap; + } + else + { + homogenous_a = priv->homogenous_columns; + homogenous_b = priv->homogenous_rows; + gap = priv->column_gap; + } + + for (l = siblings; l != NULL; l = l->next) + { + ClutterActor *child = l->data; + ClutterUnit natural_width, natural_height; + + /* each child will get as much space as they require */ + clutter_actor_get_preferred_size (CLUTTER_ACTOR (child), + NULL, NULL, + &natural_width, &natural_height); + + + if (priv->column_major) + natural_width = natural_height; + + /* if the primary axis is homogenous, each additional item is the same width */ + if (homogenous_a) + natural_width = priv->max_extent_a; + + /* if the child is overflowing, we wrap to next line */ + if (current_a + natural_width + gap > priv->a_wrap) + { + if (current_a == start_x) + return start_x; + return (priv->a_wrap - current_a); + } + current_a += natural_width + gap; + } + return (priv->a_wrap - current_a); +} + +static void +tidy_grid_allocate (ClutterActor *self, + const ClutterActorBox *box, + gboolean absolute_origin_changed) +{ + TidyGrid *layout = (TidyGrid *) self; + TidyGridPrivate *priv = layout->priv; + + ClutterUnit current_a; + ClutterUnit current_b; + ClutterUnit next_b; + ClutterUnit agap; + ClutterUnit bgap; + + gboolean homogenous_a; + gboolean homogenous_b; + gdouble aalign; + gdouble balign; + + current_a = current_b = next_b = 0; + + GList *iter; + + /* chain up to set actor->allocation */ + CLUTTER_ACTOR_CLASS (tidy_grid_parent_class) + ->allocate (self, box, absolute_origin_changed); + + priv->alloc_width = box->x2 - box->x1; + priv->alloc_height = box->y2 - box->y1; + priv->absolute_origin_changed = absolute_origin_changed; + + /* Make sure we have calculated the preferred size */ + /* what does this do? */ + clutter_actor_get_preferred_size (self, NULL, NULL, NULL, NULL); + + + if (priv->column_major) + { + priv->a_wrap = priv->alloc_height; + homogenous_b = priv->homogenous_columns; + homogenous_a = priv->homogenous_rows; + aalign = priv->valign; + balign = priv->halign; + agap = priv->row_gap; + bgap = priv->column_gap; + } + else + { + priv->a_wrap = priv->alloc_width; + homogenous_a = priv->homogenous_columns; + homogenous_b = priv->homogenous_rows; + aalign = priv->halign; + balign = priv->valign; + agap = priv->column_gap; + bgap = priv->row_gap; + } + + priv->max_extent_a = 0; + priv->max_extent_b = 0; + + priv->first_of_batch = TRUE; + + if (homogenous_a || + homogenous_b) + { + for (iter = priv->list; iter; iter = iter->next) + { + ClutterActor *child = iter->data; + ClutterUnit natural_width; + ClutterUnit natural_height; + + /* each child will get as much space as they require */ + clutter_actor_get_preferred_size (CLUTTER_ACTOR (child), + NULL, NULL, + &natural_width, &natural_height); + if (natural_width > priv->max_extent_a) + priv->max_extent_a = natural_width; + if (natural_height > priv->max_extent_b) + priv->max_extent_b = natural_width; + } + } + + if (priv->column_major) + { + ClutterUnit temp = priv->max_extent_a; + priv->max_extent_a = priv->max_extent_b; + priv->max_extent_b = temp; + } + + for (iter = priv->list; iter; iter=iter->next) + { + ClutterActor *child = iter->data; + ClutterUnit natural_a; + ClutterUnit natural_b; + + /* each child will get as much space as they require */ + clutter_actor_get_preferred_size (CLUTTER_ACTOR (child), + NULL, NULL, + &natural_a, &natural_b); + + if (priv->column_major) /* swap axes around if column is major */ + { + ClutterUnit temp = natural_a; + natural_a = natural_b; + natural_b = temp; + } + + /* if the child is overflowing, we wrap to next line */ + if (current_a + natural_a > priv->a_wrap || + (homogenous_a && current_a + priv->max_extent_a > priv->a_wrap)) + { + current_b = next_b + bgap; + current_a = 0; + next_b = current_b + bgap; + priv->first_of_batch = TRUE; + } + + if (priv->end_align && + priv->first_of_batch) + { + current_a = compute_row_start (iter, current_a, priv); + priv->first_of_batch = FALSE; + } + + if (next_b-current_b < natural_b) + next_b = current_b + natural_b; + + { + ClutterUnit row_height; + ClutterActorBox child_box; + + if (homogenous_b) + { + row_height = priv->max_extent_b; + } + else + { + row_height = compute_row_height (iter, next_b-current_b, + current_a, priv); + } + + if (homogenous_a) + { + child_box.x1 = current_a + (priv->max_extent_a-natural_a) * aalign; + child_box.x2 = child_box.x1 + natural_a; + + } + else + { + child_box.x1 = current_a; + child_box.x2 = child_box.x1 + natural_a; + } + + child_box.y1 = current_b + (row_height-natural_b) * balign; + child_box.y2 = child_box.y1 + natural_b; + + + if (priv->column_major) + { + ClutterUnit temp = child_box.x1; + child_box.x1 = child_box.y1; + child_box.y1 = temp; + + temp = child_box.x2; + child_box.x2 = child_box.y2; + child_box.y2 = temp; + } + + /* update the allocation */ + clutter_actor_allocate (CLUTTER_ACTOR (child), + &child_box, + absolute_origin_changed); + + if (homogenous_a) + { + current_a += priv->max_extent_a + agap; + } + else + { + current_a += natural_a + agap; + } + } + } +} diff --git a/src/compositor/mutter/tidy/tidy-grid.h b/src/compositor/mutter/tidy/tidy-grid.h new file mode 100644 index 000000000..374baeba2 --- /dev/null +++ b/src/compositor/mutter/tidy/tidy-grid.h @@ -0,0 +1,99 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Authored By Matthew Allum + * + * Copyright (C) 2008 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __TIDY_GRID_H__ +#define __TIDY_GRID_H__ + +#include + +G_BEGIN_DECLS + +#define TIDY_TYPE_GRID (tidy_grid_get_type()) +#define TIDY_GRID(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + TIDY_TYPE_GRID, \ + TidyGrid)) +#define TIDY_GRID_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + TIDY_TYPE_GRID, \ + TidyGridClass)) +#define TIDY_IS_GRID(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + TIDY_TYPE_GRID)) +#define TIDY_IS_GRID_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + TIDY_TYPE_GRID)) +#define TIDY_GRID_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + TIDY_TYPE_GRID, \ + TidyGridClass)) + +typedef struct _TidyGrid TidyGrid; +typedef struct _TidyGridClass TidyGridClass; +typedef struct _TidyGridPrivate TidyGridPrivate; + +struct _TidyGridClass +{ + ClutterActorClass parent_class; +}; + +struct _TidyGrid +{ + ClutterActor parent; + + TidyGridPrivate *priv; +}; + +GType tidy_grid_get_type (void) G_GNUC_CONST; + +ClutterActor *tidy_grid_new (void); +void tidy_grid_set_end_align (TidyGrid *self, + gboolean value); +gboolean tidy_grid_get_end_align (TidyGrid *self); +void tidy_grid_set_homogenous_rows (TidyGrid *self, + gboolean value); +gboolean tidy_grid_get_homogenous_rows (TidyGrid *self); +void tidy_grid_set_homogenous_columns (TidyGrid *self, + gboolean value); +gboolean tidy_grid_get_homogenous_columns (TidyGrid *self); +void tidy_grid_set_column_major (TidyGrid *self, + gboolean value); +gboolean tidy_grid_get_column_major (TidyGrid *self); +void tidy_grid_set_row_gap (TidyGrid *self, + ClutterUnit value); +ClutterUnit tidy_grid_get_row_gap (TidyGrid *self); +void tidy_grid_set_column_gap (TidyGrid *self, + ClutterUnit value); +ClutterUnit tidy_grid_get_column_gap (TidyGrid *self); +void tidy_grid_set_valign (TidyGrid *self, + gdouble value); +gdouble tidy_grid_get_valign (TidyGrid *self); +void tidy_grid_set_halign (TidyGrid *self, + gdouble value); +gdouble tidy_grid_get_halign (TidyGrid *self); + +G_END_DECLS + +#endif /* __TIDY_GRID_H__ */ diff --git a/src/core/prefs.c b/src/core/prefs.c index b9d0ff82a..660f58729 100644 --- a/src/core/prefs.c +++ b/src/core/prefs.c @@ -68,6 +68,8 @@ #define KEY_CLUTTER_PLUGINS "/apps/metacity/general/clutter_plugins" #endif +#define KEY_LIVE_HIDDEN_WINDOWS "/apps/metacity/general/live_hidden_windows" + #ifdef HAVE_GCONF static GConfClient *default_client = NULL; static GList *changes = NULL; @@ -114,6 +116,8 @@ static gboolean clutter_disabled = FALSE; static GSList *clutter_plugins = NULL; #endif +static gboolean live_hidden_windows = FALSE; + #ifdef HAVE_GCONF static gboolean handle_preference_update_enum (const gchar *key, GConfValue *value); @@ -423,6 +427,11 @@ static MetaBoolPreference preferences_bool[] = FALSE, }, #endif + { "/apps/metacity/general/live_hidden_windows", + META_PREF_LIVE_HIDDEN_WINDOWS, + &live_hidden_windows, + FALSE, + }, { NULL, 0, NULL, FALSE }, }; @@ -1827,6 +1836,8 @@ meta_preference_to_string (MetaPreference pref) case META_PREF_CLUTTER_PLUGINS: return "CLUTTER_PLUGINS"; #endif + case META_PREF_LIVE_HIDDEN_WINDOWS: + return "LIVE_HIDDEN_WINDOWS"; } return "(unknown)"; @@ -2962,6 +2973,34 @@ meta_prefs_set_clutter_plugins (GSList *list) } #endif +gboolean +meta_prefs_get_live_hidden_windows (void) +{ + return live_hidden_windows; +} + +void +meta_prefs_set_live_hidden_windows (gboolean whether) +{ +#ifdef HAVE_GCONF + GError *err = NULL; + + gconf_client_set_bool (default_client, + KEY_LIVE_HIDDEN_WINDOWS, + whether, + &err); + + if (err) + { + meta_warning (_("Error setting live hidden windows status status: %s\n"), + err->message); + g_error_free (err); + } +#else + live_hidden_windows = whether; +#endif +} + #ifndef HAVE_GCONF static void init_button_layout(void) diff --git a/src/core/screen.c b/src/core/screen.c index de2d50473..7f2b5ec47 100644 --- a/src/core/screen.c +++ b/src/core/screen.c @@ -772,6 +772,17 @@ meta_screen_manage_all_windows (MetaScreen *screen) } meta_stack_thaw (screen->stack); + /* + * Because the windows have already been created/mapped/etc, if the compositor + * maintains a separate stack based on ConfigureNotify restack messages, it + * will not necessarily get what it needs; we explicitely notify the + * compositor to fix up its stacking order. + * + * For more on this issue, see comments in meta_window_hide(). + */ + if (screen->display->compositor) + meta_compositor_ensure_stack_order (screen->display->compositor, screen); + g_list_foreach (windows, (GFunc)g_free, NULL); g_list_free (windows); diff --git a/src/core/stack.c b/src/core/stack.c index cf770bcae..0622a342f 100644 --- a/src/core/stack.c +++ b/src/core/stack.c @@ -240,7 +240,13 @@ get_standalone_layer (MetaWindow *window) { MetaStackLayer layer; gboolean focused_transient = FALSE; - + + if (window->hidden) + { + layer = META_LAYER_DESKTOP; + return layer; + } + switch (window->type) { case META_WINDOW_DESKTOP: @@ -329,7 +335,8 @@ compute_layer (MetaWindow *window) * windows getting in fullscreen layer if any terminal is * fullscreen. */ - if (WINDOW_HAS_TRANSIENT_TYPE(window) && + if (window->layer != META_LAYER_DESKTOP && + WINDOW_HAS_TRANSIENT_TYPE(window) && (window->xtransient_for == None || window->transient_parent_is_root_window)) { @@ -854,7 +861,6 @@ stack_do_relayer (MetaStack *stack) meta_topic (META_DEBUG_STACK, "Window %s moved from layer %u to %u\n", w->desc, old_layer, w->layer); - stack->need_resort = TRUE; stack->need_constrain = TRUE; /* don't need to constrain as constraining diff --git a/src/core/window-private.h b/src/core/window-private.h index 8d6392875..fd2ff328e 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -150,6 +150,11 @@ struct _MetaWindow */ guint mapped : 1; + /* Whether window has been hidden from view by lowering it to the bottom + * of window stack. + */ + guint hidden : 1; + /* Iconic is the state in WM_STATE; happens for workspaces/shading * in addition to minimize */ @@ -393,11 +398,6 @@ void meta_window_change_workspace (MetaWindow *window, void meta_window_stick (MetaWindow *window); void meta_window_unstick (MetaWindow *window); -void meta_window_activate (MetaWindow *window, - guint32 current_time); -void meta_window_activate_with_workspace (MetaWindow *window, - guint32 current_time, - MetaWorkspace *workspace); void meta_window_make_fullscreen_internal (MetaWindow *window); void meta_window_make_fullscreen (MetaWindow *window); void meta_window_unmake_fullscreen (MetaWindow *window); diff --git a/src/core/window.c b/src/core/window.c index f16d1019e..3d4dd79bc 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -472,6 +472,7 @@ meta_window_new_with_attrs (MetaDisplay *display, window->tab_unminimized = FALSE; window->iconic = FALSE; window->mapped = attrs->map_state != IsUnmapped; + window->hidden = 0; /* if already mapped, no need to worry about focus-on-first-time-showing */ window->showing_for_first_time = !window->mapped; /* if already mapped we don't want to do the placement thing */ @@ -2243,8 +2244,22 @@ meta_window_show (MetaWindow *window) XMapWindow (window->display->xdisplay, window->xwindow); meta_error_trap_pop (window->display, FALSE); did_show = TRUE; - - if (window->was_minimized) + window->hidden = FALSE; + } + else if (meta_prefs_get_live_hidden_windows ()) + { + if (window->hidden && window->type != META_WINDOW_DESKTOP) + { + window->hidden = FALSE; + meta_stack_freeze (window->screen->stack); + meta_window_update_layer (window); + meta_window_raise (window); + meta_stack_thaw (window->screen->stack); + did_show = TRUE; + } + } + + if (did_show && window->was_minimized) { MetaRectangle window_rect; MetaRectangle icon_rect; @@ -2261,7 +2276,6 @@ meta_window_show (MetaWindow *window) NULL, NULL); } } - } if (window->iconic) { @@ -2309,33 +2323,83 @@ static void meta_window_hide (MetaWindow *window) { gboolean did_hide; - + meta_topic (META_DEBUG_WINDOW_STATE, "Hiding window %s\n", window->desc); did_hide = FALSE; - - if (window->frame && window->frame->mapped) - { - meta_topic (META_DEBUG_WINDOW_STATE, "Frame actually needs unmap\n"); - window->frame->mapped = FALSE; - meta_ui_unmap_frame (window->screen->ui, window->frame->xwindow); - did_hide = TRUE; - } - if (window->mapped) + if (meta_prefs_get_live_hidden_windows ()) { - meta_topic (META_DEBUG_WINDOW_STATE, - "%s actually needs unmap\n", window->desc); - meta_topic (META_DEBUG_WINDOW_STATE, - "Incrementing unmaps_pending on %s for hide\n", - window->desc); - window->mapped = FALSE; - window->unmaps_pending += 1; - meta_error_trap_push (window->display); - XUnmapWindow (window->display->xdisplay, window->xwindow); - meta_error_trap_pop (window->display, FALSE); + gboolean was_mapped; + + if (window->hidden) + return; + + was_mapped = window->mapped; + + if (!was_mapped) + meta_window_show (window); + + window->hidden = TRUE; did_hide = TRUE; + + meta_stack_freeze (window->screen->stack); + meta_window_update_layer (window); + meta_window_lower (window); + meta_stack_thaw (window->screen->stack); + + /* + * The X server does not implement lower-below semantics for restacking + * windows, only raise-above; consequently each single lower-bottom call + * gets translated to a bunch of raise-above moves, and often there will + * be no ConfigureNotify at all for the window we are lowering (only for + * its siblings). If we mix the lower-bottom sequence of calls with + * mapping of windows, the batch of ConfigureNotify events that is + * generated does not correctly reflect the stack order, and if the + * Compositor relies on these for its own internal stack, it will + * invariably end up with wrong stacking order. + * + * I have not been able to find a way to get this just work so that the + * resulting ConfigureNotify messages would reflect the actual state of + * the stack, so in the special case we map a window while hiding it, we + * explitely notify the compositor that it should ensure its stacking + * matches the cannonical stack of the WM. + * + * NB: this is uncommon, and generally only happens on the WM start up, + * when we are taking over pre-existing windows, so this brute-force + * fix is OK performance wise. + */ + if (!was_mapped && window->display->compositor) + { + meta_compositor_ensure_stack_order (window->display->compositor, + window->screen); + } + } + else + { + if (window->frame && window->frame->mapped) + { + meta_topic (META_DEBUG_WINDOW_STATE, "Frame actually needs unmap\n"); + window->frame->mapped = FALSE; + meta_ui_unmap_frame (window->screen->ui, window->frame->xwindow); + did_hide = TRUE; + } + + if (window->mapped) + { + meta_topic (META_DEBUG_WINDOW_STATE, + "%s actually needs unmap\n", window->desc); + meta_topic (META_DEBUG_WINDOW_STATE, + "Incrementing unmaps_pending on %s for hide\n", + window->desc); + window->mapped = FALSE; + window->unmaps_pending += 1; + meta_error_trap_push (window->display); + XUnmapWindow (window->display->xdisplay, window->xwindow); + meta_error_trap_pop (window->display, FALSE); + did_hide = TRUE; + } } if (!window->iconic) @@ -7974,7 +8038,7 @@ meta_window_update_layer (MetaWindow *window) meta_stack_freeze (window->screen->stack); group = meta_window_get_group (window); - if (group) + if (!window->hidden && group) meta_group_update_layers (group); else meta_stack_update_layer (window->screen->stack, window); @@ -8223,3 +8287,9 @@ meta_window_is_on_all_workspaces (MetaWindow *window) return window->on_all_workspaces; } +gboolean +meta_window_is_hidden (MetaWindow *window) +{ + return window->hidden; +} + diff --git a/src/core/workspace-private.h b/src/core/workspace-private.h index b589274b7..5b9589ccd 100644 --- a/src/core/workspace-private.h +++ b/src/core/workspace-private.h @@ -65,11 +65,6 @@ void meta_workspace_remove_window (MetaWorkspace *workspace, MetaWindow *window); void meta_workspace_relocate_windows (MetaWorkspace *workspace, MetaWorkspace *new_home); -void meta_workspace_activate_with_focus (MetaWorkspace *workspace, - MetaWindow *focus_this, - guint32 timestamp); -void meta_workspace_activate (MetaWorkspace *workspace, - guint32 timestamp); GList* meta_workspace_list_windows (MetaWorkspace *workspace); void meta_workspace_invalidate_work_area (MetaWorkspace *workspace); diff --git a/src/core/workspace.c b/src/core/workspace.c index a2e3ed1e1..625da5087 100644 --- a/src/core/workspace.c +++ b/src/core/workspace.c @@ -306,7 +306,19 @@ meta_workspace_queue_calc_showing (MetaWorkspace *workspace) tmp = workspace->windows; while (tmp != NULL) { - meta_window_queue (tmp->data, META_QUEUE_CALC_SHOWING); + if (meta_prefs_get_live_hidden_windows ()) + { + /* + * When we hide rather than unmap windows, we need the show/hide + * status of the window to be recalculated *before* we call the + * compositor switch_workspace hook. + */ + meta_window_calc_showing (tmp->data); + } + else + { + meta_window_queue (tmp->data, META_QUEUE_CALC_SHOWING); + } tmp = tmp->next; } diff --git a/src/include/compositor-mutter.h b/src/include/compositor-mutter.h index 11c86636a..5114d70af 100644 --- a/src/include/compositor-mutter.h +++ b/src/include/compositor-mutter.h @@ -57,10 +57,13 @@ struct _MutterWindow GType mutter_window_get_type (void); -Window mutter_window_get_x_window (MutterWindow *mcw); -MetaCompWindowType mutter_window_get_window_type (MutterWindow *mcw); -gint mutter_window_get_workspace (MutterWindow *mcw); - +Window mutter_window_get_x_window (MutterWindow *mcw); +MetaCompWindowType mutter_window_get_window_type (MutterWindow *mcw); +gint mutter_window_get_workspace (MutterWindow *mcw); +gboolean mutter_window_is_hidden (MutterWindow *mcw); +MetaWindow * mutter_window_get_meta_window (MutterWindow *mcw); +ClutterActor * mutter_window_get_texture (MutterWindow *mcw); +gboolean mutter_window_is_override_redirect (MutterWindow *mcw); /* Compositor API */ MetaCompositor *mutter_new (MetaDisplay *display); @@ -70,5 +73,6 @@ void mutter_window_effect_completed (MutterWindow *actor, gulong event); ClutterActor * mutter_get_stage_for_screen (MetaScreen *screen); ClutterActor * mutter_get_overlay_group_for_screen (MetaScreen *screen); Window mutter_get_overlay_window (MetaScreen *screen); +GList * mutter_get_windows (MetaScreen *screen); #endif diff --git a/src/include/compositor.h b/src/include/compositor.h index 736a1d2a3..c0f15d626 100644 --- a/src/include/compositor.h +++ b/src/include/compositor.h @@ -127,6 +127,11 @@ meta_compositor_switch_workspace (MetaCompositor *compositor, MetaWorkspace *from, MetaWorkspace *to, MetaMotionDirection direction); + +void +meta_compositor_ensure_stack_order (MetaCompositor *compositor, + MetaScreen *screen); + #endif diff --git a/src/include/mutter-plugin.h b/src/include/mutter-plugin.h index b6cfcc5f2..a54e8093b 100644 --- a/src/include/mutter-plugin.h +++ b/src/include/mutter-plugin.h @@ -225,4 +225,7 @@ void mutter_plugin_set_stage_input_area (MutterPlugin *plugin, gint x, gint y, gint width, gint height); +GList * +mutter_plugin_get_windows (MutterPlugin *plugin); + #endif /* MUTTER_PLUGIN_H_ */ diff --git a/src/include/prefs.h b/src/include/prefs.h index 4740771a1..152477e3e 100644 --- a/src/include/prefs.h +++ b/src/include/prefs.h @@ -62,8 +62,9 @@ typedef enum META_PREF_COMPOSITING_MANAGER, #ifdef WITH_CLUTTER META_PREF_CLUTTER_DISABLED, - META_PREF_CLUTTER_PLUGINS + META_PREF_CLUTTER_PLUGINS, #endif + META_PREF_LIVE_HIDDEN_WINDOWS, } MetaPreference; typedef void (* MetaPrefsChangedFunc) (MetaPreference pref, @@ -139,6 +140,10 @@ GSList * meta_prefs_get_clutter_plugins (void); void meta_prefs_set_clutter_plugins (GSList *list); #endif +gboolean meta_prefs_get_live_hidden_windows (void); +void meta_prefs_set_live_hidden_windows (gboolean whether); + + /* XXX FIXME This should be x-macroed, but isn't yet because it would be * difficult (or perhaps impossible) to add the suffixes using the current * system. It needs some more thought, perhaps after the current system diff --git a/src/include/window.h b/src/include/window.h index a3f49928b..6fa21f238 100644 --- a/src/include/window.h +++ b/src/include/window.h @@ -58,4 +58,10 @@ MetaWindowType meta_window_get_type (MetaWindow *window); Atom meta_window_get_type_atom (MetaWindow *window); MetaWorkspace *meta_window_get_workspace (MetaWindow *window); gboolean meta_window_is_on_all_workspaces (MetaWindow *window); +gboolean meta_window_is_hidden (MetaWindow *window); +void meta_window_activate (MetaWindow *window,guint32 current_time); +void meta_window_activate_with_workspace (MetaWindow *window, + guint32 current_time, + MetaWorkspace *workspace); + #endif diff --git a/src/include/workspace.h b/src/include/workspace.h index e4e4c5d23..00497068c 100644 --- a/src/include/workspace.h +++ b/src/include/workspace.h @@ -57,4 +57,8 @@ int meta_workspace_index (MetaWorkspace *workspace); MetaScreen *meta_workspace_get_screen (MetaWorkspace *workspace); void meta_workspace_get_work_area_all_xineramas (MetaWorkspace *workspace, MetaRectangle *area); +void meta_workspace_activate (MetaWorkspace *workspace, guint32 timestamp); +void meta_workspace_activate_with_focus (MetaWorkspace *workspace, + MetaWindow *focus_this, + guint32 timestamp); #endif diff --git a/src/metacity.schemas.in b/src/metacity.schemas.in index b9cb74908..1336cd3ef 100644 --- a/src/metacity.schemas.in +++ b/src/metacity.schemas.in @@ -370,6 +370,22 @@ + + /schemas/apps/metacity/general/live_hidden_windows + /apps/metacity/general/live_hidden_windows + metacity + bool + false + + Live Hidden Windows + + Determines whether hidden windows (i.e., minimized windows and + windows on other workspaces than the current one) should be kept + alive. + + + + /schemas/apps/metacity/workspace_names/name /apps/metacity/workspace_names/name_1