Esempio n. 1
0
 def do_get_property_shm_handle(self, name):
     if not self._use_shm or not CompositeHelper.XShmEnabled:
         return None
     if self._shm_handle and self._shm_handle.get_size()!=self._window.get_size():
         #size has changed!
         #make sure the current wrapper gets garbage collected:
         self._shm_handle.cleanup()
         self._shm_handle = None
     if self._shm_handle is None:
         #make a new one:
         self._shm_handle = XShmWrapper()
         init_ok, retry_window, xshm_failed = self._shm_handle.init(self._window)
         if not init_ok:
             #this handle is not valid, clear it:
             self._shm_handle = None
         if not retry_window:
             #and it looks like it is not worth re-trying this window:
             self._use_shm = False
         if xshm_failed:
             log.warn("disabling XShm support following irrecoverable error")
             CompositeHelper.XShmEnabled = False
     return self._shm_handle
Esempio n. 2
0
class CompositeHelper(AutoPropGObjectMixin, gobject.GObject):

    XShmEnabled = True

    __gsignals__ = {
        "contents-changed": one_arg_signal,

        "xpra-damage-event": one_arg_signal,
        "xpra-unmap-event": one_arg_signal,
        "xpra-configure-event": one_arg_signal,
        "xpra-reparent-event": one_arg_signal,
        }

    __gproperties__ = {
        "contents-handle": (gobject.TYPE_PYOBJECT,
                            "", "", gobject.PARAM_READABLE),
        "shm-handle": (gobject.TYPE_PYOBJECT,
                            "", "", gobject.PARAM_READABLE),
        }

    # This may raise XError.
    def __init__(self, window, already_composited, use_shm=False):
        super(CompositeHelper, self).__init__()
        log("CompositeHelper.__init__(%s,%s)", window, already_composited)
        self._window = window
        self._already_composited = already_composited
        self._listening_to = None
        self._damage_handle = None
        self._use_shm = use_shm
        self._shm_handle = None

    def setup(self):
        xwin = get_xwindow(self._window)
        if not self._already_composited:
            X11Window.XCompositeRedirectWindow(xwin)
        _, _, _, _, self._border_width = X11Window.geometry_with_border(xwin)
        self.invalidate_pixmap()
        self._damage_handle = X11Window.XDamageCreate(xwin)
        log("CompositeHelper.setup() damage handle(%s)=%s", hex(xwin), hex(self._damage_handle))
        add_event_receiver(self._window, self)

    def destroy(self):
        if self._window is None:
            log.warn("composite window %s already destroyed!", self)
            return
        #clear the reference to the window early:
        win = self._window
        xwin = get_xwindow(self._window)
        #Note: invalidate_pixmap()/_cleanup_listening() use self._window, but won't care if it's None
        self._window = None
        remove_event_receiver(win, self)
        self.invalidate_pixmap()
        if not self._already_composited:
            trap.swallow_synced(X11Window.XCompositeUnredirectWindow, xwin)
        if self._damage_handle:
            trap.swallow_synced(X11Window.XDamageDestroy, self._damage_handle)
            self._damage_handle = None
        if self._shm_handle:
            self._shm_handle.cleanup()
            self._shm_handle = None
        #note: this should be redundant since we cleared the
        #reference to self._window and shortcut out in do_get_property_contents_handle
        #but it's cheap anyway
        self.invalidate_pixmap()

    def acknowledge_changes(self):
        if self._damage_handle is not None and self._window is not None:
            #"Synchronously modifies the regions..." so unsynced?
            if not trap.swallow_synced(X11Window.XDamageSubtract, self._damage_handle):
                self.invalidate_pixmap()

    def invalidate_pixmap(self):
        log("invalidating named pixmap")
        if self._listening_to is not None:
            self._cleanup_listening(self._listening_to)
            self._listening_to = None
        self._contents_handle = None

    def _cleanup_listening(self, listening):
        if listening:
            # Don't want to stop listening to self._window!:
            assert self._window is None or self._window not in listening
            for w in listening:
                remove_event_receiver(w, self)

    def do_get_property_shm_handle(self, name):
        if not self._use_shm or not CompositeHelper.XShmEnabled:
            return None
        if self._shm_handle and self._shm_handle.get_size()!=self._window.get_size():
            #size has changed!
            #make sure the current wrapper gets garbage collected:
            self._shm_handle.cleanup()
            self._shm_handle = None
        if self._shm_handle is None:
            #make a new one:
            self._shm_handle = XShmWrapper()
            init_ok, retry_window, xshm_failed = self._shm_handle.init(self._window)
            if not init_ok:
                #this handle is not valid, clear it:
                self._shm_handle = None
            if not retry_window:
                #and it looks like it is not worth re-trying this window:
                self._use_shm = False
            if xshm_failed:
                log.warn("disabling XShm support following irrecoverable error")
                CompositeHelper.XShmEnabled = False
        return self._shm_handle

    def do_get_property_contents_handle(self, name):
        if self._window is None:
            #shortcut out
            return  None
        if self._contents_handle is None:
            log("refreshing named pixmap")
            assert self._listening_to is None
            def set_pixmap():
                # The tricky part here is that the pixmap returned by
                # NameWindowPixmap gets invalidated every time the window's
                # viewable state changes.  ("viewable" here is the X term that
                # means "mapped, and all ancestors are also mapped".)  But
                # there is no X event that will tell you when a window's
                # viewability changes!  Instead we have to find all ancestors,
                # and watch all of them for unmap and reparent events.  But
                # what about races?  I hear you cry.  By doing things in the
                # exact order:
                #   1) select for StructureNotify
                #   2) QueryTree to get parent
                #   3) repeat 1 & 2 up to the root
                #   4) call NameWindowPixmap
                # we are safe.  (I think.)
                listening = []
                e = None
                try:
                    win = get_parent(self._window)
                    while win is not None and win.get_parent() is not None:
                        # We have to use a lowlevel function to manipulate the
                        # event selection here, because SubstructureRedirectMask
                        # does not roundtrip through the GDK event mask
                        # functions.  So if we used them, here, we would clobber
                        # corral window selection masks, and those don't deserve
                        # clobbering.  They are our friends!  X is driving me
                        # slowly mad.
                        X11Window.addXSelectInput(get_xwindow(win), const["StructureNotifyMask"])
                        add_event_receiver(win, self, max_receivers=-1)
                        listening.append(win)
                        win = get_parent(win)
                    handle = xcomposite_name_window_pixmap(self._window)
                except Exception, e:
                    try:
                        self._cleanup_listening(listening)
                    except:
                        pass
                    raise
                if handle is None:
                    log("failed to name a window pixmap for %s: %s", get_xwindow(self._window), e)
                    self._cleanup_listening(listening)
                else:
                    self._contents_handle = handle
                    # Don't save the listening set until after
                    # NameWindowPixmap has succeeded, to maintain our
                    # invariant:
                    self._listening_to = listening
            trap.swallow_synced(set_pixmap)
        return self._contents_handle