Skip to content

Commit

Permalink
#1247: split damage from composite so we can re-use one without the o…
Browse files Browse the repository at this point in the history
…ther

git-svn-id: https://xpra.org/svn/Xpra/trunk@12978 3bb7dfac-3a0b-4e04-842a-767bc560f471
  • Loading branch information
totaam committed Jul 10, 2016
1 parent 212b5ab commit 1b84ed9
Show file tree
Hide file tree
Showing 3 changed files with 292 additions and 221 deletions.
21 changes: 18 additions & 3 deletions src/xpra/x11/bindings/ximage.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -699,12 +699,12 @@ cdef class PixmapWrapper(object):



cdef get_image(Display * display, Pixmap pixmap, unsigned int x, unsigned int y, unsigned int width, unsigned int height):
cdef get_image(Display * display, Drawable drawable, unsigned int x, unsigned int y, unsigned int width, unsigned int height):
cdef XImage* ximage
ximage = XGetImage(display, pixmap, x, y, width, height, AllPlanes, ZPixmap)
ximage = XGetImage(display, drawable, x, y, width, height, AllPlanes, ZPixmap)
#log.info("get_pixels(..) ximage==NULL : %s", ximage==NULL)
if ximage==NULL:
log("get_image(..) failed to get XImage for X11 pixmap %#x", pixmap)
log("get_image(..) failed to get XImage for X11 drawable %#x", drawable)
return None
xi = XImageWrapper(x, y, width, height)
xi.set_image(ximage)
Expand Down Expand Up @@ -733,6 +733,18 @@ cdef xcomposite_name_window_pixmap(Display * xdisplay, Window xwindow):
pw.init(xdisplay, xpixmap, width, height)
return pw

cdef window_pixmap_wrapper(Display *xdisplay, Window xwindow):
cdef Window root_window
cdef int x, y
cdef unsigned width, height, border, depth
status = XGetGeometry(xdisplay, xwindow, &root_window,
&x, &y, &width, &height, &border, &depth)
if status==0:
log("failed to get window dimensions for %#x", xwindow)
return None
cdef PixmapWrapper pw = PixmapWrapper()
pw.init(xdisplay, xwindow, width, height)
return pw

from core_bindings cimport _X11CoreBindings

Expand All @@ -758,3 +770,6 @@ cdef class _XImageBindings(_X11CoreBindings):

def get_xcomposite_pixmap(self, xwindow):
return xcomposite_name_window_pixmap(self.display, xwindow)

def get_xwindow_pixmap_wrapper(self, xwindow):
return window_pixmap_wrapper(self.display, xwindow)
298 changes: 80 additions & 218 deletions src/xpra/x11/gtk2/composite.py
Original file line number Diff line number Diff line change
@@ -1,270 +1,132 @@
# This file is part of Xpra.
# Copyright (C) 2008, 2009 Nathaniel Smith <[email protected]>
# Copyright (C) 2012-2014 Antoine Martin <[email protected]>
# Copyright (C) 2012-2016 Antoine Martin <[email protected]>
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

import os
import gobject

from xpra.log import Logger
from xpra.x11.gtk2.window_damage import WindowDamageHandler
log = Logger("x11", "window")

from xpra.gtk_common.gobject_util import one_arg_signal, AutoPropGObjectMixin
from xpra.x11.gtk2.gdk_bindings import (
add_event_receiver, #@UnresolvedImport
remove_event_receiver, #@UnresolvedImport
get_parent) #@UnresolvedImport
from xpra.gtk_common.error import trap, xsync, XError
from xpra.gtk_common.error import trap

from xpra.x11.gtk2.world_window import get_world_window
from xpra.x11.bindings.ximage import XImageBindings #@UnresolvedImport
XImage = XImageBindings()
from xpra.x11.bindings.window_bindings import constants, X11WindowBindings #@UnresolvedImport
X11Window = X11WindowBindings()
try:
X11Window.ensure_XComposite_support()
has_composite = True
except Exception as e:
log("No XComposite: %s", e)
has_composite = False
X11Window.ensure_XDamage_support()
X11Window.ensure_XComposite_support()


StructureNotifyMask = constants["StructureNotifyMask"]
USE_XSHM = os.environ.get("XPRA_XSHM", "1")=="1"


class CompositeHelper(AutoPropGObjectMixin, gobject.GObject):
class CompositeHelper(WindowDamageHandler, 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,
}
__gsignals__ = WindowDamageHandler.__common_gsignals__.copy()
__gsignals__.update({
#emit:
"contents-changed" : one_arg_signal,
})

# This may raise XError.
def __init__(self, window, skip_compositing=False, use_shm=USE_XSHM):
super(CompositeHelper, self).__init__()
log("CompositeHelper.__init__(%#x, %s, %s)", window.xid, skip_compositing, use_shm)
self._window = window
self._skip_compositing = skip_compositing
def __init__(self, window):
WindowDamageHandler.__init__(self, window)
AutoPropGObjectMixin.__init__(self)
gobject.GObject.__init__(self)
self._listening_to = None
self._damage_handle = None
self._use_shm = use_shm
self._shm_handle = None
self._contents_handle = None

