Attached in a replacement for clutter_stage_glx_realize. It makes it possible to use Clutter with true per pixel alpha and as OSD on top of another video window.

Jörn Reder wrote:
Emmanuele Bassi wrote:

if you provide a patch for the clutter-perl bindings to expose those
methods (under the Clutter::X11 namespace, for instance) and you verify
that they work, then I'll consider pushing it upstream.

[...]

what you need to do is create a small C library that implements a
subclass of ClutterStage and calls clutter_x11_stage_set_foreign()
inside the realize() virtual function -- after you chained up to the
parent implementation.

then write the Perl bindings using the perl-Glib machinery that
clutter-perl uses, and use the custom class inside your Perl code.

Thanks for your quick answer!

Since my C/glib knowledge is very limited (subclassing gobject's is beyond my scope), I tried quick and dirty binding clutter_x11_set_stage_foreign() in ClutterStage.xs to see if this is basically working (using Clutter 0.820 from CPAN).

This is the xsub I added:

--snip--

gboolean
clutter_stage_set_foreign_x11_window(ClutterStage *stage, int xwindow)
    CODE:
        RETVAL = clutter_x11_set_stage_foreign(stage, (Window) xwindow);
    OUTPUT:
        RETVAL

--snip--

And used this small script to test:

--snip--

#!/usr/bin/perl

use strict;
use Gtk2 '-init';
use Clutter '-init';
use Glib qw(TRUE FALSE);

main: {
    my $window = Gtk2::Window->new('toplevel');
    $window->set_default_size(800, 400);
    $window->set_decorated(0);

    $window->signal_connect('delete_event', sub { Gtk2->main_quit; });

    my $colormap = $window->get_screen->get_rgba_colormap;
    $window->set_colormap($colormap) if $colormap;
    $window->show_all;

    my $xid = $window->window->get_xid;
    print "xid=$xid\n";

    my $stage = Clutter::Stage->get_default();
    $stage->set_foreign_x11_window($xid);
    $stage->set_color(Clutter::Color->parse('DarkBlue'));
    $stage->set_opacity(128);

    Gtk2->main;
}

--snip--

That gives me a warning:

  (./osd-test-foreign.pl:20579): ClutterX11-WARNING **: Unable to retrieve the 
new window geometry

and of course no active clutter stage at all ;)

The warning is issued by clutter-stage-x11.c on the check whether all conditions of the target X11 window are met:

  if (clutter_x11_untrap_x_errors () ||
      !status ||
      width == 0 || height == 0 ||
      depth != stage_x11->xvisinfo->depth)
    {
      g_warning ("Unable to retrieve the new window geometry");
      return FALSE;
    }

I added some debugging output and found out that the depth condition is
the problem:

  depth                      == 32
  stage_x11->xvisinfo->depth == 24

So my X11 window has an alpha channel while the clutter stage has not.

Is this due to my "quick and dirty" approach or did I hit another problem?

Thanks,

Jörn

