]> git.zerfleddert.de Git - micropolis/blob - src/tk/tkshare.c
Import Micropolis from http://www.donhopkins.com/home/micropolis/
[micropolis] / src / tk / tkshare.c
1 /*
2 * tkShare.c --
3 *
4 * This module implements a simple mechanism for sharing
5 * mouse- and button-related events among collections of
6 * windows. It is used primarily for menus. For example,
7 * if one menu is posted and mouse moves over the menu button
8 * for a different menu, then the menubutton needs to see the
9 * event so that it can post itself and unpost the first menu.
10 *
11 * Copyright 1990-1992 Regents of the University of California
12 * Permission to use, copy, modify, and distribute this
13 * software and its documentation for any purpose and without
14 * fee is hereby granted, provided that the above copyright
15 * notice appear in all copies. The University of California
16 * makes no representations about the suitability of this
17 * software for any purpose. It is provided "as is" without
18 * express or implied warranty.
19 */
20
21 #ifndef lint
22 static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkShare.c,v 1.10 92/05/31 16:20:12 ouster Exp $ SPRITE (Berkeley)";
23 #endif /* not lint */
24
25 #include "tkconfig.h"
26 #include "tk.h"
27
28 /*
29 * the global variable below is used to tell TkPointerEvent
30 * not to do any processing on an event that we're forwarding from one
31 * window to another. This is really ugly. Eventually this file and
32 * tkGrab.c need to get merged together to produce something cleaner.
33 */
34
35 XEvent *tkShareEventPtr = NULL;
36
37 /*
38 * Sharing is implemented in terms of groups of windows, where events
39 * are shared among all the windows in a group. One of the following
40 * structures exists for each group.
41 */
42
43 typedef struct Group {
44 Tk_Uid groupId; /* Identifies group uniquely among all
45 * share groups. */
46 Tk_Window *windows; /* Pointer to array of windows in
47 * this group. Malloc'ed. */
48 int numWindows; /* Number of windows currently in
49 * this group. */
50 Tk_Window lastWindow; /* Last window found that contained
51 * an event. Needed in order to
52 * notify window when mouse moves out
53 * of it. NULL means nobody to
54 * notify. */
55 XEvent *activeEvent; /* If non-NULL, means that a recursive
56 * call to Tk_HandleEvent is in
57 * progress for this share group, and
58 * identifies event. NULL means no
59 * recursive call in progress. Used
60 * to avoid infinite recursion. */
61 struct Group *nextPtr; /* Next in list of all share groups. */
62 } Group;
63
64 static Group *groupList = NULL; /* First in list of all share groups
65 * currently defined. */
66
67 /*
68 * Forward declarations for procedures defined later in this file:
69 */
70
71 static void DeleteGroup _ANSI_ARGS_((Group *groupPtr));
72 static void ShareEventProc _ANSI_ARGS_((ClientData clientData,
73 XEvent *eventPtr));
74 \f
75 /*
76 *----------------------------------------------------------------------
77 *
78 * Tk_ShareEvents --
79 *
80 * Add tkwin to a group of windows sharing events.
81 *
82 * Results:
83 * None.
84 *
85 * Side effects:
86 * In the future, if a button- or mouse-related event occurs for
87 * any window in the same group as tkwin, but the mouse is actually
88 * in tkwin (the event went to a different window because of a
89 * grab) then a synthetic event will be generated with tkwin as
90 * window and adjusted coordinates.
91 *
92 *----------------------------------------------------------------------
93 */
94
95 void
96 Tk_ShareEvents(tkwin, groupId)
97 Tk_Window tkwin; /* Token for window. */
98 Tk_Uid groupId; /* Identifier for group among which
99 * events are to be shared. */
100 {
101 register Group *groupPtr;
102
103 /*
104 * See if this group exists. If so, add the window to the group.
105 */
106
107 for (groupPtr = groupList; groupPtr != NULL;
108 groupPtr = groupPtr->nextPtr) {
109 Tk_Window *new;
110
111 if (groupPtr->groupId != groupId) {
112 continue;
113 }
114 new = (Tk_Window *) ckalloc((unsigned)
115 (groupPtr->numWindows+1) * sizeof(Tk_Window *));
116 memcpy((VOID *) (new+1), (VOID *) groupPtr->windows,
117 (groupPtr->numWindows * sizeof(Tk_Window *)));
118 ckfree((char *) groupPtr->windows);
119 groupPtr->windows = new;
120 groupPtr->windows[0] = tkwin;
121 groupPtr->numWindows++;
122 break;
123 }
124
125 if (groupPtr == NULL) {
126 /*
127 * Group doesn't exist. Make a new one.
128 */
129
130 groupPtr = (Group *) ckalloc(sizeof(Group));
131 groupPtr->groupId = groupId;
132 groupPtr->windows = (Tk_Window *) ckalloc(sizeof (Tk_Window *));
133 groupPtr->windows[0] = tkwin;
134 groupPtr->numWindows = 1;
135 groupPtr->lastWindow = NULL;
136 groupPtr->activeEvent = NULL;
137 groupPtr->nextPtr = groupList;
138 groupList = groupPtr;
139 }
140
141 /*
142 * Create an event handler so we find out about relevant events
143 * that are directed to tkwin.
144 */
145
146 Tk_CreateEventHandler(tkwin,
147 ButtonPressMask|ButtonReleaseMask|PointerMotionMask,
148 ShareEventProc, (ClientData) groupPtr);
149 }
150 \f
151 /*
152 *----------------------------------------------------------------------
153 *
154 * Tk_UnshareEvents --
155 *
156 * Remove tkwin from a group of windows sharing events.
157 *
158 * Results:
159 * None.
160 *
161 * Side effects:
162 * Tkwin will no longer participate in event-sharing for the
163 * given group, either as source of events or as destination.
164 *
165 *----------------------------------------------------------------------
166 */
167
168 void
169 Tk_UnshareEvents(tkwin, groupId)
170 Tk_Window tkwin; /* Token for window. */
171 Tk_Uid groupId; /* Identifier for group. */
172 {
173 register Group *groupPtr;
174 int i;
175
176 for (groupPtr = groupList; groupPtr != NULL;
177 groupPtr = groupPtr->nextPtr) {
178 if (groupPtr->groupId != groupId) {
179 continue;
180 }
181 if (groupPtr->lastWindow == tkwin) {
182 groupPtr->lastWindow = NULL;
183 }
184 for (i = 0; i < groupPtr->numWindows; i++) {
185 if (groupPtr->windows[i] != tkwin) {
186 continue;
187 }
188 if ((i+1) < groupPtr->numWindows) {
189 memcpy((VOID *) (groupPtr->windows + i),
190 (VOID *) (groupPtr->windows + i + 1),
191 (groupPtr->numWindows - (i+1))*sizeof(Tk_Window *));
192 }
193 groupPtr->numWindows--;
194 Tk_DeleteEventHandler(tkwin,
195 ButtonPressMask|ButtonReleaseMask|PointerMotionMask,
196 ShareEventProc, (ClientData) groupPtr);
197 if (groupPtr->numWindows == 0) {
198 DeleteGroup(groupPtr);
199 }
200 return;
201 }
202 }
203 }
204 \f
205 /*
206 *----------------------------------------------------------------------
207 *
208 * DeleteGroup --
209 *
210 * This procedure is called when a group has no more members.
211 * It deletes the group from the list of existing groups.
212 *
213 * Results:
214 * None.
215 *
216 * Side effects:
217 * Memory gets freed.
218 *
219 *----------------------------------------------------------------------
220 */
221
222 static void
223 DeleteGroup(groupPtr)
224 Group *groupPtr; /* Group to delete. */
225 {
226 if (groupList == groupPtr) {
227 groupList = groupPtr->nextPtr;
228 } else {
229 register Group *prevPtr;
230
231 for (prevPtr = groupList; ; prevPtr = prevPtr->nextPtr) {
232 if (prevPtr == NULL) {
233 panic("DeleteGroup couldn't find group on shareList");
234 }
235 if (prevPtr->nextPtr == groupPtr) {
236 prevPtr->nextPtr = groupPtr->nextPtr;
237 break;
238 }
239 }
240 }
241 ckfree((char *) groupPtr->windows);
242 ckfree((char *) groupPtr);
243 }
244 \f
245 /*
246 *----------------------------------------------------------------------
247 *
248 * ShareEventProc --
249 *
250 * This procedure is invoked by the Tk dispatcher when an event
251 * occurs for which we need to implement sharing.
252 *
253 * Results:
254 * None.
255 *
256 * Side effects:
257 * If the mouse is actually in a window other than the one for
258 * which the event occurred, generate a new event translated to
259 * that window.
260 *
261 *----------------------------------------------------------------------
262 */
263
264 static void
265 ShareEventProc(clientData, eventPtr)
266 ClientData clientData; /* Information about share group. */
267 register XEvent *eventPtr; /* Event that just occurred. */
268 {
269 register Group *groupPtr = (Group *) clientData;
270 register Tk_Window tkwin;
271 Window window;
272 XEvent newEvent, *savedActive, *savedShareEventPtr;
273 int i, x, y;
274 Tk_Uid savedId;
275 register Group *grpPtr;
276
277 /*
278 * If this event was a synthetic one that we generated, then
279 * don't bother to process it again.
280 */
281
282 if (groupPtr->activeEvent == eventPtr) {
283 return;
284 }
285 savedActive = groupPtr->activeEvent;
286 groupPtr->activeEvent = &newEvent;
287 savedId = groupPtr->groupId;
288
289 savedShareEventPtr = tkShareEventPtr;
290 tkShareEventPtr = &newEvent;
291
292 /*
293 * Scan through all of the windows for this group to find the
294 * first one (if any) that contains the event.
295 */
296
297 tkwin = NULL; /* Not needed, but stops compiler warning. */
298 for (i = 0; i < groupPtr->numWindows; i++) {
299 Tk_Window tkwin2;
300
301 tkwin = groupPtr->windows[i];
302 Tk_GetRootCoords(tkwin, &x, &y);
303 x = eventPtr->xmotion.x_root - x - Tk_Changes(tkwin)->border_width;
304 y = eventPtr->xmotion.y_root - y - Tk_Changes(tkwin)->border_width;
305 if ((x < 0) || (y < 0) || (x >= Tk_Width(tkwin))
306 || (y >= Tk_Height(tkwin))) {
307 continue;
308 }
309 for (tkwin2 = tkwin; ; tkwin2 = Tk_Parent(tkwin2)) {
310 if (tkwin2 == NULL) {
311 goto foundWindow;
312 }
313 if (!Tk_IsMapped(tkwin2)) {
314 break;
315 }
316 if (((Tk_FakeWin *) (tkwin2))->flags & TK_TOP_LEVEL) {
317 goto foundWindow;
318 }
319 }
320 }
321
322 foundWindow:
323 window = None; /* Not really needed but stops compiler warning. */
324 if (i >= groupPtr->numWindows) {
325 tkwin = NULL;
326 } else {
327 window = Tk_WindowId(tkwin);
328 }
329
330 /*
331 * SPECIAL NOTE: it is possible that any or all of the information
332 * in groupPtr could be modified as part of the processing of the
333 * events that we generate and hand to Tk_HandleEvent below. For this
334 * to work smoothly, it is imperative that we extract any information
335 * we need from groupPtr (and from tkwin's, since they could be
336 * deleted) before the first call to Tk_HandleEvent below. The code
337 * below may potentially pass an X window identifier to Tk_HandleEvent
338 * after the window has been deleted, but as long as identifiers
339 * aren't recycled Tk_HandleEvent will simply discard the event if
340 * this occurs.
341 */
342
343 /*
344 * If the pointer is in a different window now than the last time
345 * we were invoked, send a LeaveNotify event to the old window and
346 * an EnterNotify event to the new window.
347 */
348
349 newEvent = *eventPtr;
350 newEvent.xany.send_event = True;
351 if (tkwin != groupPtr->lastWindow) {
352 newEvent = *eventPtr;
353 newEvent.xany.send_event = True;
354 newEvent.xcrossing.mode = TK_NOTIFY_SHARE;
355 newEvent.xcrossing.detail = NotifyAncestor;
356 newEvent.xcrossing.same_screen = True;
357 newEvent.xcrossing.state = eventPtr->xmotion.state;
358 if (groupPtr->lastWindow != NULL) {
359 newEvent.xcrossing.type = LeaveNotify;
360 newEvent.xcrossing.window = Tk_WindowId(groupPtr->lastWindow);
361 Tk_GetRootCoords(groupPtr->lastWindow, &newEvent.xcrossing.x,
362 &newEvent.xcrossing.y);
363 newEvent.xcrossing.x = eventPtr->xmotion.x_root
364 - newEvent.xcrossing.x
365 - Tk_Changes(groupPtr->lastWindow)->border_width;
366 newEvent.xcrossing.y = eventPtr->xmotion.y_root
367 - newEvent.xcrossing.y
368 - Tk_Changes(groupPtr->lastWindow)->border_width;
369 Tk_HandleEvent(&newEvent);
370 }
371 if (tkwin != NULL) {
372 newEvent.xcrossing.type = EnterNotify;
373 newEvent.xcrossing.window = window;
374 newEvent.xcrossing.x = x;
375 newEvent.xcrossing.y = y;
376 Tk_HandleEvent(&newEvent);
377 }
378 groupPtr->lastWindow = tkwin;
379 }
380
381 /*
382 * If the pointer is in the window to which the event was sent,
383 * then we needn't do any forwarding at all. Ditto if the pointer
384 * isn't in any window at all.
385 */
386
387 if ((tkwin != NULL) && (Tk_WindowId(tkwin) != eventPtr->xmotion.window)) {
388 newEvent = *eventPtr;
389 newEvent.xmotion.send_event = True;
390 newEvent.xmotion.window = window;
391 newEvent.xmotion.x = x;
392 newEvent.xmotion.y = y;
393 Tk_HandleEvent(&newEvent);
394 }
395
396 /*
397 * Only restore the activeEvent if the group still exists.
398 * (It could be deleted as a side effect of processing the event.)
399 */
400
401 for (grpPtr = groupList; grpPtr != NULL; grpPtr = grpPtr->nextPtr) {
402 if (grpPtr->groupId == savedId) {
403 groupPtr->activeEvent = savedActive;
404 break;
405 }
406 }
407
408 tkShareEventPtr = savedShareEventPtr;
409 }
Impressum, Datenschutz