2 xsoap.py: an X rpc module
4 Copyright 2004 Kenneth Hayber <ken@hayber.us>
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)
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...
52 if not isinstance(window
, gtk
.Window
):
53 raise TypeError, "appl_window is not a gtk.Window"
58 #need to realize (or show) the window to get a gdkWindow
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"""
84 def handle_event(window
, event
, property):
85 """parse the property data and call the client's parser"""
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
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"
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))
116 #the xid is in a list in a tuple: c from (a, b, [c])
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
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)]
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)