static void
clutter_stage_glx_realize (ClutterActor *actor)
{
  ClutterStageX11   *stage_x11 = CLUTTER_STAGE_X11 (actor);
  ClutterStageGLX   *stage_glx = CLUTTER_STAGE_GLX (actor);
  ClutterBackendGLX *backend_glx;
  ClutterBackendX11 *backend_x11;
  gboolean           is_offscreen;

  CLUTTER_NOTE (MISC, "Realizing main stage");

  g_object_get (stage_x11->wrapper, "offscreen", &is_offscreen, NULL);

  backend_glx = CLUTTER_BACKEND_GLX (clutter_get_default_backend ());
  backend_x11 = CLUTTER_BACKEND_X11 (clutter_get_default_backend ());

  Colormap colormap = 0;
  int eventBase, errorBase;

  if (G_LIKELY (!is_offscreen))
    {
      int gl_attributes[] = 
        {
          GLX_RGBA, 
          GLX_DOUBLEBUFFER,
          GLX_RED_SIZE, 1,
          GLX_GREEN_SIZE, 1,
          GLX_BLUE_SIZE, 1,
          GLX_STENCIL_SIZE, 1,
          0
        };

      if (stage_x11->xvisinfo)
        {
          XFree (stage_x11->xvisinfo);
          stage_x11->xvisinfo = None;
        }

      if (XRenderQueryExtension(stage_x11->xdpy, &eventBase, &errorBase)) {
          int i, nvi;
          XVisualInfo templ;
          templ.screen  = DefaultScreen(stage_x11->xdpy);
          templ.depth   = 32;
          templ.class = TrueColor;
          XVisualInfo *xvi = XGetVisualInfo(stage_x11->xdpy, VisualScreenMask | 
VisualDepthMask | VisualClassMask, &templ, &nvi);

          for (i = 0; i < nvi; ++i) {
              XRenderPictFormat *format = 
XRenderFindVisualFormat(stage_x11->xdpy, xvi[i].visual);
              if (format->type == PictTypeDirect && format->direct.alphaMask) {
                  stage_x11->xvisinfo = g_memdup(&xvi[i], sizeof(Visual));
                  colormap = XCreateColormap(stage_x11->xdpy, 
RootWindow(stage_x11->xdpy, DefaultScreen(stage_x11->xdpy)), xvi[i].visual, 
AllocNone);
                  break;
              }
          }
      }

      /* The following check seems strange */
      if (stage_x11->xvisinfo == None)
        stage_x11->xvisinfo = glXChooseVisual (stage_x11->xdpy,
                                               stage_x11->xscreen,
                                               gl_attributes);
      if (!stage_x11->xvisinfo)
        {
          g_critical ("Unable to find suitable GL visual.");
          goto fail;
        }

      if (stage_x11->xwin == None)
        {
          XSetWindowAttributes xattr;
          unsigned long mask;

          CLUTTER_NOTE (MISC, "Creating stage X window");

          /* window attributes */  
          xattr.background_pixel = WhitePixel (stage_x11->xdpy,
                                               stage_x11->xscreen);
          xattr.border_pixel = 0;
          xattr.colormap = colormap ? colormap : 
                           XCreateColormap (stage_x11->xdpy, 
                                            stage_x11->xwin_root,
                                            stage_x11->xvisinfo->visual,
                                            AllocNone);
          mask = CWBorderPixel | CWColormap;
          stage_x11->xwin = XCreateWindow (stage_x11->xdpy,
                                           stage_x11->xwin_root,
                                           0, 0,
                                           stage_x11->xwin_width,
                                           stage_x11->xwin_height,
                                           0,
                                           stage_x11->xvisinfo->depth,
                                           InputOutput,
                                           stage_x11->xvisinfo->visual,
                                           mask, &xattr);
        }

      if (clutter_x11_has_event_retrieval())
        {
          if (clutter_x11_has_xinput())
            {
              XSelectInput (stage_x11->xdpy, stage_x11->xwin,
                            StructureNotifyMask |
                            FocusChangeMask |
                            ExposureMask |
                            KeyPressMask | KeyReleaseMask |
                            PropertyChangeMask);
#ifdef USE_XINPUT          
              _clutter_x11_select_events (stage_x11->xwin);
#endif
            }
          else
            XSelectInput (stage_x11->xdpy, stage_x11->xwin,
                          StructureNotifyMask |
                          FocusChangeMask |
                          ExposureMask |
                          PointerMotionMask |
                          KeyPressMask | KeyReleaseMask |
                          ButtonPressMask | ButtonReleaseMask |
                          PropertyChangeMask);
        }

      /* no user resize.. */
      clutter_stage_x11_fix_window_size (stage_x11);
      clutter_stage_x11_set_wm_protocols (stage_x11);

      if (G_UNLIKELY (backend_glx->gl_context == None))
        {
          CLUTTER_NOTE (GL, "Creating GL Context");
          backend_glx->gl_context = glXCreateContext (stage_x11->xdpy, 
                                                      stage_x11->xvisinfo, 
                                                      0,
                                                      True);

          if (backend_glx->gl_context == None)
            {
              g_critical ("Unable to create suitable GL context.");
              goto fail;
            }
        }

      CLUTTER_NOTE (BACKEND, "Marking stage as realized");
      CLUTTER_ACTOR_SET_FLAGS (stage_x11, CLUTTER_ACTOR_REALIZED);
    }
  else
    {
      int gl_attributes[] = {
        GLX_DEPTH_SIZE,    0,
        GLX_ALPHA_SIZE,    0,
        GLX_RED_SIZE, 1,
        GLX_GREEN_SIZE, 1,
        GLX_BLUE_SIZE, 1,
        GLX_USE_GL,
        GLX_RGBA,
        0
      };

      if (stage_x11->xvisinfo)
         XFree (stage_x11->xvisinfo);

      stage_x11->xvisinfo = NULL;

      CLUTTER_NOTE (GL, "glXChooseVisual");
      stage_x11->xvisinfo = glXChooseVisual (stage_x11->xdpy,
                                             stage_x11->xscreen,
                                             gl_attributes);
      if (!stage_x11->xvisinfo)
        {
          g_critical ("Unable to find suitable GL visual.");
          goto fail;
        }

      stage_x11->xpixmap = XCreatePixmap (stage_x11->xdpy,
                                          stage_x11->xwin_root,
                                          stage_x11->xwin_width, 
                                          stage_x11->xwin_height,
                                          DefaultDepth (stage_x11->xdpy,
                                                        stage_x11->xscreen));

      stage_glx->glxpixmap = glXCreateGLXPixmap (stage_x11->xdpy,
                                                 stage_x11->xvisinfo,
                                                 stage_x11->xpixmap);

      if (backend_glx->gl_context == None)
        {
          CLUTTER_NOTE (GL, "Creating GL Context");

          /* FIXME: we probably need a seperate offscreen context here
           * - though it likely makes most sense to drop offscreen stages
           * and rely on FBO's instead and GLXPixmaps seems mostly broken
           * anyway..
          */
          backend_glx->gl_context =  glXCreateContext (stage_x11->xdpy, 
                                                       stage_x11->xvisinfo, 
                                                       0,
                                                       False);

          if (backend_glx->gl_context == None)
            {
              g_critical ("Unable to create suitable GL context.");
              goto fail;
            }
        }

      CLUTTER_NOTE (BACKEND, "Marking stage as realized");
      CLUTTER_ACTOR_SET_FLAGS (stage_x11, CLUTTER_ACTOR_REALIZED);
    }

  /* we need to chain up to the X11 stage implementation in order to
   * set the window state in case we set it before realizing the stage
   */
  CLUTTER_ACTOR_CLASS (clutter_stage_glx_parent_class)->realize (actor);
  return;
  
fail:
  CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
}

Reply via email to