Skip to content

Commit

Permalink
#565 separate the different "alpha" concepts: defaults, settings, ser…
Browse files Browse the repository at this point in the history
…ver-side window setting, window availability, backing state

git-svn-id: https://xpra.org/svn/Xpra/trunk@6445 3bb7dfac-3a0b-4e04-842a-767bc560f471
  • Loading branch information
totaam committed May 13, 2014
1 parent 1c71066 commit 0c85e6d
Show file tree
Hide file tree
Showing 12 changed files with 107 additions and 55 deletions.
9 changes: 9 additions & 0 deletions src/xpra/client/client_tray.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def __init__(self, client, wid, w, h, tray_widget, mmap_enabled, mmap_area):
ClientWidgetBase.__init__(self, client, wid, True)
self.tray_widget = tray_widget
self._geometry = None
self._window_alpha = True
self.group_leader = None

self.mmap_enabled = mmap_enabled
Expand Down Expand Up @@ -156,6 +157,14 @@ def __init__(self, wid, w, h, has_alpha, data=None):
self.data = data
GTKWindowBacking.__init__(self, wid, True)

def get_encoding_properties(self):
#override so we skip all csc caps:
return {
"encodings.rgb_formats" : self.RGB_MODES,
"encoding.transparency" : True
}


def _do_paint_rgb24(self, img_data, x, y, width, height, rowstride, options, callbacks):
log("TrayBacking._do_paint_rgb24%s", ("%s bytes" % len(img_data), x, y, width, height, rowstride, options, callbacks))
self.data = ["rgb24", width, height, rowstride, img_data[:]]
Expand Down
8 changes: 6 additions & 2 deletions src/xpra/client/client_widget_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ def __init__(self, client, wid, has_alpha):
self.source_remove = client.source_remove
self.idle_add = client.idle_add
self.timeout_add = client.timeout_add
#tells us if the server-side window has an alpha channel
#(whether we are capable of rendering it is down the backing)
self._has_alpha = has_alpha
#tells us if this window instance can paint with alpha
self._window_alpha = False
self._client = client

def make_new_backing(self, backing_class, w, h):
Expand All @@ -34,8 +38,8 @@ def make_new_backing(self, backing_class, w, h):
if USE_FAKE_BACKING:
from xpra.client.fake_window_backing import FakeBacking
bc = FakeBacking
log("make_new_backing(%s, %s, %s) effective backing class=%s, alpha=%s", backing_class, w, h, bc, self._has_alpha)
backing = bc(self._id, w, h, self._has_alpha)
log("make_new_backing(%s, %s, %s) effective backing class=%s, server alpha=%s, window alpha=%s", backing_class, w, h, bc, self._has_alpha, self._window_alpha)
backing = bc(self._id, w, h, self._window_alpha)
if self._client.mmap_enabled:
backing.enable_mmap(self._client.mmap)
backing.init(w, h)
Expand Down
42 changes: 11 additions & 31 deletions src/xpra/client/client_window_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import sys

from xpra.client.client_widget_base import ClientWidgetBase
from xpra.codecs.video_helper import getVideoHelper
from xpra.util import typedict
from xpra.log import Logger
log = Logger("window")
Expand All @@ -30,8 +29,10 @@ class ClientWindowBase(ClientWidgetBase):

def __init__(self, client, group_leader, wid, x, y, w, h, metadata, override_redirect, client_properties, border):
log("%s%s", type(self), (client, group_leader, wid, x, y, w, h, metadata, override_redirect, client_properties))
has_alpha = self.get_backing_class().HAS_ALPHA
ClientWidgetBase.__init__(self, client, wid, has_alpha)
ClientWidgetBase.__init__(self, client, wid, metadata.boolget("has_alpha"))
#remove alpha from metadata, so we can verify the flag never changes in set_metadata
if "has_alpha" in metadata:
del metadata["has_alpha"]
self._override_redirect = override_redirect
self.group_leader = group_leader
self._pos = (x, y)
Expand Down Expand Up @@ -71,30 +72,10 @@ def destroy(self):


def setup_window(self):
rgb_modes = self.get_backing_class().RGB_MODES
self._client_properties["encodings.rgb_formats"] = rgb_modes
self._client_properties["encoding.full_csc_modes"] = self._get_full_csc_modes(rgb_modes)
self._client_properties["encoding.csc_modes"] = self._get_csc_modes(rgb_modes)
self.new_backing(*self._size)

