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).
This commit is contained in:
Tomas Frydrych 2008-10-24 10:07:24 +01:00
parent 273d213509
commit 07c1003905
22 changed files with 2017 additions and 221 deletions

View File

@ -112,6 +112,8 @@ metacity_SOURCES += \
compositor/mutter/mutter-plugin-manager.h \ compositor/mutter/mutter-plugin-manager.h \
compositor/mutter/tidy/tidy-texture-frame.c \ compositor/mutter/tidy/tidy-texture-frame.c \
compositor/mutter/tidy/tidy-texture-frame.h \ compositor/mutter/tidy/tidy-texture-frame.h \
compositor/mutter/tidy/tidy-grid.c \
compositor/mutter/tidy/tidy-grid.h \
include/mutter-plugin.h include/mutter-plugin.h
endif endif

View File

@ -78,6 +78,9 @@ struct _MetaCompositor
MetaWorkspace *from, MetaWorkspace *from,
MetaWorkspace *to, MetaWorkspace *to,
MetaMotionDirection direction); MetaMotionDirection direction);
void (*ensure_stack_order) (MetaCompositor *compositor,
MetaScreen *screen);
}; };
#endif #endif

View File

@ -242,3 +242,12 @@ meta_compositor_switch_workspace (MetaCompositor *compositor,
#endif #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
}

View File

