From 9ceccaf38295d73b729bc3a6777fa6b948fa4007 Mon Sep 17 00:00:00 2001 From: "Owen W. Taylor" Date: Thu, 8 May 2014 18:44:15 -0400 Subject: [PATCH] Add support for quad-buffer stereo Track the stereo status of windows using the new EXT_stereo_tree GLX extension. When stereo is enabled or disabled, a restart is triggered via meta_restart() after a timeout, setting a _META_ENABLE_STEREO property on the root window to indicate whether we should turn on a stereo stage for clutter. The property avoids a loop, since we need to enable stereo *before* initializing Clutter and GL, but we need GL to figure out whether we have stereo windows. Stereo windows are drawn to the stage using new functionality in Cogl to setup a stereo context, select which buffer to draw to, and draw either the left or right buffer of a stereo texture_from_pixmap. --- src/Makefile.am | 2 + src/compositor/compositor-private.h | 10 ++ src/compositor/compositor.c | 127 +++++++++++++++++++- src/compositor/meta-shaped-texture-private.h | 37 ++++++ src/compositor/meta-shaped-texture.c | 166 ++++++++++++++++++++------- src/compositor/meta-window-actor-private.h | 7 ++ src/compositor/meta-window-actor.c | 27 ++++- src/core/main.c | 2 + src/core/stereo.c | 144 +++++++++++++++++++++++ src/core/stereo.h | 28 +++++ src/meta/meta-shaped-texture.h | 3 +- 11 files changed, 508 insertions(+), 45 deletions(-) create mode 100644 src/compositor/meta-shaped-texture-private.h create mode 100644 src/core/stereo.c create mode 100644 src/core/stereo.h diff --git a/src/Makefile.am b/src/Makefile.am index d664b54..1d2422e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -122,6 +122,8 @@ libmutter_la_SOURCES = \ core/restart.c \ core/session.c \ core/session.h \ + core/stereo.c \ + core/stereo.h \ core/stack.c \ core/stack.h \ core/stack-tracker.c \ diff --git a/src/compositor/compositor-private.h b/src/compositor/compositor-private.h index acb8d3c..b4c5c16 100644 --- a/src/compositor/compositor-private.h +++ b/src/compositor/compositor-private.h @@ -28,6 +28,8 @@ struct _MetaCompositor gint64 server_time_query_time; gint64 server_time_offset; + int glx_opcode; + guint server_time_is_monotonic_time : 1; guint show_redraw : 1; guint debug : 1; @@ -56,6 +58,9 @@ struct _MetaCompScreen gint switch_workspace_in_progress; + guint stereo_tree_ext : 1; + guint have_stereo_windows : 1; + MetaPluginManager *plugin_mgr; }; @@ -79,4 +84,9 @@ gint64 meta_compositor_monotonic_time_to_server_time (MetaDisplay *display, void meta_check_end_modal (MetaScreen *screen); +gboolean meta_compositor_window_is_stereo (MetaScreen *screen, + Window xwindow); +void meta_compositor_select_stereo_notify (MetaScreen *screen, + Window xwindow); + #endif /* META_COMPOSITOR_PRIVATE_H */ diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c index 539a7a6..fd29e48 100644 --- a/src/compositor/compositor.c +++ b/src/compositor/compositor.c @@ -86,7 +86,8 @@ #include "meta-window-group.h" #include "window-private.h" /* to check window->hidden */ #include "display-private.h" /* for meta_display_lookup_x_window() */ -#include "util-private.h" +#include "stack-tracker.h" +#include "stereo.h" #include #include @@ -579,6 +580,101 @@ redirect_windows (MetaCompositor *compositor, } } +#define GLX_STEREO_TREE_EXT 0x20F5 +#define GLX_STEREO_NOTIFY_MASK_EXT 0x00000001 +#define GLX_STEREO_NOTIFY_EXT 0x00000000 + +typedef struct { + int type; + unsigned long serial; + Bool send_event; + Display *display; + int extension; + int evtype; + Drawable window; + Bool stereo_tree; +} StereoNotifyEvent; + +static gboolean +screen_has_stereo_tree_ext (MetaScreen *screen) +{ +#if 0 + MetaDisplay *display = meta_screen_get_display (screen); + Display *xdisplay = meta_display_get_xdisplay (display); + const char *extensions_string; + + static const char * (*query_extensions_string) (Display *display, + int screen); + + if (query_extensions_string == NULL) + query_extensions_string = + (const char * (*) (Display *, int)) + cogl_get_proc_address ("glXQueryExtensionsString"); + + extensions_string = query_extensions_string (xdisplay, + meta_screen_get_screen_number (screen)); + + return strstr (extensions_string, "EXT_stereo_tree") != 0; +#else + return TRUE; +#endif +} + +#include + +gboolean +meta_compositor_window_is_stereo (MetaScreen *screen, + Window xwindow) +{ + MetaCompScreen *info = meta_screen_get_compositor_data (screen); + MetaDisplay *display = meta_screen_get_display (screen); + Display *xdisplay = meta_display_get_xdisplay (display); + + static int (*query_drawable) (Display *dpy, + Drawable draw, + int attribute, + unsigned int *value); + + if (info->stereo_tree_ext) + { + unsigned int stereo_tree = 0; + + if (query_drawable == NULL) + query_drawable = + (int (*) (Display *, Drawable, int, unsigned int *)) + cogl_get_proc_address ("glXQueryDrawable"); + + query_drawable (xdisplay, xwindow, GLX_STEREO_TREE_EXT, &stereo_tree); + + return stereo_tree != 0; + } + else + return FALSE; +} + +void +meta_compositor_select_stereo_notify (MetaScreen *screen, + Window xwindow) +{ + MetaCompScreen *info = meta_screen_get_compositor_data (screen); + MetaDisplay *display = meta_screen_get_display (screen); + Display *xdisplay = meta_display_get_xdisplay (display); + + static void (*select_event) (Display *dpy, + Drawable draw, + unsigned long event_mask); + + if (info->stereo_tree_ext) + { + if (select_event == NULL) + select_event = + (void (*) (Display *, Drawable, unsigned long)) + cogl_get_proc_address ("glXSelectEvent"); + + select_event (xdisplay, xwindow, GLX_STEREO_NOTIFY_MASK_EXT); + } +} + void meta_compositor_manage_screen (MetaCompositor *compositor, MetaScreen *screen) @@ -609,6 +705,8 @@ meta_compositor_manage_screen (MetaCompositor *compositor, info->output = None; info->windows = NULL; + info->stereo_tree_ext = screen_has_stereo_tree_ext (screen); + meta_screen_set_cm_selection (screen); info->stage = clutter_stage_new (); @@ -995,6 +1093,23 @@ meta_compositor_process_event (MetaCompositor *compositor, DEBUG_TRACE ("meta_compositor_process_event (process_damage)\n"); process_damage (compositor, (XDamageNotifyEvent *) event, window); } + else if (event->type == GenericEvent && + event->xcookie.extension == compositor->glx_opcode) + { + if (event->xcookie.evtype == GLX_STEREO_NOTIFY_EXT) + { + StereoNotifyEvent *stereo_event = (StereoNotifyEvent *)(event->xcookie.data); + window = meta_display_lookup_x_window (compositor->display, stereo_event->window); + + if (window != NULL) + { + MetaWindowActor *window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (window)); + meta_window_actor_stereo_notify (window_actor, stereo_event->stereo_tree); + meta_stack_tracker_queue_sync_stack (window->screen->stack_tracker); + } + } + } + break; } @@ -1209,6 +1324,7 @@ meta_compositor_sync_stack (MetaCompositor *compositor, { GList *old_stack; MetaCompScreen *info = meta_screen_get_compositor_data (screen); + int stereo_window_count = 0; DEBUG_TRACE ("meta_compositor_sync_stack\n"); @@ -1286,12 +1402,16 @@ meta_compositor_sync_stack (MetaCompositor *compositor, * near the front of the other.) */ info->windows = g_list_prepend (info->windows, actor); + if (meta_window_actor_is_stereo (actor)) + stereo_window_count++; stack = g_list_remove (stack, window); old_stack = g_list_remove (old_stack, actor); } sync_actor_stacking (info); + + meta_stereo_set_have_stereo_windows (stereo_window_count > 0); } void @@ -1505,6 +1625,7 @@ meta_compositor_new (MetaDisplay *display) Atom atoms[G_N_ELEMENTS(atom_names)]; MetaCompositor *compositor; Display *xdisplay = meta_display_get_xdisplay (display); + int glx_major_opcode, glx_first_event, glx_first_error; if (!composite_at_least_version (display, 0, 3)) return NULL; @@ -1531,6 +1652,10 @@ meta_compositor_new (MetaDisplay *display) compositor->repaint_func_id = clutter_threads_add_repaint_func (meta_repaint_func, compositor, NULL); + if (XQueryExtension (xdisplay, + "GLX", + &glx_major_opcode, &glx_first_event, &glx_first_error)) + compositor->glx_opcode = glx_major_opcode; return compositor; } diff --git a/src/compositor/meta-shaped-texture-private.h b/src/compositor/meta-shaped-texture-private.h new file mode 100644 index 0000000..c70f6a8 --- /dev/null +++ b/src/compositor/meta-shaped-texture-private.h @@ -0,0 +1,37 @@ +/* + * shaped texture + * + * An actor to draw a texture clipped to a list of rectangles + * + * Authored By Neil Roberts + * + * Copyright (C) 2008 Intel Corporation + * 2013 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef __META_SHAPED_TEXTURE_PRIVATE_H__ +#define __META_SHAPED_TEXTURE_PRIVATE_H__ + +#include + +ClutterActor *meta_shaped_texture_new (void); +gboolean meta_shaped_texture_get_unobscured_bounds (MetaShapedTexture *stex, + cairo_rectangle_int_t *unobscured_bounds); +gboolean meta_shaped_texture_is_obscured (MetaShapedTexture *self); + +#endif diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c index c6239c9..132cb09 100644 --- a/src/compositor/meta-shaped-texture.c +++ b/src/compositor/meta-shaped-texture.c @@ -65,8 +65,10 @@ G_DEFINE_TYPE (MetaShapedTexture, meta_shaped_texture, struct _MetaShapedTexturePrivate { MetaTextureTower *paint_tower; + MetaTextureTower *paint_tower_right; Pixmap pixmap; CoglTexturePixmapX11 *texture; + CoglTexturePixmapX11 *texture_right; CoglTexture *mask_texture; CoglPipeline *pipeline; CoglPipeline *pipeline_unshaped; @@ -75,6 +77,7 @@ struct _MetaShapedTexturePrivate guint tex_width, tex_height; + guint stereo : 1; guint create_mipmaps : 1; }; @@ -103,7 +106,9 @@ meta_shaped_texture_init (MetaShapedTexture *self) priv = self->priv = META_SHAPED_TEXTURE_GET_PRIVATE (self); priv->paint_tower = meta_texture_tower_new (); + priv->paint_tower_right = NULL; /* demand create */ priv->texture = NULL; + priv->texture_right = NULL; priv->mask_texture = NULL; priv->create_mipmaps = TRUE; } @@ -114,13 +119,13 @@ meta_shaped_texture_dispose (GObject *object) MetaShapedTexture *self = (MetaShapedTexture *) object; MetaShapedTexturePrivate *priv = self->priv; - if (priv->paint_tower) - meta_texture_tower_free (priv->paint_tower); - priv->paint_tower = NULL; + g_clear_pointer (&priv->paint_tower, meta_texture_tower_free); + g_clear_pointer (&priv->paint_tower_right, meta_texture_tower_free); g_clear_pointer (&priv->pipeline, cogl_object_unref); g_clear_pointer (&priv->pipeline_unshaped, cogl_object_unref); g_clear_pointer (&priv->texture, cogl_object_unref); + g_clear_pointer (&priv->texture_right, cogl_object_unref); meta_shaped_texture_set_mask_texture (self, NULL); meta_shaped_texture_set_clip_region (self, NULL); @@ -129,11 +134,11 @@ meta_shaped_texture_dispose (GObject *object) } static void -meta_shaped_texture_paint (ClutterActor *actor) +paint_texture (MetaShapedTexture *stex, + CoglTexture *paint_tex) { - MetaShapedTexture *stex = (MetaShapedTexture *) actor; MetaShapedTexturePrivate *priv = stex->priv; - CoglTexture *paint_tex; + ClutterActor *actor = CLUTTER_ACTOR (stex); guint tex_width, tex_height; ClutterActorBox alloc; @@ -145,32 +150,6 @@ meta_shaped_texture_paint (ClutterActor *actor) if (priv->clip_region && cairo_region_is_empty (priv->clip_region)) return; - if (!CLUTTER_ACTOR_IS_REALIZED (CLUTTER_ACTOR (stex))) - clutter_actor_realize (CLUTTER_ACTOR (stex)); - - /* The GL EXT_texture_from_pixmap extension does allow for it to be - * used together with SGIS_generate_mipmap, however this is very - * rarely supported. Also, even when it is supported there - * are distinct performance implications from: - * - * - Updating mipmaps that we don't need - * - Having to reallocate pixmaps on the server into larger buffers - * - * So, we just unconditionally use our mipmap emulation code. If we - * wanted to use SGIS_generate_mipmap, we'd have to query COGL to - * see if it was supported (no API currently), and then if and only - * if that was the case, set the clutter texture quality to HIGH. - * Setting the texture quality to high without SGIS_generate_mipmap - * support for TFP textures will result in fallbacks to XGetImage. - */ - if (priv->create_mipmaps) - paint_tex = meta_texture_tower_get_paint_texture (priv->paint_tower); - else - paint_tex = COGL_TEXTURE (priv->texture); - - if (paint_tex == NULL) - return; - tex_width = priv->tex_width; tex_height = priv->tex_height; @@ -279,6 +258,76 @@ meta_shaped_texture_paint (ClutterActor *actor) } static void +meta_shaped_texture_paint (ClutterActor *actor) +{ + MetaShapedTexture *stex = (MetaShapedTexture *) actor; + MetaShapedTexturePrivate *priv = stex->priv; + CoglFramebuffer *fb; + gboolean stereo; + CoglTexture *paint_tex; + CoglTexture *paint_tex_right; + + if (priv->clip_region && cairo_region_is_empty (priv->clip_region)) + return; + + if (!CLUTTER_ACTOR_IS_REALIZED (CLUTTER_ACTOR (stex))) + clutter_actor_realize (CLUTTER_ACTOR (stex)); + + /* The GL EXT_texture_from_pixmap extension does allow for it to be + * used together with SGIS_generate_mipmap, however this is very + * rarely supported. Also, even when it is supported there + * are distinct performance implications from: + * + * - Updating mipmaps that we don't need + * - Having to reallocate pixmaps on the server into larger buffers + * + * So, we just unconditionally use our mipmap emulation code. If we + * wanted to use SGIS_generate_mipmap, we'd have to query COGL to + * see if it was supported (no API currently), and then if and only + * if that was the case, set the clutter texture quality to HIGH. + * Setting the texture quality to high without SGIS_generate_mipmap + * support for TFP textures will result in fallbacks to XGetImage. + */ + if (priv->create_mipmaps) + paint_tex = meta_texture_tower_get_paint_texture (priv->paint_tower); + else + paint_tex = COGL_TEXTURE (priv->texture); + + if (paint_tex == NULL) + return; + + fb = cogl_get_draw_framebuffer (); + + stereo = priv->stereo && cogl_framebuffer_get_is_stereo (fb); + + if (stereo) + { + if (priv->create_mipmaps) + paint_tex_right = meta_texture_tower_get_paint_texture (priv->paint_tower_right); + else + paint_tex_right = COGL_TEXTURE (priv->texture_right); + } + else + paint_tex_right = NULL; + + if (paint_tex != NULL) + { + if (stereo) + cogl_framebuffer_set_stereo_mode (fb, COGL_STEREO_LEFT); + paint_texture (stex, paint_tex); + if (stereo) + cogl_framebuffer_set_stereo_mode (fb, COGL_STEREO_BOTH); + } + + if (paint_tex_right != NULL) + { + cogl_framebuffer_set_stereo_mode (fb, COGL_STEREO_RIGHT); + paint_texture (stex, paint_tex_right); + cogl_framebuffer_set_stereo_mode (fb, COGL_STEREO_BOTH); + } +} + +static void meta_shaped_texture_pick (ClutterActor *actor, const ClutterColor *color) { @@ -392,6 +441,12 @@ meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex, base_texture = create_mipmaps ? COGL_TEXTURE (priv->texture) : NULL; meta_texture_tower_set_base_texture (priv->paint_tower, base_texture); + + if (priv->stereo) + { + base_texture = create_mipmaps ? COGL_TEXTURE (priv->texture_right) : NULL; + meta_texture_tower_set_base_texture (priv->paint_tower_right, base_texture); + } } } @@ -435,13 +490,16 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex, x, y, width, height); meta_texture_tower_update_area (priv->paint_tower, x, y, width, height); + if (priv->stereo) + meta_texture_tower_update_area (priv->paint_tower_right, x, y, width, height); clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stex), &clip); } static void set_cogl_texture (MetaShapedTexture *stex, - CoglTexturePixmapX11 *cogl_tex) + CoglTexturePixmapX11 *cogl_tex, + gboolean stereo) { MetaShapedTexturePrivate *priv; guint width, height; @@ -452,9 +510,17 @@ set_cogl_texture (MetaShapedTexture *stex, if (priv->texture != NULL) cogl_object_unref (priv->texture); + if (priv->texture_right != NULL) + cogl_object_unref (priv->texture_right); + priv->stereo = stereo; priv->texture = cogl_tex; + if (priv->stereo) + priv->texture_right = cogl_texture_pixmap_x11_new_right ((CoglTexturePixmapX11 *)cogl_tex); + else + priv->texture_right = NULL; + if (priv->pipeline != NULL) cogl_pipeline_set_layer_texture (priv->pipeline, 0, COGL_TEXTURE (cogl_tex)); @@ -493,7 +559,8 @@ set_cogl_texture (MetaShapedTexture *stex, */ void meta_shaped_texture_set_pixmap (MetaShapedTexture *stex, - Pixmap pixmap) + Pixmap pixmap, + gboolean stereo) { MetaShapedTexturePrivate *priv; @@ -501,23 +568,44 @@ meta_shaped_texture_set_pixmap (MetaShapedTexture *stex, priv = stex->priv; - if (priv->pixmap == pixmap) + if (priv->pixmap == pixmap && priv->stereo == stereo) return; priv->pixmap = pixmap; + priv->stereo = stereo; if (pixmap != None) { CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); - set_cogl_texture (stex, cogl_texture_pixmap_x11_new (ctx, pixmap, FALSE, NULL)); + CoglTexturePixmapX11 *texture; + if (priv->stereo) + texture = cogl_texture_pixmap_x11_new_left (ctx, pixmap, FALSE, NULL); + else + texture = cogl_texture_pixmap_x11_new (ctx, pixmap, FALSE, NULL); + set_cogl_texture (stex, texture, stereo); } else - set_cogl_texture (stex, NULL); + set_cogl_texture (stex, NULL, stereo); + + if (priv->stereo) + { + if (priv->paint_tower_right == NULL) + priv->paint_tower_right = meta_texture_tower_new (); + } + else + { + g_clear_pointer (&priv->paint_tower_right, meta_texture_tower_free); + } if (priv->create_mipmaps) - meta_texture_tower_set_base_texture (priv->paint_tower, - COGL_TEXTURE (priv->texture)); + { + meta_texture_tower_set_base_texture (priv->paint_tower, + COGL_TEXTURE (priv->texture)); + if (priv->stereo) + meta_texture_tower_set_base_texture (priv->paint_tower_right, + COGL_TEXTURE (priv->texture_right)); + } } /** diff --git a/src/compositor/meta-window-actor-private.h b/src/compositor/meta-window-actor-private.h index 90a9e35..914a8db 100644 --- a/src/compositor/meta-window-actor-private.h +++ b/src/compositor/meta-window-actor-private.h @@ -66,4 +66,11 @@ void meta_window_actor_reset_visible_regions (MetaWindowActor *self); void meta_window_actor_effect_completed (MetaWindowActor *actor, gulong event); +void meta_window_actor_stereo_notify (MetaWindowActor *actor, + gboolean stereo_tree); + +gboolean meta_window_actor_is_stereo (MetaWindowActor *actor); + +void meta_window_actor_detach (MetaWindowActor *self); + #endif /* META_WINDOW_ACTOR_PRIVATE_H */ diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c index 42d1257..1a11d0e 100644 --- a/src/compositor/meta-window-actor.c +++ b/src/compositor/meta-window-actor.c @@ -108,6 +108,7 @@ struct _MetaWindowActorPrivate guint visible : 1; guint mapped : 1; guint argb32 : 1; + guint stereo : 1; guint disposed : 1; guint redecorating : 1; @@ -175,7 +176,6 @@ static gboolean meta_window_actor_get_paint_volume (ClutterActor *actor, ClutterPaintVolume *volume); -static void meta_window_actor_detach (MetaWindowActor *self); static gboolean meta_window_actor_has_shadow (MetaWindowActor *self); static void meta_window_actor_handle_updates (MetaWindowActor *self); @@ -362,6 +362,9 @@ meta_window_actor_constructed (GObject *object) if (format && format->type == PictTypeDirect && format->direct.alphaMask) priv->argb32 = TRUE; + priv->stereo = meta_compositor_window_is_stereo (screen, xwindow); + meta_compositor_select_stereo_notify (screen, xwindow); + if (!priv->actor) { priv->actor = meta_shaped_texture_new (); @@ -1218,7 +1221,7 @@ meta_window_actor_effect_completed (MetaWindowActor *self, * when the window is unmapped or when we want to update to a new * pixmap for a new size. */ -static void +void meta_window_actor_detach (MetaWindowActor *self) { MetaWindowActorPrivate *priv = self->priv; @@ -1234,7 +1237,7 @@ meta_window_actor_detach (MetaWindowActor *self) * pixmap, but it certainly doesn't work with current DRI/Mesa */ meta_shaped_texture_set_pixmap (META_SHAPED_TEXTURE (priv->actor), - None); + None, FALSE); cogl_flush(); XFreePixmap (xdisplay, priv->back_pixmap); @@ -1823,7 +1826,7 @@ check_needs_pixmap (MetaWindowActor *self) FALSE); meta_shaped_texture_set_pixmap (META_SHAPED_TEXTURE (priv->actor), - priv->back_pixmap); + priv->back_pixmap, priv->stereo); texture = meta_shaped_texture_get_texture (META_SHAPED_TEXTURE (priv->actor)); @@ -2575,3 +2578,19 @@ meta_window_actor_set_updates_frozen (MetaWindowActor *self, meta_window_actor_thaw (self); } } + +void +meta_window_actor_stereo_notify (MetaWindowActor *self, + gboolean stereo_tree) +{ + MetaWindowActorPrivate *priv = self->priv; + + priv->stereo = stereo_tree; + meta_window_actor_detach (self); +} + +gboolean +meta_window_actor_is_stereo (MetaWindowActor *self) +{ + return self->priv->stereo; +} diff --git a/src/core/main.c b/src/core/main.c index d5bde44..8092afc 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -444,6 +444,8 @@ meta_init (void) meta_restart_init (); + meta_stereo_init (); + /* * Clutter can only be initialized after the UI. */ diff --git a/src/core/stereo.c b/src/core/stereo.c new file mode 100644 index 0000000..d16a210 --- /dev/null +++ b/src/core/stereo.c @@ -0,0 +1,144 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/* + * Copyright (C) 2014 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/* + * SECTION:stereo + * @short_description: Keep track of whether we are a stereo compositor + * + * With GLX, we need to use a different GL context for stereo and + * non-stereo support. Support for multiple GL contexts is unfinished + * in Cogl and entirely lacking in Clutter, so it's by far easier + * to just restart Mutter when we detect a stereo window. + * + * A property _MUTTER_ENABLE_STEREO is maintained on the root window + * to know whether we should initialize clutter for stereo or not. + * When the presence or absence of stereo windows mismatches the + * stereo-enabled state for a sufficiently long period of time, + * we restart Mutter. + */ + +#include + +#include +#include +#include + +#include +#include "ui.h" +#include +#include "display-private.h" +#include "stereo.h" + +static guint stereo_switch_id = 0; +static gboolean stereo_enabled = FALSE; +/* -1 so the first time meta_stereo_set_have_stereo_windows() is called + * we avoid the short-circuit and set up a timeout to restart + * if necessary */ +static gboolean stereo_have_windows = (gboolean)-1; +static gboolean stereo_restart = FALSE; + +#define STEREO_ENABLE_WAIT 1000 +#define STEREO_DISABLE_WAIT 5000 + +void +meta_stereo_init (void) +{ + Display *xdisplay = meta_ui_get_display (); + Window root = DefaultRootWindow (xdisplay); + Atom atom_enable_stereo = XInternAtom (xdisplay, "_MUTTER_ENABLE_STEREO", False); + Atom type; + int format; + unsigned long n_items, bytes_after; + guchar *data; + + XGetWindowProperty (xdisplay, root, atom_enable_stereo, + 0, 1, False, XA_INTEGER, + &type, &format, &n_items, &bytes_after, &data); + if (type == XA_INTEGER) + { + if (format == 32 && n_items == 1 && bytes_after == 0) + { + stereo_enabled = *(long *)data; + } + else + { + meta_warning ("Bad value for _MUTTER_ENABLE_STEREO property\n"); + } + + XFree (data); + } + else if (type != None) + { + meta_warning ("Bad type for _MUTTER_ENABLE_STEREO property\n"); + } + + meta_verbose ("On startup, _MUTTER_ENABLE_STEREO=%s", + stereo_enabled ? "yes" : "no"); + clutter_x11_set_use_stereo_stage (stereo_enabled); +} + +static gboolean +meta_stereo_switch (gpointer data) +{ + stereo_switch_id = 0; + stereo_restart = TRUE; + + meta_restart (stereo_have_windows ? + _("Enabling stereo...") : + _("Disabling stereo...")); + + return FALSE; +} + +void +meta_stereo_set_have_stereo_windows (gboolean have_windows) +{ + have_windows = have_windows != FALSE; + + if (!stereo_restart && have_windows != stereo_have_windows) + { + MetaDisplay *display = meta_get_display (); + Display *xdisplay = meta_display_get_xdisplay (display); + Window root = DefaultRootWindow (xdisplay); + Atom atom_enable_stereo = XInternAtom (xdisplay, "_MUTTER_ENABLE_STEREO", False); + long value; + + stereo_have_windows = have_windows; + + if (stereo_have_windows) + meta_verbose ("Detected stereo windows\n"); + else + meta_verbose ("No stereo windows detected\n"); + + value = stereo_have_windows; + XChangeProperty (xdisplay, root, + atom_enable_stereo, XA_INTEGER, 32, + PropModeReplace, (guchar *)&value, 1); + + if (stereo_switch_id != 0) + { + g_source_remove (stereo_switch_id); + stereo_switch_id = 0; + } + + if (stereo_have_windows != stereo_enabled) + stereo_switch_id = g_timeout_add (stereo_have_windows ? STEREO_ENABLE_WAIT : STEREO_DISABLE_WAIT, + meta_stereo_switch, NULL); + } +} diff --git a/src/core/stereo.h b/src/core/stereo.h new file mode 100644 index 0000000..ccd1d70 --- /dev/null +++ b/src/core/stereo.h @@ -0,0 +1,28 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/* + * Copyright (C) 2014 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef META_STEREO_H +#define META_STEREO_H + +void meta_stereo_init (void); +void meta_stereo_set_have_stereo_windows (gboolean have_windows); +gboolean meta_stereo_is_restart (void); +void meta_stereo_finish_restart (void); + +#endif diff --git a/src/meta/meta-shaped-texture.h b/src/meta/meta-shaped-texture.h index 28fb5f6..fcf33c3 100644 --- a/src/meta/meta-shaped-texture.h +++ b/src/meta/meta-shaped-texture.h @@ -76,7 +76,8 @@ void meta_shaped_texture_update_area (MetaShapedTexture *stex, int height); void meta_shaped_texture_set_pixmap (MetaShapedTexture *stex, - Pixmap pixmap); + Pixmap pixmap, + gboolean stereo); CoglTexture * meta_shaped_texture_get_texture (MetaShapedTexture *stex); -- 1.8.3.1