Skip to content

Commit

Permalink
#489:
Browse files Browse the repository at this point in the history
* add generic window filter code
* add x11 specific filters via subclassing
* add dbus methods for setting and clearing the filters

git-svn-id: https://xpra.org/svn/Xpra/trunk@10751 3bb7dfac-3a0b-4e04-842a-767bc560f471
  • Loading branch information
totaam committed Oct 6, 2015
1 parent 4ebbd6d commit 8723ca2
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 5 deletions.
8 changes: 8 additions & 0 deletions src/xpra/server/dbus/dbus_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,14 @@ def ShowDesktop(self, show):
def RaiseWindow(self, wid):
self.source.raise_window(ni(wid))

@dbus.service.method(INTERFACE, in_signature='')
def ResetWindowFilters(self):
self.source.reset_window_filters()

@dbus.service.method(INTERFACE, in_signature='sssv')
def AddWindowFilter(self, object_name, property_name, operator, value):
self.source.add_window_filter(ns(object_name), ns(property_name), ns(operator), n(value))


@dbus.service.method(INTERFACE, in_signature='')
def SetDefaultKeymap(self):
Expand Down
9 changes: 7 additions & 2 deletions src/xpra/server/server_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -859,8 +859,8 @@ def drop_client(reason="unknown", *args):
self.disconnect_client(proto, reason, *args)
def get_window_id(wid):
return self._window_to_id.get(wid)
from xpra.server.source import ServerSource
ss = ServerSource(proto, drop_client,
ServerSourceClass = self.get_server_source_class()
ss = ServerSourceClass(proto, drop_client,
self.idle_add, self.timeout_add, self.source_remove,
self.idle_timeout, self.idle_timeout_cb, self.idle_grace_timeout_cb,
self._socket_dir, self.main_socket_path, self.dbus_control,
Expand All @@ -886,6 +886,11 @@ def get_window_id(wid):
send_ui = ui_client and not is_request
self.idle_add(self.parse_hello_ui, ss, c, auth_caps, send_ui, share_count)

def get_server_source_class(self):
from xpra.server.source import ServerSource
return ServerSource


def parse_hello_ui(self, ss, c, auth_caps, send_ui, share_count):
#adds try:except around parse hello ui code:
try:
Expand Down
58 changes: 57 additions & 1 deletion src/xpra/server/source.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,40 @@ def raw():
raise Exception("unhandled property name: %s" % propname)


class WindowPropertyFilter(object):
def __init__(self, property_name, value):
self.property_name = property_name
self.value = value

def get_window_value(self, window):
return window.get_property(self.property_name)

def show(self, window):
try:
v = self.get_window_value(window)
log("%s.show(%s) %s(..)=%s", type(self).__name__, window, self.get_window_value, v)
except Exception:
log("%s.show(%s) %s(..) error:", type(self).__name__, window, self.get_window_value, exc_info=True)
v = None
e = self.evaluate(v)
return e

def evaluate(self, window_value):
raise NotImplementedError()


class WindowPropertyIn(WindowPropertyFilter):

def evaluate(self, window_value):
return window_value in self.value


class WindowPropertyNotIn(WindowPropertyIn):

def evaluate(self, window_value):
return not(WindowPropertyIn.evaluate(window_value))


class ServerSource(object):
"""
A ServerSource represents a client connection.
Expand Down Expand Up @@ -296,7 +330,7 @@ def __init__(self, protocol, disconnect_cb, idle_add, timeout_add, source_remove
dbuslog.error(" %s", e)

def __str__(self):
return "ServerSource(%s)" % self.protocol
return "%s(%s)" % (type(self).__name__, self.protocol)

def init_vars(self):
self.encoding = None #the default encoding for all windows
Expand All @@ -309,6 +343,7 @@ def init_vars(self):

self.window_sources = {} #WindowSource for each Window ID
self.suspended = False
self.window_filters = []

self.uuid = ""
self.machine_id = ""
Expand Down Expand Up @@ -1693,7 +1728,28 @@ def window_metadata(self, wid, window, prop):
if len(metadata)>0:
self.send("window-metadata", wid, metadata)

def reset_window_filters(self):
self.window_filters = []

def get_window_filter(self, object_name, property_name, operator, value):
if object_name!="window":
raise ValueError("invalid object name")
if operator=="=":
return WindowPropertyIn(property_name, [value])
elif operator=="!=":
return WindowPropertyNotIn(property_name, [value])
raise ValueError("unknown filter operator: %s" % operator)

def add_window_filter(self, object_name, property_name, operator, value):
window_filter = self.get_window_filter(object_name, property_name, operator, value)
assert window_filter
self.window_filters.append(window_filter.show)

def can_send_window(self, window):
for x in self.window_filters:
v = x(window)
if v is True or v is False:
return v
if self.send_windows and self.system_tray:
#common case shortcut
return True
Expand Down
1 change: 1 addition & 0 deletions src/xpra/x11/bindings/constants.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ PointerWindow
InputFocus
PointerRoot
CurrentTime
AnyPropertyType

# Map states
IsUnmapped
Expand Down
40 changes: 38 additions & 2 deletions src/xpra/x11/bindings/window_bindings.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -864,7 +864,7 @@ cdef class X11WindowBindings(X11CoreBindings):
self.addXSelectInput(xwindow, FocusChangeMask)


def XGetWindowProperty(self, Window xwindow, property, req_type, etype=None):
def XGetWindowProperty(self, Window xwindow, property, req_type=0, etype=None):
# NB: Accepts req_type == 0 for AnyPropertyType
# "64k is enough for anybody"
# (Except, I've found window icons that are strictly larger)
Expand All @@ -876,7 +876,9 @@ cdef class X11WindowBindings(X11CoreBindings):
cdef unsigned long nitems = 0, bytes_after = 0
cdef unsigned char * prop = <unsigned char*> 0
cdef Status status
xreq_type = self.get_xatom(req_type)
cdef Atom xreq_type = AnyPropertyType
if req_type:
xreq_type = self.get_xatom(req_type)
# This is the most bloody awful API I have ever seen. You will probably
# not be able to understand this code fully without reading
# XGetWindowProperty's man page at least 3 times, slowly.
Expand Down Expand Up @@ -922,6 +924,40 @@ cdef class X11WindowBindings(X11CoreBindings):
else:
return data


def GetWindowPropertyType(self, Window xwindow, property):
#as above, but for any property type
#and returns the type found
cdef int buffer_size = 64 * 1024
cdef Atom xactual_type = <Atom> 0
cdef int actual_format = 0
cdef unsigned long nitems = 0, bytes_after = 0
cdef unsigned char * prop = <unsigned char*> 0
cdef Status status
cdef Atom xreq_type = AnyPropertyType
status = XGetWindowProperty(self.display,
xwindow,
self.get_xatom(property),
0,
# This argument has to be divided by 4. Thus
# speaks the spec.
buffer_size / 4,
False,
xreq_type, &xactual_type,
&actual_format, &nitems, &bytes_after, &prop)
if status != Success:
raise None
if xactual_type == XNone:
raise None
# This should only occur for bad property types:
assert not (bytes_after and not nitems)
if bytes_after:
raise None
assert actual_format in (8, 16, 32)
XFree(prop)
return self.XGetAtomName(xactual_type)


def XDeleteProperty(self, Window xwindow, property):
XDeleteProperty(self.display, xwindow, self.get_xatom(property))

Expand Down
11 changes: 11 additions & 0 deletions src/xpra/x11/gtk_x11/prop.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,17 @@ def get_xsettings(disp, v):
return get_settings(_get_display_name(disp), v)


def get_python_type(scalar_type):
#ie: get_python_type("STRING") = "latin1"
return {"UTF8_STRING" : "utf8",
"STRING" : "latin1",
"ATOM" : "atom",
"CARDINAL" : "u32",
"INTEGER" : "integer",
"VISUALID" : "visual",
"WINDOW" : "window",
}.get(scalar_type)

_prop_types = {
# Python type, X type Atom, formatbits, serializer, deserializer, list
# terminator
Expand Down
5 changes: 5 additions & 0 deletions src/xpra/x11/x11_server_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ def init_packet_handlers(self):
self._authenticated_ui_packet_handlers["force-ungrab"] = self._process_force_ungrab


def get_server_source_class(self):
from xpra.x11.x11_source import X11ServerSource
return X11ServerSource


def get_child_env(self):
#adds fakeXinerama:
env = GTKServerBase.get_child_env(self)
Expand Down
44 changes: 44 additions & 0 deletions src/xpra/x11/x11_source.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# coding=utf8
# This file is part of Xpra.
# Copyright (C) 2015 Antoine Martin <antoine@devloop.org.uk>
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

from xpra.server.source import ServerSource
from xpra.gtk_common.gobject_compat import get_xid
from xpra.gtk_common.error import xsync
from xpra.x11.gtk_x11.prop import prop_get, get_python_type
from xpra.x11.bindings.window_bindings import X11WindowBindings #@UnresolvedImport
window_bindings = X11WindowBindings()

from xpra.log import Logger
log = Logger("x11", "server")


def get_x11_window_value(filter_object, window):
xid = get_xid(window)
#log("get_x11_window_value(%s, %s) xid=%#x", filter_object, window, xid)
with xsync:
x11type = window_bindings.GetWindowPropertyType(xid, filter_object.property_name)
ptype = get_python_type(x11type)
#log("%s: %s (%s)", filter_object.property_name, x11type, ptype)
assert ptype, "type '%s' is not handled!" % x11type
v = prop_get(window, filter_object.property_name, ptype)
log("%s=%s", filter_object.property_name, v)
return v


class X11ServerSource(ServerSource):
""" Adds the ability to filter windows using X11 properties """

def get_window_filter(self, object_name, property_name, operator, value):
if object_name.lower() not in ("x11window", "window"):
raise ValueError("invalid object name")
wf = ServerSource.get_window_filter(self, "window", property_name, operator, value)
if object_name.lower()=="x11window":
#same filter but use X11 properties:
def get_window_value(window):
return get_x11_window_value(wf, window)
wf.get_window_value = get_window_value
log("get_window_filter%s=%s", (object_name, property_name, operator, value), wf)
return wf

0 comments on commit 8723ca2

Please sign in to comment.