@ -1,3 +1,5 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#define _GNU_SOURCE #define _GNU_SOURCE
#define _XOPEN_SOURCE 500 /* for usleep() */ #define _XOPEN_SOURCE 500 /* for usleep() */
@ -10,6 +12,7 @@
#include <gdk/gdk.h> #include <gdk/gdk.h>
#include "../../core/window-private.h"
#include "display.h" #include "display.h"
#include "screen.h" #include "screen.h"
#include "frame.h" #include "frame.h"
@ -20,6 +23,7 @@
#include "mutter-plugin-manager.h" #include "mutter-plugin-manager.h"
#include "tidy/tidy-texture-frame.h" #include "tidy/tidy-texture-frame.h"
#include "xprops.h" #include "xprops.h"
#include "prefs.h"
#include "mutter-shaped-texture.h" #include "mutter-shaped-texture.h"
#include <X11/Xatom.h> #include <X11/Xatom.h>
#include <X11/Xlibint.h> #include <X11/Xlibint.h>
@ -46,6 +50,16 @@
#define TILE_WIDTH (3*MAX_TILE_SZ) #define TILE_WIDTH (3*MAX_TILE_SZ)
#define TILE_HEIGHT (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 * Register GType wrapper for XWindowAttributes, so we do not have to
* query window attributes in the MutterWindow constructor but can pass * 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); MetaCompScreen *info = meta_screen_get_compositor_data (screen);
if (info == NULL) if (info == NULL)
return NULL; return NULL;
return g_hash_table_lookup (info->windows_by_xid, (gpointer) xwindow); 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; 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 MetaCompWindowType
mutter_window_get_window_type (MutterWindow *mcw) mutter_window_get_window_type (MutterWindow *mcw)
{ {
@ -699,6 +725,15 @@ mutter_window_get_window_type (MutterWindow *mcw)
return mcw->priv->type; return mcw->priv->type;
} }
gboolean
mutter_window_is_override_redirect (MutterWindow *mcw)
{
if (!mcw->priv->window)
return TRUE;
return FALSE;
}
gint gint
mutter_window_get_workspace (MutterWindow *mcw) mutter_window_get_workspace (MutterWindow *mcw)
{ {
@ -718,6 +753,23 @@ mutter_window_get_workspace (MutterWindow *mcw)
return meta_workspace_index (workspace); 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 repair_win (MutterWindow *cw);
static void map_win (MutterWindow *cw); static void map_win (MutterWindow *cw);
static void unmap_win (MutterWindow *cw); static void unmap_win (MutterWindow *cw);
@ -726,54 +778,83 @@ static void
mutter_finish_workspace_switch (MetaCompScreen *info) mutter_finish_workspace_switch (MetaCompScreen *info)
{ {
GList *last = g_list_last (info->windows); 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) while (l)
{ {
MutterWindow *cw = l->data; ClutterActor *a = l->data;
MutterWindowPrivate *priv = cw->priv; 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);
} }
else
if (priv->needs_unmap)
{ {
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; l = l->prev;
} }
/* /* printf ("... FINISHED DESKTOP SWITCH\n"); */
* Now fix up stacking order in case the plugin messed it up.
*/
l = last;
while (l)
{
ClutterActor *a = l->data;
GList *prev = l->prev;
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 void
mutter_window_effect_completed (MutterWindow *cw, mutter_window_effect_completed (MutterWindow *cw, gulong event)
gulong event)
{ {
MutterWindowPrivate *priv = cw->priv; MutterWindowPrivate *priv = cw->priv;
MetaScreen *screen = priv->screen; MetaScreen *screen = priv->screen;
@ -784,8 +865,7 @@ mutter_window_effect_completed (MutterWindow *cw,
{ {
case MUTTER_PLUGIN_MINIMIZE: case MUTTER_PLUGIN_MINIMIZE:
{ {
ClutterActor *a = CLUTTER_ACTOR (cw); ClutterActor *a = CLUTTER_ACTOR (cw);
gint height = clutter_actor_get_height (a);
priv->minimize_in_progress--; priv->minimize_in_progress--;
if (priv->minimize_in_progress < 0) if (priv->minimize_in_progress < 0)
@ -797,7 +877,19 @@ mutter_window_effect_completed (MutterWindow *cw,
if (!priv->minimize_in_progress) if (!priv->minimize_in_progress)
{ {
priv->is_minimized = TRUE; 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; break;
@ -911,16 +1003,51 @@ mutter_window_detach (MutterWindow *self)
} }
static void 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)
if (cw == NULL)
return; 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 static void
@ -931,6 +1058,13 @@ restack_win (MutterWindow *cw, Window above)
MetaCompScreen *info = meta_screen_get_compositor_data (screen); MetaCompScreen *info = meta_screen_get_compositor_data (screen);
Window previous_above; Window previous_above;
GList *sibling, *next; 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); sibling = g_list_find (info->windows, (gconstpointer) cw);
next = g_list_next (sibling); next = g_list_next (sibling);
@ -947,45 +1081,135 @@ restack_win (MutterWindow *cw, Window above)
*/ */
if (above == None) 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 */ /* 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_delete_link (info->windows, sibling);
info->windows = g_list_append (info->windows, cw); info->windows = g_list_append (info->windows, cw);
CHECK_LIST_INTEGRITY_END(info->windows)
if (!info->switch_workspace_in_progress) 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) else if (previous_above != above)
{ {
GList *index; 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; MutterWindow *cw2 = (MutterWindow *) index->data;
if (cw2->priv->xwindow == above) 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) 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); CHECK_LIST_INTEGRITY_START(info->windows)
info->windows = g_list_insert_before (info->windows, index, cw); info->windows = g_list_delete_link (info->windows, sibling);
if (!info->switch_workspace_in_progress) info->windows = g_list_insert_before (info->windows, index, cw);
clutter_actor_raise (CLUTTER_ACTOR (cw), above_win); 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 static void
resize_win (MutterWindow *cw, resize_win (MutterWindow *cw,
int x, int x,
int y, int y,
int width, int width,
int height, int height,
int border_width, int border_width,
gboolean override_redirect) gboolean override_redirect)
{ {
MutterWindowPrivate *priv = cw->priv; MutterWindowPrivate *priv = cw->priv;
@ -1093,15 +1317,15 @@ unmap_win (MutterWindow *cw)
} }
priv->attrs.map_state = IsUnmapped; 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_unmap = FALSE;
priv->needs_map = 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; MutterWindowPrivate *priv;
Display *xdisplay = meta_display_get_xdisplay (display); Display *xdisplay = meta_display_get_xdisplay (display);
XWindowAttributes attrs; XWindowAttributes attrs;
gulong events_needed;
if (info == NULL) if (info == NULL)
return; return;
@ -1122,17 +1347,19 @@ add_win (MetaScreen *screen, MetaWindow *window, Window xwindow)
return; return;
if (!XGetWindowAttributes (xdisplay, xwindow, &attrs)) if (!XGetWindowAttributes (xdisplay, xwindow, &attrs))
return; return;
/* /*
* If Metacity has decided not to manage this window then the input events * If Metacity has decided not to manage this window then the input events
* won't have been set on the window * 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; gulong event_mask;
event_mask = attrs.your_event_mask | events_needed;
event_mask = attrs.your_event_mask | PropertyChangeMask;
XSelectInput (xdisplay, xwindow, event_mask); 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); info->dock_windows = g_slist_append (info->dock_windows, cw);
} }
meta_verbose ("added 0x%x (%p) type:", (guint)xwindow, cw);
#if 0 #if 0
printf ("added 0x%x (%p) type:", (guint)xwindow, cw);
switch (cw->type) switch (cw->type)
{ {
@ -1335,8 +1562,8 @@ process_create (Mutter *compositor,
MetaWindow *window) MetaWindow *window)
{ {
MetaScreen *screen; MetaScreen *screen;
MutterWindow *cw;
Window xwindow = event->window; Window xwindow = event->window;
MutterWindow *mw;
screen = meta_display_screen_for_root (compositor->display, event->parent); 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 * This is quite silly as we end up creating windows as then immediatly
* destroying them as they (likely) become framed and thus reparented. * 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); 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); add_win (screen, window, event->window);
@ -1369,16 +1596,24 @@ process_reparent (Mutter *compositor,
XReparentEvent *event, XReparentEvent *event,
MetaWindow *window) MetaWindow *window)
{ {
MetaScreen *screen; MetaScreen *screen;
MutterWindow *mw; MutterWindow *mw;
Window xwindow = event->window; Window xwindow = event->window;
gboolean viewable = FALSE;
screen = meta_display_screen_for_root (compositor->display, event->parent); screen = meta_display_screen_for_root (compositor->display, event->parent);
if (!screen) if (!screen)
return; 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) if (!mw && window)
{ {
@ -1390,19 +1625,23 @@ process_reparent (Mutter *compositor,
if (mw) if (mw)
{ {
meta_verbose ("reparent: destroying a window 0%x\n", viewable = (mw->priv->attrs.map_state == IsViewable);
(guint)event->window); destroy_win (mw, TRUE);
destroy_win (compositor->display, xwindow);
} }
add_win (screen, window, event->window); add_win (screen, window, event->window);
} }
static void static void
process_destroy (Mutter *compositor, process_destroy (Mutter *compositor, XDestroyWindowEvent *event)
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 static void
@ -1437,7 +1676,7 @@ process_damage (Mutter *compositor,
if (XCheckTypedWindowEvent (dpy, drawable, DestroyNotify, &next)) if (XCheckTypedWindowEvent (dpy, drawable, DestroyNotify, &next))
{ {
priv->destroy_pending = TRUE; priv->destroy_pending = TRUE;
process_destroy (compositor, (XDestroyWindowEvent *) &next); destroy_win (cw, FALSE);
return; return;
} }
@ -1501,7 +1740,18 @@ process_configure_notify (Mutter *compositor,
XConfigureEvent *event) XConfigureEvent *event)
{ {
MetaDisplay *display = compositor->display; 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) if (cw)
{ {
@ -1596,25 +1846,28 @@ process_unmap (Mutter *compositor,
if (XCheckTypedWindowEvent (dpy, xwin, DestroyNotify, &next)) if (XCheckTypedWindowEvent (dpy, xwin, DestroyNotify, &next))
{ {
priv->destroy_pending = TRUE; priv->destroy_pending = TRUE;
process_destroy (compositor, (XDestroyWindowEvent *) &next); destroy_win (cw, FALSE);
return; return;
} }
meta_verbose ("processing unmap of 0x%x (%p)\n", (guint)xwin, cw);
unmap_win (cw); unmap_win (cw);
} }
} }
static void static void
process_map (Mutter *compositor, process_map (Mutter *compositor,
XMapEvent *event, XMapEvent *event,
MetaWindow *window) MetaWindow *window)
{ {
MutterWindow *cw = find_window_in_display (compositor->display, MutterWindow *cw;
event->window); Window xwindow = event->window;
cw = find_window_in_display (compositor->display, xwindow);
if (cw) if (cw)
map_win (cw); {
map_win (cw);
}
} }
static void static void
@ -1727,6 +1980,16 @@ mutter_get_overlay_group_for_screen (MetaScreen *screen)
return info->overlay_group; 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 static void
clutter_cmp_manage_screen (MetaCompositor *compositor, 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); MetaScreen *screen = meta_screen_for_x_screen (attrs->screen);
meta_error_trap_push (xrc->display); meta_error_trap_push (xrc->display);
add_win (screen, window, xwindow); add_win (screen, window, xwindow);
meta_error_trap_pop (xrc->display, FALSE); meta_error_trap_pop (xrc->display, FALSE);
#endif #endif
@ -1883,9 +2147,8 @@ clutter_cmp_process_event (MetaCompositor *compositor,
screen = meta_window_get_screen (window); screen = meta_window_get_screen (window);
info = meta_screen_get_compositor_data (screen); info = meta_screen_get_compositor_data (screen);
if (mutter_plugin_manager_xevent_filter if (mutter_plugin_manager_xevent_filter (info->plugin_mgr,
(info->plugin_mgr, event) == TRUE)
event) == TRUE)
return; return;
} }
else else
@ -2006,44 +2269,20 @@ clutter_cmp_destroy_window (MetaCompositor *compositor,
MetaWindow *window) MetaWindow *window)
{ {
#ifdef HAVE_COMPOSITE_EXTENSIONS #ifdef HAVE_COMPOSITE_EXTENSIONS
MutterWindow *cw = NULL; MutterWindow *cw = NULL;
MetaScreen *screen = meta_window_get_screen (window); MetaScreen *screen = meta_window_get_screen (window);
MetaCompScreen *info = meta_screen_get_compositor_data (screen); MetaFrame *f = meta_window_get_frame (window);
MetaFrame *f = meta_window_get_frame (window); Window xwindow;
MutterWindowPrivate *priv;
/* Chances are we actually get the window frame here */ /* Chances are we actually get the window frame here */
cw = find_window_for_screen (screen, xwindow = f ? meta_frame_get_xwindow (f) : meta_window_get_xwindow (window);
f ? meta_frame_get_xwindow (f) :
meta_window_get_xwindow (window)); cw = find_window_for_screen (screen, xwindow);
if (!cw) if (!cw)
return; return;
priv = cw->priv; destroy_win (cw, FALSE);
/*
* 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));
}
#endif #endif
} }
@ -2077,12 +2316,8 @@ clutter_cmp_minimize_window (MetaCompositor *compositor, MetaWindow *window)
cw, cw,
MUTTER_PLUGIN_MINIMIZE)) MUTTER_PLUGIN_MINIMIZE))
{ {
ClutterActor *a = CLUTTER_ACTOR (cw);
gint height = clutter_actor_get_height (a);
cw->priv->is_minimized = TRUE; cw->priv->is_minimized = TRUE;
cw->priv->minimize_in_progress--; cw->priv->minimize_in_progress--;
clutter_actor_set_position (a, 0, -height);
} }
#endif #endif
} }
@ -2181,65 +2416,109 @@ clutter_cmp_switch_workspace (MetaCompositor *compositor,
{ {
#ifdef HAVE_COMPOSITE_EXTENSIONS #ifdef HAVE_COMPOSITE_EXTENSIONS
MetaCompScreen *info; MetaCompScreen *info;
GList *l;
gint to_indx, from_indx; gint to_indx, from_indx;
info = meta_screen_get_compositor_data (screen); info = meta_screen_get_compositor_data (screen);
to_indx = meta_workspace_index (to); to_indx = meta_workspace_index (to);
from_indx = meta_workspace_index (from); from_indx = meta_workspace_index (from);
printf ("Direction of switch %d\n", direction); if (!meta_prefs_get_live_hidden_windows ())
l = info->windows;
while (l)
{ {
MutterWindow *cw = l->data; /*
MetaWindow *mw = cw->priv->window; * We are in the traditional mode where hidden windows get unmapped,
gboolean sticky; * we need to pre-calculate the map status of each window so that once
gint workspace = -1; * 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)); while (l)
if (!sticky)
{ {
MetaWorkspace *w; MutterWindow *cw = l->data;
MetaWindow *mw = cw->priv->window;
gboolean sticky;
gint workspace = -1;
w = meta_window_get_workspace (cw->priv->window); sticky = (!mw || meta_window_is_on_all_workspaces (mw));
workspace = meta_workspace_index (w);
/* if (!sticky)
* If the window is not on the target workspace, mark it for
* unmap.
*/
if (to_indx != workspace)
{ {
cw->priv->needs_unmap = TRUE; MetaWorkspace *w;
}
else w = meta_window_get_workspace (cw->priv->window);
{ workspace = meta_workspace_index (w);
cw->priv->needs_map = TRUE;
cw->priv->needs_unmap = FALSE; /*
* 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++; info->switch_workspace_in_progress++;
if (!info->plugin_mgr || if (!info->plugin_mgr ||
!mutter_plugin_manager_switch_workspace ( !mutter_plugin_manager_switch_workspace (info->plugin_mgr,
info->plugin_mgr, (const GList **)&info->windows,
(const GList **)&info->windows, from_indx,
from_indx, to_indx,
to_indx, direction))
direction))
{ {
info->switch_workspace_in_progress--; 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 #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 = { static MetaCompositor comp_info = {
clutter_cmp_destroy, clutter_cmp_destroy,
@ -2256,7 +2535,8 @@ static MetaCompositor comp_info = {
clutter_cmp_maximize_window, clutter_cmp_maximize_window,
clutter_cmp_unmaximize_window, clutter_cmp_unmaximize_window,
clutter_cmp_update_workspace_geometry, clutter_cmp_update_workspace_geometry,
clutter_cmp_switch_workspace clutter_cmp_switch_workspace,
clutter_cmp_ensure_stack_order,
}; };
MetaCompositor * MetaCompositor *

View File

@ -884,3 +884,13 @@ mutter_plugin_set_stage_input_area (MutterPlugin *plugin,
XFixesSetWindowShapeRegion (xdpy, xstage, ShapeInput, 0, 0, region); XFixesSetWindowShapeRegion (xdpy, xstage, ShapeInput, 0, 0, region);
XFixesSetWindowShapeRegion (xdpy, xoverlay, 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);
}

View File

@ -33,6 +33,12 @@
#include <gmodule.h> #include <gmodule.h>
#include <string.h> #include <string.h>
#include "../tidy/tidy-grid.h"
/* For debugging only */
#include "../../../core/window-private.h"
#include "compositor-mutter.h"
#define DESTROY_TIMEOUT 250 #define DESTROY_TIMEOUT 250
#define MINIMIZE_TIMEOUT 250 #define MINIMIZE_TIMEOUT 250
#define MAXIMIZE_TIMEOUT 250 #define MAXIMIZE_TIMEOUT 250
@ -43,6 +49,10 @@
#define PANEL_SLIDE_THRESHOLD 2 #define PANEL_SLIDE_THRESHOLD 2
#define PANEL_HEIGHT 40 #define PANEL_HEIGHT 40
#define ACTOR_DATA_KEY "MCCP-scratch-actor-data" #define ACTOR_DATA_KEY "MCCP-scratch-actor-data"
#define SWITCHER_CELL_WIDTH 200
#define SWITCHER_CELL_HEIGHT 200
static GQuark actor_data_quark = 0; static GQuark actor_data_quark = 0;
typedef struct PluginPrivate PluginPrivate; typedef struct PluginPrivate PluginPrivate;
@ -119,6 +129,8 @@ struct PluginPrivate
ClutterActor *d_overlay ; /* arrow indicator */ ClutterActor *d_overlay ; /* arrow indicator */
ClutterActor *panel; ClutterActor *panel;
ClutterActor *switcher;
gboolean debug_mode : 1; gboolean debug_mode : 1;
gboolean panel_out : 1; gboolean panel_out : 1;
gboolean panel_out_in_progress : 1; gboolean panel_out_in_progress : 1;
@ -131,6 +143,8 @@ struct PluginPrivate
struct ActorPrivate struct ActorPrivate
{ {
ClutterActor *orig_parent; ClutterActor *orig_parent;
gint orig_x;
gint orig_y;
ClutterTimeline *tml_minimize; ClutterTimeline *tml_minimize;
ClutterTimeline *tml_maximize; ClutterTimeline *tml_maximize;
@ -800,36 +814,225 @@ g_module_check_init (GModule *module)
} }
#endif #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 static gboolean
stage_input_cb (ClutterActor *stage, ClutterEvent *event, gpointer data) 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 (); MutterPlugin *plugin = get_plugin ();
PluginPrivate *priv = plugin->plugin_private; 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) if (priv->panel_out_in_progress || priv->panel_back_in_progress)
return FALSE; 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); guint height = clutter_actor_get_height (priv->panel);
gint x = clutter_actor_get_x (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; priv->panel_back_in_progress = TRUE;
clutter_effect_move (priv->panel_slide_effect, clutter_effect_move (priv->panel_slide_effect,
priv->panel, x, -height, priv->panel, x, -height,
on_panel_effect_complete, on_panel_effect_complete,
GINT_TO_POINTER (FALSE)); GINT_TO_POINTER (FALSE));
priv->panel_out = 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); 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)); GINT_TO_POINTER (TRUE));
priv->panel_out = 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; return FALSE;
@ -964,7 +1176,12 @@ do_init (const char *params)
* children and do not interfere with their event processing. * children and do not interfere with their event processing.
*/ */
g_signal_connect (mutter_plugin_get_stage (plugin), 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); mutter_plugin_set_stage_input_area (plugin, 0, 0, screen_width, 1);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,99 @@
/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Authored By Matthew Allum <mallum@openedhand.com>
*
* 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 <clutter/clutter-actor.h>
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__ */

View File

@ -68,6 +68,8 @@
#define KEY_CLUTTER_PLUGINS "/apps/metacity/general/clutter_plugins" #define KEY_CLUTTER_PLUGINS "/apps/metacity/general/clutter_plugins"
#endif #endif
#define KEY_LIVE_HIDDEN_WINDOWS "/apps/metacity/general/live_hidden_windows"
#ifdef HAVE_GCONF #ifdef HAVE_GCONF
static GConfClient *default_client = NULL; static GConfClient *default_client = NULL;
static GList *changes = NULL; static GList *changes = NULL;
@ -114,6 +116,8 @@ static gboolean clutter_disabled = FALSE;
static GSList *clutter_plugins = NULL; static GSList *clutter_plugins = NULL;
#endif #endif
static gboolean live_hidden_windows = FALSE;
#ifdef HAVE_GCONF #ifdef HAVE_GCONF
static gboolean handle_preference_update_enum (const gchar *key, GConfValue *value); static gboolean handle_preference_update_enum (const gchar *key, GConfValue *value);
@ -423,6 +427,11 @@ static MetaBoolPreference preferences_bool[] =
FALSE, FALSE,
}, },
#endif #endif
{ "/apps/metacity/general/live_hidden_windows",
META_PREF_LIVE_HIDDEN_WINDOWS,
&live_hidden_windows,
FALSE,
},
{ NULL, 0, NULL, FALSE }, { NULL, 0, NULL, FALSE },
}; };
@ -1827,6 +1836,8 @@ meta_preference_to_string (MetaPreference pref)
case META_PREF_CLUTTER_PLUGINS: case META_PREF_CLUTTER_PLUGINS:
return "CLUTTER_PLUGINS"; return "CLUTTER_PLUGINS";
#endif #endif
case META_PREF_LIVE_HIDDEN_WINDOWS:
return "LIVE_HIDDEN_WINDOWS";
} }
return "(unknown)"; return "(unknown)";
@ -2962,6 +2973,34 @@ meta_prefs_set_clutter_plugins (GSList *list)
} }
#endif #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 #ifndef HAVE_GCONF
static void static void
init_button_layout(void) init_button_layout(void)

