Theme changes, fix for unicode conversion errors, misc
[rox-musicbox.git] / xsoap.py
blobdb70bee7f93491dda8c8cdbed584c73830718520
1 """
2 xsoap.py: an X rpc module
4 Copyright 2004 Kenneth Hayber <ken@hayber.us>
5 All rights reserved.
7 This module implements a method to register a window to receive rpc commands and
8 parameters from another application.
10 Using the X root window, the server registers a property that gives both the name
11 of the server/application and the X window ID of a window to receive messages.
13 The server may either allow the register_server() function to create a new hidden window
14 to receive these messages, or it may pass in an existing window for this purpose.
16 The client can determine if a server is already running on the current display by
17 checking the return value of get_server().
19 There is currently no mechanism for passing values back from the server to the client.
21 (Note: despite the name, this module does not (yet) implement any form of SOAP,
22 it is just a simple rpc mechanism)
24 This program is free software; you can redistribute it and/or modify
25 it under the terms of the GNU General Public License as published by
26 the Free Software Foundation; either version 2 of the License.
28 This program is distributed in the hope that it will be useful
29 but WITHOUT ANY WARRANTY; without even the implied warranty of
30 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 GNU General Public License for more details.
33 You should have received a copy of the GNU General Public License
34 along with this program; if not, write to the Free Software
35 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
37 (the idea of using X properties for rpc was taken from ROX-Filer, by Thomas Leonard)
38 """
40 import gtk
41 from types import *
43 _callback = None
45 def register_server(appl_id, appl_window=None):
46 """register the soap server name and window id"""
47 if not appl_id or not isinstance(appl_id, str):
48 raise TypeError, "appl_id must be a string"
50 #allow passing in an existing window...
51 if appl_window:
52 if not isinstance(window, gtk.Window):
53 raise TypeError, "appl_window is not a gtk.Window"
54 window = appl_window
55 #or create our own.
56 else:
57 window = gtk.Window()
58 #need to realize (or show) the window to get a gdkWindow
59 window.realize()
61 #set the appl_id and xid to the X root window for clients to find.
62 gtk.gdk.get_default_root_window().property_change(gtk.gdk.atom_intern(appl_id, False),
63 "WINDOW", 32, gtk.gdk.PROP_MODE_REPLACE, [window.window.xid])
65 #Register for notification events
66 window.add_events(gtk.gdk.PROPERTY_CHANGE_MASK)
67 window.connect('property-notify-event', handle_event, gtk.gdk.atom_intern("_XSOAP", False))
70 def unregister_server(appl_id):
71 """remove the application xid from the root window"""
72 if not appl_id or not isinstance(appl_id, str):
73 raise TypeError, "appl_id must be a string"
75 gtk.gdk.get_default_root_window().property_delete(gtk.gdk.atom_intern(appl_id, False))
78 def register_callback(callback):
79 """register the client's callback function for later"""
80 global _callback
81 _callback = callback
84 def handle_event(window, event, property):
85 """parse the property data and call the client's parser"""
86 global _callback
88 #msg is a list of atoms
89 msg = window.window.property_get(property)
91 #the use of str() gets the string value from the atom
92 if msg:
93 cmd = str(msg[2][0])
94 args = []
95 for a in msg[2][1:]:
96 args.append(str(a))
98 _callback(window, cmd, args)
100 #don't want to do this again and again...
101 window.window.property_delete(property)
104 def get_server(appl_id):
105 """query the root X window to find the server"""
106 if not appl_id or not isinstance(appl_id, str):
107 raise TypeError, "appl_id must be a string"
109 appl_window = None
111 #appl_id is the same as used in register_server
112 #the xid is the X window id of the server's rpc window
113 root_window = gtk.gdk.get_default_root_window()
114 xid = root_window.property_get(gtk.gdk.atom_intern(appl_id, False))
115 if xid:
116 #the xid is in a list in a tuple: c from (a, b, [c])
117 xid = xid[2][0]
119 #create a gdk window from the xid
120 appl_window = gtk.gdk.window_foreign_new(long(xid))
122 #if we didn't find the window we return None
123 return appl_window
126 def send_message(window, message, args):
127 """send a message and optional arguments to the server"""
129 if not isinstance(window, gtk.gdk.Window):
130 raise TypeError, "window must be a valid gtk.gdk.Window"
131 if not isinstance(message, str):
132 raise TypeError, "message must be a string"
133 if args and not isinstance(args, list):
134 raise TypeError, "args must be a list"
136 #format the message and arguments as a list of atoms
137 atom_args = [gtk.gdk.atom_intern(message, False)]
138 for x in args:
139 atom_args.append(gtk.gdk.atom_intern(x))
141 #send the message by setting the _XSOAP property on the server window
142 window.property_change(gtk.gdk.atom_intern("_XSOAP", False),
143 "ATOM", 32, gtk.gdk.PROP_MODE_REPLACE, atom_args)
145 #make sure the message gets through (triggers a notification event)
146 gtk.gdk.flush()