def __repr__(self):
xid = None
if self._window:
xid = self._window.xid
cw = self.client_window
if cw:
xid = cw.xid
return "CompositeHelper(%#x)" % xid

def setup(self):
xwin = self._window.xid
if not self._skip_compositing:
assert has_composite, "no compositing support on this display!"
X11Window.XCompositeRedirectWindow(xwin)
self._border_width = X11Window.geometry_with_border(xwin)[-1]
self.invalidate_pixmap()
self._damage_handle = X11Window.XDamageCreate(xwin)
log("CompositeHelper.setup() damage handle(%#x)=%#x", xwin, self._damage_handle)
add_event_receiver(self._window, self)
X11Window.XCompositeRedirectWindow(self.client_window.xid)
WindowDamageHandler.setup(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 = self._window.xid
#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._skip_compositing:
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._shm_handle:
self._shm_handle.discard()
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 do_destroy(self, window):
trap.swallow_synced(X11Window.XCompositeUnredirectWindow, window)
WindowDamageHandler.do_destroy(self, window)

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

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
# Don't want to stop listening to self.client_window!:
assert self.client_window is None or self.client_window not in listening
for w in listening:
remove_event_receiver(w, self)

def get_shm_handle(self):
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 = XImage.get_XShmWrapper(self._window.xid)
if self._shm_handle is None:
#failed (may retry)
return None
init_ok, retry_window, xshm_failed = self._shm_handle.setup()
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 get_contents_handle(self):
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():
if self._skip_compositing:
self._contents_handle = XImage.get_xwindow_pixmap_wrapper(self._window.xid)
return
# 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:
root = self._window.get_screen().get_root_window()
world = get_world_window().window
win = get_parent(self._window)
while win not in (None, root, world) 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(win.xid, StructureNotifyMask)
add_event_receiver(win, self, max_receivers=-1)
listening.append(win)
win = get_parent(win)
handle = XImage.get_xcomposite_pixmap(self._window.xid)
except Exception as e:
try:
self._cleanup_listening(listening)
except:
pass
raise
if handle is None:
#avoid race during signal exit, which will clear self._window:
win = self._window
xid = 0
if win:
xid = win.xid
log("failed to name a window pixmap for %#x: %s", xid, 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


def get_image(self, x, y, width, height, logger=log.debug):
handle = self.get_contents_handle()
if handle is None:
logger("get_image(..) pixmap is None for window %#x", self._window.xid)
return None

#try XShm:
def _set_pixmap(self):
# 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:
shm = self.get_shm_handle()
#logger("get_image(..) XShm handle: %s, handle=%s, pixmap=%s", shm, handle, handle.get_pixmap())
if shm is not None:
with xsync:
shm_image = shm.get_image(handle.get_pixmap(), x, y, width, height)
#logger("get_image(..) XShm image: %s", shm_image)
if shm_image:
return shm_image
root = self.client_window.get_screen().get_root_window()
world = get_world_window().window
win = get_parent(self.client_window)
while win not in (None, root, world) 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(win.xid, StructureNotifyMask)
add_event_receiver(win, self, max_receivers=-1)
listening.append(win)
win = get_parent(win)
handle = XImage.get_xcomposite_pixmap(self.client_window.xid)
except Exception as e:
if type(e)==XError and e.msg=="BadMatch":
logger("get_image(%s, %s, %s, %s) get_image BadMatch ignored (window already gone?)", x, y, width, height)
else:
log.warn("get_image(%s, %s, %s, %s) get_image %s", x, y, width, height, e, exc_info=True)

try:
w = min(handle.get_width(), width)
h = min(handle.get_height(), height)
if w!=width or h!=height:
logger("get_image(%s, %s, %s, %s) clamped to pixmap dimensions: %sx%s", x, y, width, height, w, h)
with xsync:
return handle.get_image(x, y, w, h)
except Exception as e:
if type(e)==XError and e.msg=="BadMatch":
logger("get_image(%s, %s, %s, %s) get_image BadMatch ignored (window already gone?)", x, y, width, height)
else:
log.warn("get_image(%s, %s, %s, %s) get_image %s", x, y, width, height, e, exc_info=True)
return None


def do_xpra_unmap_event(self, event):
self.invalidate_pixmap()

def do_xpra_configure_event(self, event):
self._border_width = event.border_width
self.invalidate_pixmap()
try:
self._cleanup_listening(listening)
except:
pass
raise
if handle is None:
#avoid race during signal exit, which will clear self.client_window:
win = self.client_window
xid = 0
if win:
xid = win.xid
log("failed to name a window pixmap for %#x: %s", xid, 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

def do_xpra_reparent_event(self, event):
self.invalidate_pixmap()

def do_xpra_damage_event(self, event):
event.x += self._border_width
Expand Down
Loading

0 comments on commit 1b84ed9

Please sign in to comment.