Skip to content

Commit

Permalink
support for forwarding notifications to the client
Browse files Browse the repository at this point in the history
git-svn-id: https://xpra.org/svn/Xpra/trunk@202 3bb7dfac-3a0b-4e04-842a-767bc560f471
  • Loading branch information
totaam committed Sep 30, 2011
1 parent 18b45e7 commit 847c21a
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 3 deletions.
24 changes: 23 additions & 1 deletion src/xpra/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from xpra.platform.gui import ClipboardProtocolHelper, ClientExtras, grok_modifier_map
from xpra.scripts.main import ENCODINGS

from xpra.platform.gui import system_bell
from xpra.platform.gui import system_bell, notifications_wrapper

import xpra
default_capabilities = {"__prerelease_version": xpra.__version__}
Expand Down Expand Up @@ -378,6 +378,13 @@ def __init__(self, conn, opts):
if self.max_bandwidth>0.0 and self.jpegquality==0:
""" jpegquality was not set, use a better start value """
self.jpegquality = 50

self.notifications = None
if notifications_wrapper:
try:
self.notifications = notifications_wrapper()
except ImportError, e:
log.error("failed to load notification wrapper (turning feature off) : %s", e)

self._protocol = Protocol(conn, self.process_packet)
ClientSource(self._protocol)
Expand Down Expand Up @@ -543,6 +550,7 @@ def send_hello(self, hash=None):
capabilities_request["xmodmap_data"] = self.xmodmap_data
capabilities_request["cursors"] = True
capabilities_request["bell"] = system_bell is not None
capabilities_request["notifications"] = self.notifications is not None
(_, _, current_mask) = gtk.gdk.get_default_root_window().get_pointer()
modifiers = self.mask_to_names(current_mask)
log.debug("sending modifiers=%s" % str(modifiers))
Expand Down Expand Up @@ -650,6 +658,18 @@ def _process_bell(self, packet):
window = self._id_to_window[id]
system_bell(window, device, percent, pitch, duration, bell_class, bell_id, bell_name)

def _process_notify_show(self, packet):
(_, id, app_name, replaces_id, app_icon, summary, body, expire_timeout) = packet
log("_process_notify_show(%s,%s,%s,%s,%s,%s,%s) notifications wrapper=%s", id, app_name, replaces_id, app_icon, summary, body, expire_timeout, self.notifications)
if self.notifications:
self.notifications.notify(id, app_name, replaces_id, app_icon, summary, body, expire_timeout)

def _process_notify_close(self, packet):
(_, id) = packet
log("_process_notify_close(%s)", id)
if self.notifications:
self.notifications.close_callback(id)

def _process_window_metadata(self, packet):
(_, id, metadata) = packet
window = self._id_to_window[id]
Expand Down Expand Up @@ -686,6 +706,8 @@ def _process_gibberish(self, packet):
"draw": _process_draw,
"cursor": _process_cursor,
"bell": _process_bell,
"notify_show": _process_notify_show,
"notify_close": _process_notify_close,
"window-metadata": _process_window_metadata,
"configure-override-redirect": _process_configure_override_redirect,
"lost-window": _process_lost_window,
Expand Down
1 change: 1 addition & 0 deletions src/xpra/darwin/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def get_keymap_spec():
return None,None,None

system_bell = None
notifications_wrapper = None

class ClipboardProtocolHelper(object):
def __init__(self, send_packet_cb):
Expand Down
77 changes: 77 additions & 0 deletions src/xpra/dbus_notifications_forwarder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# This file is part of Parti.
# Copyright (C) 2011 Antoine Martin <antoine@devloop.org.uk>
# Parti is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

import gtk
import dbus.service
from dbus.mainloop.glib import DBusGMainLoop

from wimpiggy.log import Logger
log = Logger()

BUS_NAME="org.freedesktop.Notifications"
BUS_PATH="/org/freedesktop/Notifications"

"""
We register this class as handling notifications on the session dbus,
optionally replacing an existing instance if one exists.
The generalized callback signatures are:
notify_callback(id, app_name, replaces_id, app_icon, summary, body, expire_timeout)
close_callback(id)
"""
class DBUSNotificationsForwarder(dbus.service.Object):

CAPABILITIES = ["body", "icon-static"]

def __init__(self, bus, notify_callback=None, close_callback=None):
self.notify_callback = notify_callback
self.close_callback = close_callback
self.counter = 1
bus_name = dbus.service.BusName(BUS_NAME, bus=bus)
dbus.service.Object.__init__(self, bus_name, BUS_PATH)

@dbus.service.method(BUS_NAME, in_signature='susssasa{sv}i', out_signature='u')
def Notify(self, app_name, replaces_id, app_icon, summary, body, actions, hints, expire_timeout):
log("Notify(%s,%s,%s,%s,%s,%s,%s,%s)" % (app_name, replaces_id, app_icon, summary, body, actions, hints, expire_timeout))
if replaces_id<=0:
self.counter += 1
id = self.counter
else:
id = replaces_id
if self.notify_callback:
self.notify_callback(id, app_name, replaces_id, app_icon, summary, body, expire_timeout)
return self.counter

