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