def _get_full_csc_modes(self, rgb_modes):
#calculate the server CSC modes the server is allowed to use
#based on the client CSC modes we can convert to in the backing class we use
#and trim the transparency if we cannot handle it
target_rgb_modes = list(rgb_modes)
if not self._has_alpha:
target_rgb_modes = [x for x in target_rgb_modes if x.find("A")<0]
full_csc_modes = getVideoHelper().get_server_full_csc_modes_for_rgb(*target_rgb_modes)
log("full csc modes (%s)=%s", target_rgb_modes, full_csc_modes)
return full_csc_modes

def _get_csc_modes(self, rgb_modes):
#as above, but for older servers: less detailed than "full" csc modes info
csc_modes = []
for modes in self._get_full_csc_modes(rgb_modes).values():
csc_modes += modes
csc_modes = list(set(csc_modes))
return csc_modes
#tell the server about the encoding capabilities of this backing instance:
#"rgb_formats", "full_csc_modes", "csc_modes":
self._client_properties.update(self._backing.get_encoding_properties())


def send(self, *args):
Expand Down Expand Up @@ -229,8 +210,10 @@ def metadata_replace(match):
self.set_opacity(opacity)

if "has-alpha" in metadata:
self._has_alpha = metadata.boolget("has-alpha")
self.set_alpha()
new_alpha = metadata.boolget("has-alpha")
if new_alpha!=self._has_alpha:
log.warn("window %s changed its alpha flag from %s to %s (unsupported)", self._id, self._has_alpha, new_alpha)
self._has_alpha = new_alpha

if "maximized" in metadata:
maximized = metadata.boolget("maximized")
Expand Down Expand Up @@ -259,9 +242,6 @@ def set_window_type(self, window_types):
def set_fullscreen(self, fullscreen):
pass

def set_alpha(self):
pass

def set_xid(self, xid):
pass

Expand Down
6 changes: 5 additions & 1 deletion src/xpra/client/fake_window_backing.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

class FakeBacking(object):

RGB_MODES = ["RGBA", "RGB"]
HAS_ALPHA = True