View File

@ -772,6 +772,17 @@ meta_screen_manage_all_windows (MetaScreen *screen)
} }
meta_stack_thaw (screen->stack); 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_foreach (windows, (GFunc)g_free, NULL);
g_list_free (windows); g_list_free (windows);

View File

@ -241,6 +241,12 @@ get_standalone_layer (MetaWindow *window)
MetaStackLayer layer; MetaStackLayer layer;
gboolean focused_transient = FALSE; gboolean focused_transient = FALSE;
if (window->hidden)
{
layer = META_LAYER_DESKTOP;
return layer;
}
switch (window->type) switch (window->type)
{ {
case META_WINDOW_DESKTOP: case META_WINDOW_DESKTOP:
@ -329,7 +335,8 @@ compute_layer (MetaWindow *window)
* windows getting in fullscreen layer if any terminal is * windows getting in fullscreen layer if any terminal is
* fullscreen. * fullscreen.
*/ */
if (WINDOW_HAS_TRANSIENT_TYPE(window) && if (window->layer != META_LAYER_DESKTOP &&
WINDOW_HAS_TRANSIENT_TYPE(window) &&
(window->xtransient_for == None || (window->xtransient_for == None ||
window->transient_parent_is_root_window)) window->transient_parent_is_root_window))
{ {
@ -854,7 +861,6 @@ stack_do_relayer (MetaStack *stack)
meta_topic (META_DEBUG_STACK, meta_topic (META_DEBUG_STACK,
"Window %s moved from layer %u to %u\n", "Window %s moved from layer %u to %u\n",
w->desc, old_layer, w->layer); w->desc, old_layer, w->layer);
stack->need_resort = TRUE; stack->need_resort = TRUE;
stack->need_constrain = TRUE; stack->need_constrain = TRUE;
/* don't need to constrain as constraining /* don't need to constrain as constraining

View File

@ -150,6 +150,11 @@ struct _MetaWindow
*/ */
guint mapped : 1; 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 /* Iconic is the state in WM_STATE; happens for workspaces/shading
* in addition to minimize * in addition to minimize
*/ */
@ -393,11 +398,6 @@ void meta_window_change_workspace (MetaWindow *window,
void meta_window_stick (MetaWindow *window); void meta_window_stick (MetaWindow *window);
void meta_window_unstick (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_internal (MetaWindow *window);
void meta_window_make_fullscreen (MetaWindow *window); void meta_window_make_fullscreen (MetaWindow *window);
void meta_window_unmake_fullscreen (MetaWindow *window); void meta_window_unmake_fullscreen (MetaWindow *window);

View File

@ -472,6 +472,7 @@ meta_window_new_with_attrs (MetaDisplay *display,
window->tab_unminimized = FALSE; window->tab_unminimized = FALSE;
window->iconic = FALSE; window->iconic = FALSE;
window->mapped = attrs->map_state != IsUnmapped; window->mapped = attrs->map_state != IsUnmapped;
window->hidden = 0;
/* if already mapped, no need to worry about focus-on-first-time-showing */ /* if already mapped, no need to worry about focus-on-first-time-showing */
window->showing_for_first_time = !window->mapped; window->showing_for_first_time = !window->mapped;
/* if already mapped we don't want to do the placement thing */ /* 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); XMapWindow (window->display->xdisplay, window->xwindow);
meta_error_trap_pop (window->display, FALSE); meta_error_trap_pop (window->display, FALSE);
did_show = TRUE; did_show = TRUE;
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 (window->was_minimized) if (did_show && window->was_minimized)
{ {
MetaRectangle window_rect; MetaRectangle window_rect;
MetaRectangle icon_rect; MetaRectangle icon_rect;
@ -2261,7 +2276,6 @@ meta_window_show (MetaWindow *window)
NULL, NULL); NULL, NULL);
} }
} }
}
if (window->iconic) if (window->iconic)
{ {
@ -2315,27 +2329,77 @@ meta_window_hide (MetaWindow *window)
did_hide = FALSE; did_hide = FALSE;
if (window->frame && window->frame->mapped) if (meta_prefs_get_live_hidden_windows ())
{ {
meta_topic (META_DEBUG_WINDOW_STATE, "Frame actually needs unmap\n"); gboolean was_mapped;
window->frame->mapped = FALSE;
meta_ui_unmap_frame (window->screen->ui, window->frame->xwindow);
did_hide = TRUE;
}
if (window->mapped) if (window->hidden)
{ return;
meta_topic (META_DEBUG_WINDOW_STATE,
"%s actually needs unmap\n", window->desc); was_mapped = window->mapped;
meta_topic (META_DEBUG_WINDOW_STATE,
"Incrementing unmaps_pending on %s for hide\n", if (!was_mapped)
window->desc); meta_window_show (window);
window->mapped = FALSE;
window->unmaps_pending += 1; window->hidden = TRUE;
meta_error_trap_push (window->display);
XUnmapWindow (window->display->xdisplay, window->xwindow);
meta_error_trap_pop (window->display, FALSE);
did_hide = 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) if (!window->iconic)
@ -7974,7 +8038,7 @@ meta_window_update_layer (MetaWindow *window)
meta_stack_freeze (window->screen->stack); meta_stack_freeze (window->screen->stack);
group = meta_window_get_group (window); group = meta_window_get_group (window);
if (group) if (!window->hidden && group)
meta_group_update_layers (group); meta_group_update_layers (group);
else else
meta_stack_update_layer (window->screen->stack, window); 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; return window->on_all_workspaces;
} }
gboolean
meta_window_is_hidden (MetaWindow *window)
{
return window->hidden;
}

View File

@ -65,11 +65,6 @@ void meta_workspace_remove_window (MetaWorkspace *workspace,
MetaWindow *window); MetaWindow *window);
void meta_workspace_relocate_windows (MetaWorkspace *workspace, void meta_workspace_relocate_windows (MetaWorkspace *workspace,
MetaWorkspace *new_home); 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); GList* meta_workspace_list_windows (MetaWorkspace *workspace);
void meta_workspace_invalidate_work_area (MetaWorkspace *workspace); void meta_workspace_invalidate_work_area (MetaWorkspace *workspace);

View File

@ -306,7 +306,19 @@ meta_workspace_queue_calc_showing (MetaWorkspace *workspace)
tmp = workspace->windows; tmp = workspace->windows;
while (tmp != NULL) 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; tmp = tmp->next;
} }

View File

@ -57,10 +57,13 @@ struct _MutterWindow
GType mutter_window_get_type (void); GType mutter_window_get_type (void);
Window mutter_window_get_x_window (MutterWindow *mcw); Window mutter_window_get_x_window (MutterWindow *mcw);
MetaCompWindowType mutter_window_get_window_type (MutterWindow *mcw); MetaCompWindowType mutter_window_get_window_type (MutterWindow *mcw);
gint mutter_window_get_workspace (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 */ /* Compositor API */
MetaCompositor *mutter_new (MetaDisplay *display); 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_stage_for_screen (MetaScreen *screen);
ClutterActor * mutter_get_overlay_group_for_screen (MetaScreen *screen); ClutterActor * mutter_get_overlay_group_for_screen (MetaScreen *screen);
Window mutter_get_overlay_window (MetaScreen *screen); Window mutter_get_overlay_window (MetaScreen *screen);
GList * mutter_get_windows (MetaScreen *screen);
#endif #endif

View File

@ -127,6 +127,11 @@ meta_compositor_switch_workspace (MetaCompositor *compositor,
MetaWorkspace *from, MetaWorkspace *from,
MetaWorkspace *to, MetaWorkspace *to,
MetaMotionDirection direction); MetaMotionDirection direction);
void
meta_compositor_ensure_stack_order (MetaCompositor *compositor,
MetaScreen *screen);
#endif #endif

View File

@ -225,4 +225,7 @@ void
mutter_plugin_set_stage_input_area (MutterPlugin *plugin, mutter_plugin_set_stage_input_area (MutterPlugin *plugin,
gint x, gint y, gint width, gint height); gint x, gint y, gint width, gint height);
GList *
mutter_plugin_get_windows (MutterPlugin *plugin);
#endif /* MUTTER_PLUGIN_H_ */ #endif /* MUTTER_PLUGIN_H_ */

View File

@ -62,8 +62,9 @@ typedef enum
META_PREF_COMPOSITING_MANAGER, META_PREF_COMPOSITING_MANAGER,
#ifdef WITH_CLUTTER #ifdef WITH_CLUTTER
META_PREF_CLUTTER_DISABLED, META_PREF_CLUTTER_DISABLED,
META_PREF_CLUTTER_PLUGINS META_PREF_CLUTTER_PLUGINS,
#endif #endif
META_PREF_LIVE_HIDDEN_WINDOWS,
} MetaPreference; } MetaPreference;
typedef void (* MetaPrefsChangedFunc) (MetaPreference pref, typedef void (* MetaPrefsChangedFunc) (MetaPreference pref,
@ -139,6 +140,10 @@ GSList * meta_prefs_get_clutter_plugins (void);
void meta_prefs_set_clutter_plugins (GSList *list); void meta_prefs_set_clutter_plugins (GSList *list);
#endif #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 /* 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 * difficult (or perhaps impossible) to add the suffixes using the current
* system. It needs some more thought, perhaps after the current system * system. It needs some more thought, perhaps after the current system

View File

@ -58,4 +58,10 @@ MetaWindowType meta_window_get_type (MetaWindow *window);
Atom meta_window_get_type_atom (MetaWindow *window); Atom meta_window_get_type_atom (MetaWindow *window);
MetaWorkspace *meta_window_get_workspace (MetaWindow *window); MetaWorkspace *meta_window_get_workspace (MetaWindow *window);
gboolean meta_window_is_on_all_workspaces (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 #endif

View File

@ -57,4 +57,8 @@ int meta_workspace_index (MetaWorkspace *workspace);
MetaScreen *meta_workspace_get_screen (MetaWorkspace *workspace); MetaScreen *meta_workspace_get_screen (MetaWorkspace *workspace);
void meta_workspace_get_work_area_all_xineramas (MetaWorkspace *workspace, void meta_workspace_get_work_area_all_xineramas (MetaWorkspace *workspace,
MetaRectangle *area); MetaRectangle *area);
void meta_workspace_activate (MetaWorkspace *workspace, guint32 timestamp);
void meta_workspace_activate_with_focus (MetaWorkspace *workspace,
MetaWindow *focus_this,
guint32 timestamp);
#endif #endif

View File

@ -370,6 +370,22 @@
</locale> </locale>
</schema> </schema>
<schema>
<key>/schemas/apps/metacity/general/live_hidden_windows</key>
<applyto>/apps/metacity/general/live_hidden_windows</applyto>
<owner>metacity</owner>
<type>bool</type>
<default>false</default>
<locale name="C">
<short>Live Hidden Windows</short>
<long>
Determines whether hidden windows (i.e., minimized windows and
windows on other workspaces than the current one) should be kept
alive.
</long>
</locale>
</schema>
<schema> <schema>
<key>/schemas/apps/metacity/workspace_names/name</key> <key>/schemas/apps/metacity/workspace_names/name</key>
<applyto>/apps/metacity/workspace_names/name_1</applyto> <applyto>/apps/metacity/workspace_names/name_1</applyto>