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