CloseHook.c revision 9dedec0c
1/*
2Copyright 1989, 1998  The Open Group
3
4Permission to use, copy, modify, distribute, and sell this software and its
5documentation for any purpose is hereby granted without fee, provided that
6the above copyright notice appear in all copies and that both that
7copyright notice and this permission notice appear in supporting
8documentation.
9
10The above copyright notice and this permission notice shall be included in
11all copies or substantial portions of the Software.
12
13THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
16OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
17AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19
20Except as contained in this notice, the name of The Open Group shall not be
21used in advertising or otherwise to promote the sale, use or other dealings
22in this Software without prior written authorization from The Open Group.
23
24*/
25
26/*
27 * CloseDisplayHook package - provide callback on XCloseDisplay
28 *
29 * *
30 * Author:  Jim Fulton, MIT X Consortium
31 *
32 *
33 *			      Public Entry Points
34 *
35 * CloseHook XmuAddCloseDisplayHook (dpy, func, arg)
36 *     Display *dpy;
37 *     XmuCloseHookProc func;
38 *     XPointer arg;
39 *
40 * Bool XmuRemoveCloseDisplayHook (dpy, hook, func, arg)
41 *     Display *dpy;
42 *     CloseHook hook;
43 *     XmuCloseHookProc func;
44 *     XPointer arg;
45 *
46 * Bool XmuLookupCloseDisplayHook (dpy, hook, func, arg)
47 *     Display *dpy;
48 *     CloseHook hook;
49 *     XmuCloseHookProc func;
50 *     XPointer arg;
51 *
52 */
53
54#ifdef HAVE_CONFIG_H
55#include <config.h>
56#endif
57#include <stdio.h>					/* for NULL */
58#include <X11/Xos.h>
59#include <X11/Xlib.h>
60#include <X11/Xmu/CloseHook.h>
61#include <stdlib.h>
62
63/*
64 *				 Private data
65 *
66 * This is a list of display entries, each of which contains a list of callback
67 * records.
68 */
69
70typedef struct _CallbackRec {
71    struct _CallbackRec *next;		/* next link in chain */
72    XmuCloseHookProc func;		/* function to call */
73    XPointer arg;			/* argument to pass with function */
74} CallbackRec;
75
76
77typedef struct _DisplayEntry {
78    struct _DisplayEntry *next;		/* next link in chain */
79    Display *dpy;			/* the display this represents */
80    int extension;			/* from XAddExtension */
81    struct _CallbackRec *start, *end;	/* linked list of callbacks */
82    struct _CallbackRec *calling;	/* currently being called back */
83} DisplayEntry;
84
85/*
86 * Prototypes
87 */
88static DisplayEntry *_FindDisplayEntry(Display*, DisplayEntry**);
89static Bool _MakeExtension(Display*, int*);
90
91static DisplayEntry *elist = NULL;
92
93
94/*
95 *****************************************************************************
96 *			      Public Entry Points                            *
97 *****************************************************************************
98 */
99
100/*
101 * Add - add a callback for the given display.  When the display is closed,
102 * the given function will be called as:
103 *
104 *         (*func) (dpy, arg)
105 *
106 * This function is declared to return an int even though the value is ignored
107 * because some compilers have problems with functions returning void.
108 *
109 * This routine returns NULL if it was unable to add the callback, otherwise
110 * it returns an untyped pointer that can be used with Remove or Lookup, but
111 * not dereferenced.
112 */
113CloseHook
114XmuAddCloseDisplayHook(Display *dpy, XmuCloseHookProc func, XPointer arg)
115{
116    DisplayEntry *de;
117    CallbackRec *cb;
118
119    /* allocate ahead of time so that we can fail atomically */
120    cb = malloc (sizeof (CallbackRec));
121    if (!cb) return ((XPointer) NULL);
122
123    de = _FindDisplayEntry (dpy, NULL);
124    if (!de) {
125	if ((de = malloc (sizeof (DisplayEntry))) == NULL ||
126	    !_MakeExtension (dpy, &de->extension)) {
127	    free (cb);
128	    free (de);
129	    return ((CloseHook) NULL);
130	}
131	de->dpy = dpy;
132	de->start = de->end = NULL;
133	de->calling = NULL;
134	de->next = elist;
135	elist = de;
136    }
137
138    /* add to end of list of callback records */
139    cb->func = func;
140    cb->arg = arg;
141    cb->next = NULL;
142    if (de->end) {
143	de->end->next = cb;
144    } else {
145	de->start = cb;
146    }
147    de->end = cb;
148
149    return ((CloseHook) cb);
150}
151
152
153/*
154 * Remove - get rid of a callback.  If handle is non-null, use that to compare
155 * entries.  Otherwise, remove first instance of the function/argument pair.
156 */
157Bool
158XmuRemoveCloseDisplayHook(Display *dpy, CloseHook handle,
159			  XmuCloseHookProc func, XPointer arg)
160{
161    DisplayEntry *de = _FindDisplayEntry (dpy, NULL);
162    register CallbackRec *h, *prev;
163
164    if (!de) return False;
165
166    /* look for handle or function/argument pair */
167    for (h = de->start, prev = NULL; h; h = h->next) {
168	if (handle) {
169	    if (h == (CallbackRec *) handle) break;
170	} else {
171	    if (h->func == func && h->arg == arg) break;
172	}
173	prev = h;
174    }
175    if (!h) return False;
176
177
178    /* remove from list, watch head and tail */
179    if (de->start == h) {
180	de->start = h->next;
181    } else {
182	prev->next = h->next;
183    }
184    if (de->end == h) de->end = prev;
185    if (de->calling != h) free (h);
186    return True;
187}
188
189
190/*
191 * Lookup - see whether or not a handle has been installed.  If handle is
192 * non-NULL, look for an entry that matches it; otherwise look for an entry
193 * with the same function/argument pair.
194 */
195Bool
196XmuLookupCloseDisplayHook(Display *dpy, CloseHook handle,
197			  XmuCloseHookProc func, XPointer arg)
198{
199    DisplayEntry *de = _FindDisplayEntry (dpy, NULL);
200    register CallbackRec *h;
201
202    if (!de) return False;
203
204    for (h = de->start; h; h = h->next) {
205	if (handle) {
206	    if (h == (CallbackRec *) handle) break;
207	} else {
208	    if (h->func == func && h->arg == arg) break;
209	}
210    }
211    return (h ? True : False);
212}
213
214
215/*
216 *****************************************************************************
217 *			       internal routines                             *
218 *****************************************************************************
219 */
220
221
222/*
223 * Find the specified display on the linked list of displays.  Also return
224 * the preceding link so that the display can be unlinked without having
225 * back pointers.
226 */
227static DisplayEntry *
228_FindDisplayEntry(register Display *dpy, DisplayEntry **prevp)
229{
230    register DisplayEntry *d, *prev;
231
232    for (d = elist, prev = NULL; d; d = d->next) {
233	if (d->dpy == dpy) {
234	    if (prevp) *prevp = prev;
235	    return d;
236	}
237	prev = d;
238    }
239    return NULL;
240}
241
242
243
244/*
245 * _DoCallbacks - process all of the callbacks for this display and free
246 * the associated callback data (callback records and display entries).
247 */
248/* ARGSUSED */
249static int
250_DoCallbacks(Display *dpy, XExtCodes *codes)
251{
252    register CallbackRec *h;
253    DisplayEntry *prev;
254    DisplayEntry *de = _FindDisplayEntry (dpy, &prev);
255
256    if (!de) return 0;
257
258    /* walk the list doing the callbacks and freeing callback record */
259    for (h = de->start; h;) {
260	register CallbackRec *nexth = h->next;
261	de->calling = h;		/* let remove know we'll free it */
262	(*(h->func)) (dpy, h->arg);
263	de->calling = NULL;
264	free (h);
265	h = nexth;
266    }
267
268    /* unlink this display from chain */
269    if (elist == de) {
270	elist = de->next;
271    } else {
272	prev->next = de->next;
273    }
274    free (de);
275    return 1;
276}
277
278
279/*
280 * _MakeExtension - create an extension for this display; done once per display
281 */
282static Bool
283_MakeExtension(Display *dpy, int *extensionp)
284{
285    XExtCodes *codes;
286
287    codes = XAddExtension (dpy);
288    if (!codes) return False;
289
290    (void) XESetCloseDisplay (dpy, codes->extension, _DoCallbacks);
291
292    *extensionp = codes->extension;
293    return True;
294}
295