]>
git.zerfleddert.de Git - micropolis/blob - src/tk/tkshare.c
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.
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.
22 static char rcsid
[] = "$Header: /user6/ouster/wish/RCS/tkShare.c,v 1.10 92/05/31 16:20:12 ouster Exp $ SPRITE (Berkeley)";
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.
35 XEvent
*tkShareEventPtr
= NULL
;
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.
43 typedef struct Group
{
44 Tk_Uid groupId
; /* Identifies group uniquely among all
46 Tk_Window
*windows
; /* Pointer to array of windows in
47 * this group. Malloc'ed. */
48 int numWindows
; /* Number of windows currently in
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
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. */
64 static Group
*groupList
= NULL
; /* First in list of all share groups
65 * currently defined. */
68 * Forward declarations for procedures defined later in this file:
71 static void DeleteGroup
_ANSI_ARGS_((Group
*groupPtr
));
72 static void ShareEventProc
_ANSI_ARGS_((ClientData clientData
,
76 *----------------------------------------------------------------------
80 * Add tkwin to a group of windows sharing events.
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.
92 *----------------------------------------------------------------------
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. */
101 register Group
*groupPtr
;
104 * See if this group exists. If so, add the window to the group.
107 for (groupPtr
= groupList
; groupPtr
!= NULL
;
108 groupPtr
= groupPtr
->nextPtr
) {
111 if (groupPtr
->groupId
!= groupId
) {
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
++;
125 if (groupPtr
== NULL
) {
127 * Group doesn't exist. Make a new one.
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
;
142 * Create an event handler so we find out about relevant events
143 * that are directed to tkwin.
146 Tk_CreateEventHandler(tkwin
,
147 ButtonPressMask
|ButtonReleaseMask
|PointerMotionMask
,
148 ShareEventProc
, (ClientData
) groupPtr
);
152 *----------------------------------------------------------------------
154 * Tk_UnshareEvents --
156 * Remove tkwin from a group of windows sharing events.
162 * Tkwin will no longer participate in event-sharing for the
163 * given group, either as source of events or as destination.
165 *----------------------------------------------------------------------
169 Tk_UnshareEvents(tkwin
, groupId
)
170 Tk_Window tkwin
; /* Token for window. */
171 Tk_Uid groupId
; /* Identifier for group. */
173 register Group
*groupPtr
;
176 for (groupPtr
= groupList
; groupPtr
!= NULL
;
177 groupPtr
= groupPtr
->nextPtr
) {
178 if (groupPtr
->groupId
!= groupId
) {
181 if (groupPtr
->lastWindow
== tkwin
) {
182 groupPtr
->lastWindow
= NULL
;
184 for (i
= 0; i
< groupPtr
->numWindows
; i
++) {
185 if (groupPtr
->windows
[i
] != tkwin
) {
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
*));
193 groupPtr
->numWindows
--;
194 Tk_DeleteEventHandler(tkwin
,
195 ButtonPressMask
|ButtonReleaseMask
|PointerMotionMask
,
196 ShareEventProc
, (ClientData
) groupPtr
);
197 if (groupPtr
->numWindows
== 0) {
198 DeleteGroup(groupPtr
);
206 *----------------------------------------------------------------------
210 * This procedure is called when a group has no more members.
211 * It deletes the group from the list of existing groups.
219 *----------------------------------------------------------------------
223 DeleteGroup(groupPtr
)
224 Group
*groupPtr
; /* Group to delete. */
226 if (groupList
== groupPtr
) {
227 groupList
= groupPtr
->nextPtr
;
229 register Group
*prevPtr
;
231 for (prevPtr
= groupList
; ; prevPtr
= prevPtr
->nextPtr
) {
232 if (prevPtr
== NULL
) {
233 panic("DeleteGroup couldn't find group on shareList");
235 if (prevPtr
->nextPtr
== groupPtr
) {
236 prevPtr
->nextPtr
= groupPtr
->nextPtr
;
241 ckfree((char *) groupPtr
->windows
);
242 ckfree((char *) groupPtr
);
246 *----------------------------------------------------------------------
250 * This procedure is invoked by the Tk dispatcher when an event
251 * occurs for which we need to implement sharing.
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
261 *----------------------------------------------------------------------
265 ShareEventProc(clientData
, eventPtr
)
266 ClientData clientData
; /* Information about share group. */
267 register XEvent
*eventPtr
; /* Event that just occurred. */
269 register Group
*groupPtr
= (Group
*) clientData
;
270 register Tk_Window tkwin
;
272 XEvent newEvent
, *savedActive
, *savedShareEventPtr
;
275 register Group
*grpPtr
;
278 * If this event was a synthetic one that we generated, then
279 * don't bother to process it again.
282 if (groupPtr
->activeEvent
== eventPtr
) {
285 savedActive
= groupPtr
->activeEvent
;
286 groupPtr
->activeEvent
= &newEvent
;
287 savedId
= groupPtr
->groupId
;
289 savedShareEventPtr
= tkShareEventPtr
;
290 tkShareEventPtr
= &newEvent
;
293 * Scan through all of the windows for this group to find the
294 * first one (if any) that contains the event.
297 tkwin
= NULL
; /* Not needed, but stops compiler warning. */
298 for (i
= 0; i
< groupPtr
->numWindows
; i
++) {
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
))) {
309 for (tkwin2
= tkwin
; ; tkwin2
= Tk_Parent(tkwin2
)) {
310 if (tkwin2
== NULL
) {
313 if (!Tk_IsMapped(tkwin2
)) {
316 if (((Tk_FakeWin
*) (tkwin2
))->flags
& TK_TOP_LEVEL
) {
323 window
= None
; /* Not really needed but stops compiler warning. */
324 if (i
>= groupPtr
->numWindows
) {
327 window
= Tk_WindowId(tkwin
);
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
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.
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
);
372 newEvent
.xcrossing
.type
= EnterNotify
;
373 newEvent
.xcrossing
.window
= window
;
374 newEvent
.xcrossing
.x
= x
;
375 newEvent
.xcrossing
.y
= y
;
376 Tk_HandleEvent(&newEvent
);
378 groupPtr
->lastWindow
= tkwin
;
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.
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
);
397 * Only restore the activeEvent if the group still exists.
398 * (It could be deleted as a side effect of processing the event.)
401 for (grpPtr
= groupList
; grpPtr
!= NULL
; grpPtr
= grpPtr
->nextPtr
) {
402 if (grpPtr
->groupId
== savedId
) {
403 groupPtr
->activeEvent
= savedActive
;
408 tkShareEventPtr
= savedShareEventPtr
;