Imported Upstream version 1.1.1
[platform/upstream/libXmu.git] / src / CloseHook.c
1 /*
2 Copyright 1989, 1998  The Open Group
3
4 Permission to use, copy, modify, distribute, and sell this software and its
5 documentation for any purpose is hereby granted without fee, provided that
6 the above copyright notice appear in all copies and that both that
7 copyright notice and this permission notice appear in supporting
8 documentation.
9
10 The above copyright notice and this permission notice shall be included in
11 all copies or substantial portions of the Software.
12
13 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
16 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
17 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19
20 Except as contained in this notice, the name of The Open Group shall not be
21 used in advertising or otherwise to promote the sale, use or other dealings
22 in 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
70 typedef 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
77 typedef 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  */
88 static DisplayEntry *_FindDisplayEntry(Display*, DisplayEntry**);
89 static Bool _MakeExtension(Display*, int*);
90
91 static 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  */
113 CloseHook
114 XmuAddCloseDisplayHook(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 = (CallbackRec *) malloc (sizeof (CallbackRec));
121     if (!cb) return ((XPointer) NULL);
122
123     de = _FindDisplayEntry (dpy, NULL);
124     if (!de) {
125         if ((de = (DisplayEntry *) malloc (sizeof (DisplayEntry))) == NULL ||
126             !_MakeExtension (dpy, &de->extension)) {
127             free ((char *) cb);
128             if (de) free ((char *) 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 recordss */
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  */
157 Bool
158 XmuRemoveCloseDisplayHook(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 ((char *) 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  */
195 Bool
196 XmuLookupCloseDisplayHook(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 preceeding link so that the display can be unlinked without having
225  * back pointers.
226  */
227 static 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 */
249 static 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 ((char *) 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 ((char *) de);
275     return 1;
276 }
277
278
279 /*
280  * _MakeExtension - create an extension for this display; done once per display
281  */
282 static 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 }