1 /* $Id: xgserver.c,v 1.1 2000/11/06 06:42:20 sgt Exp $
3 * Copyright 2000 Steve Tell
4 * Copyright (C) 1998, 1999, 2000 Greg J. Badros and Maciej Stachowiak
6 * This file is based on fragments of scwm.c and events.c from
7 * the SCWM window manager, by Greg J. Badros and Maciej Stachowiak
9 * extracting only the scwmexec protocol, generalizing it, and adapting
10 * it to gdk was done by Steve Tell.
13 #define XGNAME "GWAVE"
21 #include <sys/types.h>
27 #include <X11/Xatom.h>
28 #include <X11/Xutil.h>
31 #include <guile-gnome-gobject/gobject.h>
35 static GdkFilterReturn
xg_gdk_filter(GdkXEvent
*xevent
,
38 static void xg_cleanup();
39 extern char *remote_guile_eval(char *req
, size_t *reslen
,
40 char **outp
, size_t *outlenp
,
41 char **errp
, size_t *errlenp
);
43 static void xg_handle_exec();
46 /**CONCEPT: SCWMEXEC Protocol
47 Scwm supports a protocol for other programs to send commands to the
48 window manager. Programs send ordinary configuration language
49 expressions and are returned a string representation of the return
50 value, and the output and error output generated, if any.
52 For more information on how to make use of this protocol, see the
53 documentation for the scwmexec and scwmrepl programs, the scwm.el
54 emacs interaction mode, the libscwmexec library, and the details of
55 the SCWMEXEC protocol (as documented in
56 <filename>doc/scwmexec.proto</filename>).
60 /* w_for_scwmexec_response is the window that is used by scwmexec
61 protocol -- shutdown.c's Done function uses this too in case
62 scwmexec executes a quit, or causes a segfault (which cases
63 the HandleScwmExec function to not complete as it should) */
64 static Window w_for_exec_response
;
67 static Atom XA_XGEXEC_LISTENER
;
68 static Atom XA_XGEXEC_REQWIN
;
69 static Atom XA_XGEXEC_REQUEST
;
70 static Atom XA_XGEXEC_REPLY
;
71 static Atom XA_XGEXEC_NOTIFY
;
72 static Atom XA_XGEXEC_OUTPUT
;
73 static Atom XA_XGEXEC_ERROR
;
79 Reset the scwmexec protocol.\n\
80 This procedure removes the \"XA_SCWMEXEC_REQUEST\" property on the\n\
81 root window. It should not be necessary but may be useful in case\n\
82 your X server goes awry (and otherwise you would have to restart your\n\
83 X server). Use if scwmexec or scwmrepl are not returning (e.g.,\n\
84 if your Emacs hangs when you try evaluating a scwm expression).")
89 XDeleteProperty(Dpy
, Root
, XA_XGEXEC_REQUEST
);
93 xg_init(Display
*display
)
95 XWindowAttributes xwa
;
97 GdkWindow
*gdk_rootwindow
;
105 screen
= XDefaultScreen(Dpy
);
106 Root
= XRootWindow(Dpy
, screen
);
108 XA_XGEXEC_LISTENER
=XInternAtom(Dpy
, XGNAME
"EXEC_LISTENER", False
);
109 XA_XGEXEC_REQWIN
=XInternAtom(Dpy
, XGNAME
"EXEC_REQWIN", False
);
110 XA_XGEXEC_REQUEST
=XInternAtom(Dpy
, XGNAME
"EXEC_REQUEST", False
);
111 XA_XGEXEC_REPLY
=XInternAtom(Dpy
, XGNAME
"EXEC_REPLY", False
);
112 XA_XGEXEC_NOTIFY
=XInternAtom(Dpy
, XGNAME
"EXEC_NOTIFY", False
);
113 XA_XGEXEC_OUTPUT
=XInternAtom(Dpy
, XGNAME
"EXEC_OUTPUT", False
);
114 XA_XGEXEC_ERROR
=XInternAtom(Dpy
, XGNAME
"EXEC_ERROR", False
);
116 /* if the XA_XGEXEC_REQWIN window is already set at
117 startup, the first scwm-exec protocol request will cause
119 XDeleteProperty(Dpy
,Root
,XA_XGEXEC_REQWIN
);
121 g_atexit(xg_cleanup
);
123 /* Initialize scwmexec response window (used in shutdown.c's Done,
124 as well as by HandleScwmExec) */
125 w_for_exec_response
= None
;
126 /* Announce support for scwmexec protocol. */
127 XChangeProperty(Dpy
, Root
,
128 XA_XGEXEC_LISTENER
, XA_STRING
,
129 8, PropModeReplace
, (unsigned char *) XGNAME
"exec",
132 /* printf("xg_init root=%d LISTENER atom=%d\n", Root, XA_XGEXEC_LISTENER);
136 XGetWindowAttributes (gdk_display
, Root
, &xwa
);
137 old_event_mask
= xwa
.your_event_mask
;
138 XSelectInput (Dpy
, Root
, old_event_mask
| PropertyChangeMask
);
140 /* connect to gdk's XEvent handler using its event-filter mechanism */
141 gdk_rootwindow
= gdk_window_lookup(Root
);
142 gdk_window_add_filter(gdk_rootwindow
, xg_gdk_filter
, NULL
);
145 /* gdk event filter to hook into low-level gdk event handling
147 static GdkFilterReturn
148 xg_gdk_filter(GdkXEvent
*xevent
, GdkEvent
*event
, gpointer data
)
150 XEvent
*ev
= (XEvent
*)xevent
;
151 /* printf("in xg_gdk_filter data=%x type=%d\n", data, ev->type); */
153 if(ev
->type
== PropertyNotify
154 && ev
->xproperty
.atom
== XA_XGEXEC_REQWIN
) {
156 return GDK_FILTER_REMOVE
;
158 return GDK_FILTER_CONTINUE
;
164 XDeleteProperty(Dpy
, Root
, XA_XGEXEC_LISTENER
);
166 if (None
!= w_for_exec_response
) {
167 /* give a response to libscwmexec in case
168 we were in the middle of
169 an xgexec when we quit or segfaulted */
170 XChangeProperty(Dpy
, w_for_exec_response
,
171 XA_XGEXEC_OUTPUT
, XA_STRING
,
172 8, PropModeReplace
, "", 0);
173 XChangeProperty(Dpy
, w_for_exec_response
,
174 XA_XGEXEC_ERROR
, XA_STRING
,
175 8, PropModeReplace
, "", 0);
176 XChangeProperty(Dpy
, w_for_exec_response
,
177 XA_XGEXEC_REPLY
, XA_STRING
,
178 8, PropModeReplace
, "", 0);
182 /* implement the X-property-driven remote-exec protocol.
183 * gets called on PropertyNotify events.
192 unsigned char *ret
, *output
, *error
;
193 size_t reslen
, outlen
, errlen
;
195 unsigned long nitems
;
196 unsigned long bytes_after
;
198 unsigned long last_offset
=0;
199 unsigned long saved_bytes_after
=0;
201 /* The XGEXEC_REQWIN property is treated as a queue of window IDs
202 from which the request will be read. There may be more than one
203 (or fewer than one in some cases) by the time we get here. We
204 will loop and keep reading until we have snarfed the whole
205 property, to make sure we can safely delete it.
207 See also the doc/scwmexec.proto file for a high-level
208 description of this protocol.
211 /* Read a single request window from the queue. */
212 if (XGetWindowProperty(Dpy
, Root
, XA_XGEXEC_REQWIN
,
213 last_offset
, 1, True
, AnyPropertyType
,
214 &type_ret
, &form_ret
, &nitems
, &bytes_after
,
215 (unsigned char **) &pw
)==Success
&& pw
!= NULL
) {
216 /* This is the window we want to look at: */
219 /* Increment the offset at which to read within the property. It
220 will not get deleted until we read the very last bytes at the
222 last_offset
+= nitems
* (form_ret
/8);
223 /* Save an indication of whether we need to read more or not. */
224 saved_bytes_after
=bytes_after
;
226 /* DBUG((DBG,FUNC_NAME,"Trying to get request from %ld",w)); */
228 /* Get and delete its XGEXEC_REQUEST property. We do
229 XGetWindowProperty twice, once to get the length, and again
230 to read the whole length's worth. */
231 if (XGetWindowProperty(Dpy
, w
,
233 0, 0, False
, XA_STRING
,
234 &type_ret
, &form_ret
, &nitems
, &bytes_after
,
236 XGetWindowProperty(Dpy
, w
,
238 0, (bytes_after
/ 4) +
239 (bytes_after
% 4 ? 1 : 0), True
, XA_STRING
,
240 &type_ret
, &form_ret
, &nitems
, &bytes_after
,
243 /* before we eval the request, record the window to respond
244 in a global, so Done can respond if necessary (in case
245 the eval-d expression calls `quit' or seg faults, etc.)
246 TODO: implement this scwm behavior here.
248 w_for_exec_response
= w
;
250 ret
= remote_guile_eval(req
, &reslen
,
251 (char **)&output
, &outlen
,
252 (char **)&error
, &errlen
);
255 /* Set the output, error and reply properties appropriately. */
256 XChangeProperty(Dpy
, w_for_exec_response
,
257 XA_XGEXEC_OUTPUT
, XA_STRING
,
258 8, PropModeReplace
, output
,
260 XChangeProperty(Dpy
, w_for_exec_response
,
261 XA_XGEXEC_ERROR
, XA_STRING
,
262 8, PropModeReplace
, error
,
264 XChangeProperty(Dpy
, w_for_exec_response
,
265 XA_XGEXEC_REPLY
, XA_STRING
,
266 8, PropModeReplace
, ret
,
269 /* Since we successfully reset the reply properties,
270 shutdown.c's Done no longer needs to, so reset
272 w_for_exec_response
= None
;
278 fprintf(stderr
, "Cannot get XA_%sEXEC_REQUEST atom from window %ld",
279 XGNAME
, w_for_exec_response
);
282 /* XGetWindowProperty returned False */
283 /* DBUG((WARN,FUNC_NAME,"Done with last window in list of scwmexec requests"));*/
284 saved_bytes_after
= 0;
287 } while (saved_bytes_after
!= 0);
288 /* Repeat until we get a saved_bytes_after of 0 on reading XGEXEC_REQWIN,
289 indicating that we read it all and it was deleted. It may well have
290 been re-created before we exit, but that doesn't matter because we'll
291 get a PropertyNotify and re-enter, but the offset to use will correctly