@dbus.service.method(BUS_NAME, out_signature='ssss')
def GetServerInformation(self):
log("GetServerInformation()")
#name, vendor, version, spec-version
return ["xpra-notification-proxy", "xpra", "0.1", "0.9"]

@dbus.service.method(BUS_NAME, out_signature='as')
def GetCapabilities(self):
log("GetCapabilities()")
return DBUSNotificationsForwarder.CAPABILITIES

@dbus.service.method(BUS_NAME, in_signature='u')
def CloseNotification(self, id):
log("CloseNotification(%s)", id)
if self.close_callback:
self.close_callback(id)

def register(notify_callback=None, close_callback=None, replace=True):
DBusGMainLoop(set_as_default=True)
bus = dbus.SessionBus()
if replace:
request = bus.request_name(BUS_NAME, dbus.bus.NAME_FLAG_REPLACE_EXISTING)
log("request_name(%s)=%s" % (BUS_NAME, request))
return DBUSNotificationsForwarder(bus, notify_callback, close_callback)

def main():
register()
gtk.main()

if __name__ == "__main__":
main()
24 changes: 22 additions & 2 deletions src/xpra/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
# later version. See the file COPYING for details.

# Todo:
# cursors
# xsync resize stuff
# shape?
# any other interesting metadata? _NET_WM_TYPE, WM_TRANSIENT_FOR, etc.?
Expand Down Expand Up @@ -336,6 +335,7 @@ def __init__(self, clobber, sockets, password_file, pulseaudio, clipboard, randr
self.xkbmap_query = None
self.xmodmap_data = None

self.send_notifications = False
self.last_cursor_serial = None
self.cursor_image = None
#store list of currently pressed keys
Expand Down Expand Up @@ -376,6 +376,14 @@ def __init__(self, clobber, sockets, password_file, pulseaudio, clipboard, randr
log.info("randr enabled: %s" % self.randr)

self.pulseaudio = pulseaudio

try:
from xpra.dbus_notifications_forwarder import register
self.notifications_forwarder = register(self.notify_callback, self.notify_close_callback, replace=True)
log.info("using notification forwarder: %s", self.notifications_forwarder)
except Exception, e:
log.error("failed to load dbus notifications forwarder: %s", e)
self.notifications_forwarder = None

### All right, we're ready to accept customers:
for sock in sockets:
Expand Down Expand Up @@ -542,6 +550,16 @@ def _bell_signaled(self, wm, event):
if self.send_bell:
self._send(["bell", id, event.device, event.percent, event.pitch, event.duration, event.bell_class, event.bell_id, event.bell_name])

def notify_callback(self, id, app_name, replaces_id, app_icon, summary, body, expire_timeout):
log("notify_callback(%s,%s,%s,%s,%s,%s,%s) send_notifications=%s", id, app_name, replaces_id, app_icon, summary, body, expire_timeout, self.send_notifications)
if self.send_notifications:
self._send(["notify_show", int(id), str(app_name), int(replaces_id), str(app_icon), str(summary), str(body), long(expire_timeout)])

def notify_close_callback(self, id):
log("notify_close_callback(%s)", id)
if self.send_notifications:
self._send(["notify_close", int(id)])

def do_wimpiggy_child_map_event(self, event):
raw_window = event.window
if event.override_redirect:
Expand Down Expand Up @@ -796,7 +814,7 @@ def _calculate_capabilities(self, client_capabilities):
capabilities = {}
for cap in ("deflate", "__prerelease_version", "challenge_response",
"keymap", "xkbmap_query", "xmodmap_data", "modifiers",
"cursors", "bell",
"cursors", "bell", "notifications",
"png_window_icons", "encodings", "encoding", "jpeg"):
if cap in client_capabilities:
capabilities[cap] = client_capabilities[cap]
Expand Down Expand Up @@ -936,6 +954,8 @@ def login_failed(*args):
self.set_keymap()
self.send_cursors = capabilities.get("cursors", False)
self.send_bell = capabilities.get("bell", False)
self.send_notifications = capabilities.get("notifications", False)
log.info("send_cursors=%s, send_bell=%s, send_notifications=%s", self.send_cursors, self.send_bell, self.send_notifications)
self._wm.enableCursors(self.send_cursors)
self.png_window_icons = capabilities.get("png_window_icons", False)
# now we can set the modifiers to match the client
Expand Down
1 change: 1 addition & 0 deletions src/xpra/win32/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def get_keymap_spec():
return None,None,None

system_bell = None
notifications_wrapper = None

class ClipboardProtocolHelper(object):
def __init__(self, send_packet_cb):
Expand Down
13 changes: 13 additions & 0 deletions src/xpra/xposix/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,16 @@ def x11_system_bell(window, device, percent, pitch, duration, bell_class, bell_i
system_bell = x11_system_bell
except ImportError, e:
log.error("cannot import device_bell (turning feature off) : %s", e)

class notifications_wrapper:
def __init__(self):
import pynotify
pynotify.init("Xpra")

def notify(self, id, app_name, replaces_id, app_icon, summary, body, expire_timeout):
import pynotify
n = pynotify.Notification("Title", "message")
n.show()

def close_callback(self, id):
pass

0 comments on commit 847c21a

Please sign in to comment.