def __init__(self, wid, *args):
Expand All @@ -36,3 +35,8 @@ def draw_region(self, x, y, width, height, coding, img_data, rowstride, options,

def cairo_draw(self, context, x, y):
pass

def get_encoding_properties(self):
return {
"encodings.rgb_formats" : ["RGBA", "RGB"],
}
4 changes: 2 additions & 2 deletions src/xpra/client/gl/gl_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,11 @@ def visual_to_str(visual):
d[k] = getattr(visual, k)
return str(d)

def get_DISPLAY_MODE():
def get_DISPLAY_MODE(want_alpha=GL_ALPHA_SUPPORTED):
import gtk.gdkgl
#gtk.gdkgl.MODE_DEPTH
mode = 0
if GL_ALPHA_SUPPORTED:
if want_alpha:
mode = mode | gtk.gdkgl.MODE_RGBA | gtk.gdkgl.MODE_ALPHA
else:
mode = mode | gtk.gdkgl.MODE_RGB
Expand Down
22 changes: 13 additions & 9 deletions src/xpra/client/gl/gl_window_backing.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,32 +135,36 @@ def py_gl_debug_callback(source, error_type, error_id, severity, length, message
"""
class GLPixmapBacking(GTK2WindowBacking):

RGB_MODES = ["YUV420P", "YUV422P", "YUV444P", "GBRP"]
RGB_MODES = ["YUV420P", "YUV422P", "YUV444P", "GBRP", "BGRA", "BGRX", "RGBA", "RGBX", "RGB", "BGR"]
HAS_ALPHA = GL_ALPHA_SUPPORTED

def __init__(self, wid, w, h, has_alpha):
display_mode = get_DISPLAY_MODE()
def __init__(self, wid, w, h, window_alpha):
alpha = GL_ALPHA_SUPPORTED and window_alpha
display_mode = get_DISPLAY_MODE(want_alpha=alpha)
try:
self.glconfig = gtk.gdkgl.Config(mode=display_mode)
except gtk.gdkgl.NoMatches:
#toggle double buffering and try again:
display_mode &= ~gtk.gdkgl.MODE_DOUBLE
log.warn("failed to initialize gl context: trying again %s double buffering", (bool(display_mode & gtk.gdkgl.MODE_DOUBLE) and "with") or "without")
self.glconfig = gtk.gdkgl.Config(mode=display_mode)
GTK2WindowBacking.__init__(self, wid, has_alpha and self.glconfig.has_alpha())
GTK2WindowBacking.__init__(self, wid, alpha and self.glconfig.has_alpha())
self._backing = gtk.gtkgl.DrawingArea(self.glconfig)
#restoring missed masks:
self._backing.set_events(self._backing.get_events() | gdk.POINTER_MOTION_MASK | gdk.POINTER_MOTION_HINT_MASK)
if self._has_alpha:
if self._alpha_enabled:
assert GL_ALPHA_SUPPORTED, "BUG: cannot enable alpha if GL backing does not support it!"
screen = self._backing.get_screen()
rgba = screen.get_rgba_colormap()
if rgba:
log("%s.__init__() using rgba colormap %s", rgba)
self._backing.set_colormap(rgba)
else:
log.warn("failed to enable transparency on screen %s", screen)
self._has_alpha = False
self._alpha_enabled = False
#this is how many bpp we keep in the texture
#(pixels are always stored in 32bpp - but this makes it clearer when we do/don't support alpha)
if self._has_alpha:
if self._alpha_enabled:
self.texture_pixel_format = GL_RGBA
else:
self.texture_pixel_format = GL_RGB
Expand Down Expand Up @@ -371,7 +375,7 @@ def present_fbo(self, drawable):
# Change state to target screen instead of our FBO
glBindFramebuffer(GL_FRAMEBUFFER, 0)

if self._has_alpha:
if self._alpha_enabled:
# transparent background:
glClearColor(0.0, 0.0, 0.0, 0.0)
else:
Expand All @@ -383,7 +387,7 @@ def present_fbo(self, drawable):
w, h = self.size

glBindTexture(GL_TEXTURE_RECTANGLE_ARB, self.textures[TEX_FBO])
if self._has_alpha:
if self._alpha_enabled:
# support alpha channel if present:
glEnablei(GL_BLEND, self.textures[TEX_FBO])
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
Expand Down
5 changes: 3 additions & 2 deletions src/xpra/client/gtk2/pixmap_backing.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,16 @@ def __init__(self, wid, w, h, has_alpha):
def init(self, w, h):
old_backing = self._backing
assert w<32768 and h<32768, "dimensions too big: %sx%s" % (w, h)
if self._has_alpha:
if self._alpha_enabled:
self._backing = gdk.Pixmap(None, w, h, 32)
screen = self._backing.get_screen()
rgba = screen.get_rgba_colormap()
if rgba is not None:
self._backing.set_colormap(rgba)
else:
#cannot use transparency
self._has_alpha = False
log.warn("cannot use transparency: no RGBA colormap!")
self._alpha_enabled = False
self._backing = gdk.Pixmap(gdk.get_default_root_window(), w, h)
else:
self._backing = gdk.Pixmap(gdk.get_default_root_window(), w, h)
Expand Down
8 changes: 7 additions & 1 deletion src/xpra/client/gtk_base/cairo_backing.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,13 @@
"""
class CairoBacking(GTKWindowBacking):

RGB_MODES = ["ARGB", "XRGB", "RGBA", "RGBX", "RGB"]
if not is_gtk3():
#with gtk2 we can convert these directly to a cairo image surface:
RGB_MODES = ["ARGB", "XRGB"]
else:
RGB_MODES = []
RGB_MODES += ["RGBA", "RGBX", "RGB"]


def __init__(self, wid, w, h, has_alpha):
GTKWindowBacking.__init__(self, wid, has_alpha)
Expand Down
14 changes: 14 additions & 0 deletions src/xpra/client/gtk_base/gtk_client_window_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,25 @@ def setup_window(self):
ClientWindowBase.setup_window(self)
self.set_app_paintable(True)
self.add_events(self.WINDOW_EVENT_MASK)

#try to enable alpha on this window if needed,
#and if the backing class can support it:
if self._has_alpha and self.get_backing_class().HAS_ALPHA:
screen = self.get_screen()
rgba = screen.get_rgba_colormap()
if rgba is None:
log.error("cannot handle window transparency on screen %s", screen)
else:
log("set_alpha() using rgba colormap for %s, realized=%s", self._id, self.is_realized())
self.set_colormap(rgba)
self._window_alpha = True

if self._override_redirect:
transient_for = self.get_transient_for()
type_hint = self.get_type_hint()
if transient_for is not None and transient_for.window is not None and type_hint in self.OR_TYPE_HINTS:
transient_for._override_redirect_windows.append(self)

if not self._override_redirect:
self.connect("notify::has-toplevel-focus", self._focus_change)
def focus_in(*args):
Expand Down
8 changes: 4 additions & 4 deletions src/xpra/client/gtk_base/gtk_window_backing_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
# - on MS Windows: not supported
# - on OSX: only with gtk3
DEFAULT_HAS_ALPHA = not sys.platform.startswith("win") and (not sys.platform.startswith("darwin") or is_gtk3())
HAS_ALPHA = (unpremultiply_argb is not None) and os.environ.get("XPRA_ALPHA", DEFAULT_HAS_ALPHA) in (True, "1")
GTK_ALPHA_SUPPORTED = (unpremultiply_argb is not None) and os.environ.get("XPRA_ALPHA", DEFAULT_HAS_ALPHA) in (True, "1")


"""
Expand All @@ -30,10 +30,10 @@
"""
class GTKWindowBacking(WindowBackingBase):

HAS_ALPHA = HAS_ALPHA
HAS_ALPHA = GTK_ALPHA_SUPPORTED

def __init__(self, wid, has_alpha):
WindowBackingBase.__init__(self, wid, has_alpha and HAS_ALPHA, gobject.idle_add)
def __init__(self, wid, window_alpha):
WindowBackingBase.__init__(self, wid, window_alpha and self.HAS_ALPHA, gobject.idle_add)


def cairo_draw(self, context):
Expand Down
3 changes: 2 additions & 1 deletion src/xpra/client/ui_client_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -938,14 +938,15 @@ def make_hello(self):
if self.min_speed>=0:
capabilities["encoding.min-speed"] = self.min_speed

#note: this is mostly for old servers, with newer ones we send the properties
#again (and more accurately) once the window is instantiated.
#figure out the CSC modes supported:
#these are the RGB modes we want (the ones we can paint with):
rgb_formats = ["RGB", "RGBX"]
if not sys.platform.startswith("win") and not sys.platform.startswith("darwin"):
#only win32 and osx cannot handle transparency
rgb_formats.append("RGBA")
capabilities["encodings.rgb_formats"] = rgb_formats

#figure out which CSC modes (usually YUV) can give us those RGB modes:
full_csc_modes = getVideoHelper().get_server_full_csc_modes_for_rgb(*rgb_formats)
log("supported full csc_modes=%s", full_csc_modes)
Expand Down
33 changes: 31 additions & 2 deletions src/xpra/client/window_backing_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,12 @@ def fire_paint_callbacks(callbacks, success):
see CairoBacking and GTKWindowBacking for actual implementations
"""
class WindowBackingBase(object):
def __init__(self, wid, has_alpha, idle_add):
def __init__(self, wid, window_alpha, idle_add):
load_csc_options()
load_video_decoders()
self.wid = wid
self.idle_add = idle_add
self._has_alpha = has_alpha
self._alpha_enabled = window_alpha
self._backing = None
self._last_pixmap_data = None
self._video_decoder = None
Expand Down Expand Up @@ -128,6 +128,35 @@ def do_clean_csc_decoder(self):
self._csc_decoder = None


def get_encoding_properties(self):
rgb_modes = self.RGB_MODES
return {
"encodings.rgb_formats" : rgb_modes,
"encoding.transparency" : self._alpha_enabled,
"encoding.full_csc_modes" : self._get_full_csc_modes(rgb_modes),
"encoding.csc_modes" : self._get_csc_modes(rgb_modes)
}

def _get_full_csc_modes(self, rgb_modes):
#calculate the server CSC modes the server is allowed to use
#based on the client CSC modes we can convert to in the backing class we use
#and trim the transparency if we cannot handle it
target_rgb_modes = list(rgb_modes)
if not self._alpha_enabled:
target_rgb_modes = [x for x in target_rgb_modes if x.find("A")<0]
full_csc_modes = getVideoHelper().get_server_full_csc_modes_for_rgb(*target_rgb_modes)
log("full csc modes (%s)=%s", target_rgb_modes, full_csc_modes)
return full_csc_modes

def _get_csc_modes(self, rgb_modes):
#as above, but for older servers: less detailed than "full" csc modes info
csc_modes = []
for modes in self._get_full_csc_modes(rgb_modes).values():
csc_modes += modes
csc_modes = list(set(csc_modes))
return csc_modes


def img_data_tobytes(self, img_data):
if _memoryview and isinstance(img_data, _memoryview):
return img_data.tobytes()
Expand Down

0 comments on commit 0c85e6d

Please sign in to comment.