1 /* |
|
2 * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. Oracle designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Oracle in the LICENSE file that accompanied this code. |
|
10 * |
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 * version 2 for more details (a copy is included in the LICENSE file that |
|
15 * accompanied this code). |
|
16 * |
|
17 * You should have received a copy of the GNU General Public License version |
|
18 * 2 along with this work; if not, write to the Free Software Foundation, |
|
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 * |
|
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
22 * or visit www.oracle.com if you need additional information or have any |
|
23 * questions. |
|
24 */ |
|
25 |
|
26 #ifdef HEADLESS |
|
27 #error This file should not be included in headless library |
|
28 #endif |
|
29 |
|
30 #include "awt_mgrsel.h" |
|
31 |
|
32 static Atom XA_MANAGER = None; |
|
33 |
|
34 /* |
|
35 * Structures that describes the manager selection AWT listens to with |
|
36 * callabacks to the subsytems interested in the selection. (We only |
|
37 * listen to a couple of selections, so linear search is enough). |
|
38 */ |
|
39 struct AwtMgrsel { |
|
40 char *selname; /* base name of selection atoms */ |
|
41 Atom *per_scr_atoms; /* per-screen selection atoms (ICCCM 1.2.6) */ |
|
42 Atom *per_scr_owners; /* windows currently owning the selection */ |
|
43 long extra_mask; /* extra events to listen to on owners */ |
|
44 void *cookie; |
|
45 void (*callback_event)(int, XEvent *, void *); /* extra_mask events */ |
|
46 void (*callback_owner)(int, Window, long *, void *); /* owner changes */ |
|
47 struct AwtMgrsel *next; |
|
48 }; |
|
49 |
|
50 static struct AwtMgrsel *mgrsel_list = NULL; |
|
51 |
|
52 |
|
53 static int awt_mgrsel_screen(Window w); |
|
54 static Window awt_mgrsel_select_per_screen(Atom, long); |
|
55 static int awt_mgrsel_managed(XClientMessageEvent *mgrown); |
|
56 static int awt_mgrsel_unmanaged(XDestroyWindowEvent *ev); |
|
57 |
|
58 #ifdef DEBUG |
|
59 static void awt_mgrsel_dtraceManaged(XClientMessageEvent *mgrown); |
|
60 #endif |
|
61 |
|
62 |
|
63 |
|
64 /* |
|
65 * Find which screen the window W is the root of. |
|
66 * Returns the screen number, or -1 if W is not a root. |
|
67 */ |
|
68 static int |
|
69 awt_mgrsel_screen(Window w) |
|
70 { |
|
71 Display *dpy = awt_display; |
|
72 int scr; |
|
73 |
|
74 for (scr = 0; scr < ScreenCount(dpy); ++scr) { |
|
75 if (w == RootWindow(dpy, scr)) { |
|
76 return (scr); |
|
77 } |
|
78 } |
|
79 |
|
80 return (-1); |
|
81 } |
|
82 |
|
83 |
|
84 /************************************************************************ |
|
85 * For every one that asketh receiveth; and he that seeketh findeth; |
|
86 * and to him that knocketh it shall be opened. (Luke 11:10). |
|
87 */ |
|
88 |
|
89 |
|
90 /* |
|
91 * A method for a subsytem to express its interest in a certain |
|
92 * manager selection. |
|
93 * |
|
94 * If owner changes, the callback_owner will be called with the screen |
|
95 * number and the new owning window when onwership is established, or |
|
96 * None if the owner is gone. |
|
97 * |
|
98 * Events in extra_mask are selected for on owning windows (exsiting |
|
99 * ones and on new owners when established) and callback_event will be |
|
100 * called with the screen number and an event. |
|
101 * |
|
102 * The function returns an array of current owners. The size of the |
|
103 * array is ScreenCount(awt_display). The array is "owned" by this |
|
104 * module and should be considered by the caller as read-only. |
|
105 */ |
|
106 const Window * |
|
107 awt_mgrsel_select(const char *selname, long extra_mask, |
|
108 void *cookie, |
|
109 void (*callback_event)(int, XEvent *, void *), |
|
110 void (*callback_owner)(int, Window, long *, void *)) |
|
111 { |
|
112 Display *dpy = awt_display; |
|
113 struct AwtMgrsel *mgrsel; |
|
114 Atom *per_scr_atoms; |
|
115 Window *per_scr_owners; |
|
116 char *namesbuf; |
|
117 char **names; |
|
118 int per_scr_sz; |
|
119 int nscreens = ScreenCount(dpy); |
|
120 int scr; |
|
121 Status status; |
|
122 |
|
123 DASSERT(selname != NULL); |
|
124 DTRACE_PRINTLN1("MG: select: %s", selname); |
|
125 |
|
126 /* buffer size for one per-screen atom name */ |
|
127 per_scr_sz = strlen(selname) + /* "_S" */ 2 + /* %2d */ + 2 /* '\0' */+ 1; |
|
128 |
|
129 namesbuf = malloc(per_scr_sz * nscreens); /* actual storage for names */ |
|
130 names = malloc(sizeof(char *) * nscreens); /* pointers to names */ |
|
131 per_scr_atoms = malloc(sizeof(Atom) * nscreens); |
|
132 per_scr_owners = malloc(sizeof(Window) * nscreens); |
|
133 mgrsel = malloc(sizeof(struct AwtMgrsel)); |
|
134 |
|
135 if (namesbuf == NULL || names == NULL || per_scr_atoms == NULL |
|
136 || per_scr_owners == NULL || mgrsel == NULL) |
|
137 { |
|
138 DTRACE_PRINTLN("MG: select: unable to allocate memory"); |
|
139 if (namesbuf != NULL) free(namesbuf); |
|
140 if (names != NULL) free(names); |
|
141 if (per_scr_atoms != NULL) free(per_scr_atoms); |
|
142 if (per_scr_owners != NULL) free(per_scr_owners); |
|
143 if (mgrsel != NULL) free(mgrsel); |
|
144 return (NULL); |
|
145 } |
|
146 |
|
147 |
|
148 for (scr = 0; scr < nscreens; ++scr) { |
|
149 size_t sz; |
|
150 |
|
151 names[scr] = &namesbuf[per_scr_sz * scr]; |
|
152 sz = snprintf(names[scr], per_scr_sz, "%s_S%-d", selname, scr); |
|
153 DASSERT(sz < per_scr_sz); |
|
154 } |
|
155 |
|
156 status = XInternAtoms(dpy, names, nscreens, False, per_scr_atoms); |
|
157 |
|
158 free(names); |
|
159 free(namesbuf); |
|
160 |
|
161 if (status == 0) { |
|
162 DTRACE_PRINTLN("MG: select: XInternAtoms failed"); |
|
163 free(per_scr_atoms); |
|
164 free(per_scr_owners); |
|
165 return (NULL); |
|
166 } |
|
167 |
|
168 mgrsel->selname = strdup(selname); |
|
169 mgrsel->per_scr_atoms = per_scr_atoms; |
|
170 mgrsel->per_scr_owners = per_scr_owners; |
|
171 mgrsel->extra_mask = extra_mask; |
|
172 mgrsel->cookie = cookie; |
|
173 mgrsel->callback_event = callback_event; |
|
174 mgrsel->callback_owner = callback_owner; |
|
175 |
|
176 for (scr = 0; scr < nscreens; ++scr) { |
|
177 Window owner; |
|
178 |
|
179 owner = awt_mgrsel_select_per_screen(per_scr_atoms[scr], extra_mask); |
|
180 mgrsel->per_scr_owners[scr] = owner; |
|
181 #ifdef DEBUG |
|
182 if (owner == None) { |
|
183 DTRACE_PRINTLN1("MG: screen %d - None", scr); |
|
184 } else { |
|
185 DTRACE_PRINTLN2("MG: screen %d - 0x%08lx", scr, owner); |
|
186 } |
|
187 #endif |
|
188 } |
|
189 |
|
190 mgrsel->next = mgrsel_list; |
|
191 mgrsel_list = mgrsel; |
|
192 |
|
193 return (per_scr_owners); |
|
194 } |
|
195 |
|
196 |
|
197 static Window |
|
198 awt_mgrsel_select_per_screen(Atom selection, long extra_mask) |
|
199 { |
|
200 Display *dpy = awt_display; |
|
201 Window owner; |
|
202 |
|
203 XGrabServer(dpy); |
|
204 |
|
205 owner = XGetSelectionOwner(dpy, selection); |
|
206 if (owner == None) { |
|
207 /* we'll get notified later if one arrives */ |
|
208 XUngrabServer(dpy); |
|
209 /* Workaround for bug 5039226 */ |
|
210 XSync(dpy, False); |
|
211 return (None); |
|
212 } |
|
213 |
|
214 /* |
|
215 * Select for StructureNotifyMask to get DestroyNotify when owner |
|
216 * is gone. Also select for any additional events caller is |
|
217 * interested in (e.g. PropertyChangeMask). Caller will be |
|
218 * notifed of these events via ... XXX ... |
|
219 */ |
|
220 XSelectInput(dpy, owner, StructureNotifyMask | extra_mask); |
|
221 |
|
222 XUngrabServer(dpy); |
|
223 /* Workaround for bug 5039226 */ |
|
224 XSync(dpy, False); |
|
225 return (owner); |
|
226 } |
|
227 |
|
228 |
|
229 /************************************************************************ |
|
230 * And so I saw the wicked buried, who had come and gone from the |
|
231 * place of the holy, and they were forgotten in the city where they |
|
232 * had so done: this is also vanity. (Eccl 8:10) |
|
233 */ |
|
234 |
|
235 #ifdef DEBUG |
|
236 /* |
|
237 * Print the message from the new manager that announces it acquired |
|
238 * ownership. |
|
239 */ |
|
240 static void |
|
241 awt_mgrsel_dtraceManaged(XClientMessageEvent *mgrown) |
|
242 { |
|
243 Display *dpy = awt_display; |
|
244 Atom selection; |
|
245 char *selname, *print_selname; |
|
246 int scr; |
|
247 |
|
248 scr = awt_mgrsel_screen(mgrown->window); |
|
249 |
|
250 selection = mgrown->data.l[1]; |
|
251 print_selname = selname = XGetAtomName(dpy, selection); |
|
252 if (selname == NULL) { |
|
253 if (selection == None) { |
|
254 print_selname = "<None>"; |
|
255 } else { |
|
256 print_selname = "<Unknown>"; |
|
257 } |
|
258 } |
|
259 |
|
260 DTRACE_PRINTLN4("MG: new MANAGER for %s: screen %d, owner 0x%08lx (@%lu)", |
|
261 print_selname, scr, |
|
262 mgrown->data.l[2], /* the window owning the selection */ |
|
263 mgrown->data.l[0]); /* timestamp */ |
|
264 DTRACE_PRINTLN4("MG: %ld %ld / 0x%lx 0x%lx", /* extra data */ |
|
265 mgrown->data.l[3], mgrown->data.l[4], |
|
266 mgrown->data.l[3], mgrown->data.l[4]); |
|
267 |
|
268 if (selname != NULL) { |
|
269 XFree(selname); |
|
270 } |
|
271 } |
|
272 #endif /* DEBUG */ |
|
273 |
|
274 |
|
275 static int |
|
276 awt_mgrsel_managed(XClientMessageEvent *mgrown) |
|
277 { |
|
278 Display *dpy = awt_display; |
|
279 struct AwtMgrsel *mgrsel; |
|
280 int scr; |
|
281 |
|
282 long timestamp; |
|
283 Atom selection; |
|
284 Window owner; |
|
285 long *data; |
|
286 |
|
287 if (mgrown->message_type != XA_MANAGER) { |
|
288 DTRACE_PRINTLN("MG: ClientMessage type != MANAGER, ignoring"); |
|
289 return (0); |
|
290 } |
|
291 |
|
292 scr = awt_mgrsel_screen(mgrown->window); |
|
293 |
|
294 #ifdef DEBUG |
|
295 awt_mgrsel_dtraceManaged(mgrown); |
|
296 #endif |
|
297 |
|
298 if (scr < 0) { |
|
299 DTRACE_PRINTLN("MG: MANAGER ClientMessage with a non-root window!"); |
|
300 return (0); |
|
301 } |
|
302 |
|
303 timestamp = mgrown->data.l[0]; |
|
304 selection = mgrown->data.l[1]; |
|
305 owner = mgrown->data.l[2]; |
|
306 data = &mgrown->data.l[3]; /* long[2], selection specific */ |
|
307 |
|
308 /* is this a selection we are intrested in? */ |
|
309 for (mgrsel = mgrsel_list; mgrsel != NULL; mgrsel = mgrsel->next) { |
|
310 if (selection == mgrsel->per_scr_atoms[scr]) |
|
311 break; |
|
312 } |
|
313 |
|
314 if (mgrsel == NULL) { |
|
315 DTRACE_PRINTLN("MG: not interested in this selection, ignoring"); |
|
316 return (0); |
|
317 } |
|
318 |
|
319 |
|
320 mgrsel->per_scr_owners[scr] = owner; |
|
321 |
|
322 XSelectInput(dpy, owner, StructureNotifyMask | mgrsel->extra_mask); |
|
323 |
|
324 /* notify the listener */ |
|
325 if (mgrsel->callback_owner != NULL) { |
|
326 (*mgrsel->callback_owner)(scr, owner, data, mgrsel->cookie); |
|
327 } |
|
328 |
|
329 return (1); |
|
330 } |
|
331 |
|
332 |
|
333 static int |
|
334 awt_mgrsel_unmanaged(XDestroyWindowEvent *ev) |
|
335 { |
|
336 Display *dpy = awt_display; |
|
337 struct AwtMgrsel *mgrsel; |
|
338 Window exowner; |
|
339 int scr; |
|
340 |
|
341 exowner = ev->window; /* selection owner that's gone */ |
|
342 |
|
343 /* is this a selection we are intrested in? */ |
|
344 for (mgrsel = mgrsel_list; mgrsel != NULL; mgrsel = mgrsel->next) { |
|
345 for (scr = 0; scr < ScreenCount(dpy); ++scr) { |
|
346 if (exowner == mgrsel->per_scr_owners[scr]) { |
|
347 /* can one window own selections for more than one screen? */ |
|
348 goto out; /* XXX??? */ |
|
349 } |
|
350 } |
|
351 } |
|
352 out: |
|
353 if (mgrsel == NULL) { |
|
354 DTRACE_PRINTLN1("MG: DestroyNotify for 0x%08lx ignored", exowner); |
|
355 return (0); |
|
356 } |
|
357 |
|
358 DTRACE_PRINTLN3("MG: DestroyNotify for 0x%08lx, owner of %s at screen %d", |
|
359 exowner, mgrsel->selname, scr); |
|
360 |
|
361 /* notify the listener (pass exowner as data???) */ |
|
362 if (mgrsel->callback_owner != NULL) { |
|
363 (*mgrsel->callback_owner)(scr, None, NULL, mgrsel->cookie); |
|
364 } |
|
365 |
|
366 return (1); |
|
367 } |
|
368 |
|
369 |
|
370 /* |
|
371 * Hook to be called from toolkit event loop. |
|
372 */ |
|
373 int |
|
374 awt_mgrsel_processEvent(XEvent *ev) |
|
375 { |
|
376 Display *dpy = awt_display; |
|
377 struct AwtMgrsel *mgrsel; |
|
378 int scr; |
|
379 |
|
380 if (ev->type == ClientMessage) { /* new manager announces ownership? */ |
|
381 if (awt_mgrsel_managed(&ev->xclient)) |
|
382 return (1); |
|
383 } |
|
384 |
|
385 if (ev->type == DestroyNotify) { /* manager gives up selection? */ |
|
386 if (awt_mgrsel_unmanaged(&ev->xdestroywindow)) |
|
387 return (1); |
|
388 } |
|
389 |
|
390 /* is this an event selected on one of selection owners? */ |
|
391 for (mgrsel = mgrsel_list; mgrsel != NULL; mgrsel = mgrsel->next) { |
|
392 for (scr = 0; scr < ScreenCount(dpy); ++scr) { |
|
393 if (ev->xany.window == mgrsel->per_scr_owners[scr]) { |
|
394 /* can one window own selections for more than one screen? */ |
|
395 goto out; /* XXX??? */ |
|
396 } |
|
397 } |
|
398 } |
|
399 out: |
|
400 DTRACE_PRINT2("MG: screen %d, event %d ... ", |
|
401 scr, ev->xany.type); |
|
402 if (mgrsel == NULL) { |
|
403 DTRACE_PRINTLN("ignored"); |
|
404 return (0); /* not interested */ |
|
405 } |
|
406 |
|
407 DTRACE_PRINT1("%s ... ", mgrsel->selname); |
|
408 if (mgrsel->callback_event != NULL) { |
|
409 DTRACE_PRINTLN("dispatching"); |
|
410 (*mgrsel->callback_event)(scr, ev, mgrsel->cookie); |
|
411 } |
|
412 #ifdef DEBUG |
|
413 else { |
|
414 DTRACE_PRINTLN("no callback"); |
|
415 } |
|
416 #endif |
|
417 |
|
418 return (1); |
|
419 } |
|
420 |
|
421 |
|
422 void |
|
423 awt_mgrsel_init(void) |
|
424 { |
|
425 static Boolean inited = False; |
|
426 |
|
427 Display *dpy = awt_display; |
|
428 int scr; |
|
429 |
|
430 if (inited) { |
|
431 return; |
|
432 } |
|
433 |
|
434 XA_MANAGER = XInternAtom(dpy, "MANAGER", False); |
|
435 DASSERT(XA_MANAGER != None); |
|
436 |
|
437 |
|
438 /* |
|
439 * Listen for ClientMessage's on each screen's root. We hook into |
|
440 * the message loop in the toolkit (with awt_mgrsel_processEvent) |
|
441 * to get the events processed. We need this for notifications of |
|
442 * new manager acquiring ownership of the manager selection. |
|
443 */ |
|
444 for (scr = 0; scr < ScreenCount(dpy); ++scr) { |
|
445 XSelectInput(dpy, RootWindow(dpy, scr), StructureNotifyMask); |
|
446 } |
|
447 |
|
448 inited = True; |
|
449 } |
|