]>
cvs.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 *----------------------------------------------------------------------
97 Tk_Window tkwin
, /* Token for window. */
98 Tk_Uid groupId
/* Identifier for group among which
99 * events are to be shared. */
102 register Group
*groupPtr
;
105 * See if this group exists. If so, add the window to the group.
108 for (groupPtr
= groupList
; groupPtr
!= NULL
;
109 groupPtr
= groupPtr
->nextPtr
) {
112 if (groupPtr
->groupId
!= groupId
) {
115 new = (Tk_Window
*) ckalloc((unsigned)
116 (groupPtr
->numWindows
+1) * sizeof(Tk_Window
*));
117 memcpy((VOID
*) (new+1), (VOID
*) groupPtr
->windows
,
118 (groupPtr
->numWindows
* sizeof(Tk_Window
*)));
119 ckfree((char *) groupPtr
->windows
);
120 groupPtr
->windows
= new;
121 groupPtr
->windows
[0] = tkwin
;
122 groupPtr
->numWindows
++;
126 if (groupPtr
== NULL
) {
128 * Group doesn't exist. Make a new one.
131 groupPtr
= (Group
*) ckalloc(sizeof(Group
));
132 groupPtr
->groupId
= groupId
;
133 groupPtr
->windows
= (Tk_Window
*) ckalloc(sizeof (Tk_Window
*));
134 groupPtr
->windows
[0] = tkwin
;
135 groupPtr
->numWindows
= 1;
136 groupPtr
->lastWindow
= NULL
;
137 groupPtr
->activeEvent
= NULL
;
138 groupPtr
->nextPtr
= groupList
;
139 groupList
= groupPtr
;
143 * Create an event handler so we find out about relevant events
144 * that are directed to tkwin.
147 Tk_CreateEventHandler(tkwin
,
148 ButtonPressMask
|ButtonReleaseMask
|PointerMotionMask
,
149 ShareEventProc
, (ClientData
) groupPtr
);
153 *----------------------------------------------------------------------
155 * Tk_UnshareEvents --
157 * Remove tkwin from a group of windows sharing events.
163 * Tkwin will no longer participate in event-sharing for the
164 * given group, either as source of events or as destination.
166 *----------------------------------------------------------------------
171 Tk_Window tkwin
, /* Token for window. */
172 Tk_Uid groupId
/* Identifier for group. */
175 register Group
*groupPtr
;
178 for (groupPtr
= groupList
; groupPtr
!= NULL
;
179 groupPtr
= groupPtr
->nextPtr
) {
180 if (groupPtr
->groupId
!= groupId
) {
183 if (groupPtr
->lastWindow
== tkwin
) {
184 groupPtr
->lastWindow
= NULL
;
186 for (i
= 0; i
< groupPtr
->numWindows
; i
++) {
187 if (groupPtr
->windows
[i
] != tkwin
) {
190 if ((i
+1) < groupPtr
->numWindows
) {
191 memcpy((VOID
*) (groupPtr
->windows
+ i
),
192 (VOID
*) (groupPtr
->windows
+ i
+ 1),
193 (groupPtr
->numWindows
- (i
+1))*sizeof(Tk_Window
*));
195 groupPtr
->numWindows
--;
196 Tk_DeleteEventHandler(tkwin
,
197 ButtonPressMask
|ButtonReleaseMask
|PointerMotionMask
,
198 ShareEventProc
, (ClientData
) groupPtr
);
199 if (groupPtr
->numWindows
== 0) {
200 DeleteGroup(groupPtr
);
208 *----------------------------------------------------------------------
212 * This procedure is called when a group has no more members.
213 * It deletes the group from the list of existing groups.
221 *----------------------------------------------------------------------
226 Group
*groupPtr
/* Group to delete. */
229 if (groupList
== groupPtr
) {
230 groupList
= groupPtr
->nextPtr
;
232 register Group
*prevPtr
;
234 for (prevPtr
= groupList
; ; prevPtr
= prevPtr
->nextPtr
) {
235 if (prevPtr
== NULL
) {
236 panic("DeleteGroup couldn't find group on shareList");
238 if (prevPtr
->nextPtr
== groupPtr
) {
239 prevPtr
->nextPtr
= groupPtr
->nextPtr
;
244 ckfree((char *) groupPtr
->windows
);
245 ckfree((char *) groupPtr
);
249 *----------------------------------------------------------------------
253 * This procedure is invoked by the Tk dispatcher when an event
254 * occurs for which we need to implement sharing.
260 * If the mouse is actually in a window other than the one for
261 * which the event occurred, generate a new event translated to
264 *----------------------------------------------------------------------
269 ClientData clientData
, /* Information about share group. */
270 register XEvent
*eventPtr
/* Event that just occurred. */
273 register Group
*groupPtr
= (Group
*) clientData
;
274 register Tk_Window tkwin
;
276 XEvent newEvent
, *savedActive
, *savedShareEventPtr
;
279 register Group
*grpPtr
;
282 * If this event was a synthetic one that we generated, then
283 * don't bother to process it again.
286 if (groupPtr
->activeEvent
== eventPtr
) {
289 savedActive
= groupPtr
->activeEvent
;
290 groupPtr
->activeEvent
= &newEvent
;
291 savedId
= groupPtr
->groupId
;
293 savedShareEventPtr
= tkShareEventPtr
;
294 tkShareEventPtr
= &newEvent
;
297 * Scan through all of the windows for this group to find the
298 * first one (if any) that contains the event.
301 tkwin
= NULL
; /* Not needed, but stops compiler warning. */
302 for (i
= 0; i
< groupPtr
->numWindows
; i
++) {
305 tkwin
= groupPtr
->windows
[i
];
306 Tk_GetRootCoords(tkwin
, &x
, &y
);
307 x
= eventPtr
->xmotion
.x_root
- x
- Tk_Changes(tkwin
)->border_width
;
308 y
= eventPtr
->xmotion
.y_root
- y
- Tk_Changes(tkwin
)->border_width
;
309 if ((x
< 0) || (y
< 0) || (x
>= Tk_Width(tkwin
))
310 || (y
>= Tk_Height(tkwin
))) {
313 for (tkwin2
= tkwin
; ; tkwin2
= Tk_Parent(tkwin2
)) {
314 if (tkwin2
== NULL
) {
317 if (!Tk_IsMapped(tkwin2
)) {
320 if (((Tk_FakeWin
*) (tkwin2
))->flags
& TK_TOP_LEVEL
) {
327 window
= None
; /* Not really needed but stops compiler warning. */
328 if (i
>= groupPtr
->numWindows
) {
331 window
= Tk_WindowId(tkwin
);
335 * SPECIAL NOTE: it is possible that any or all of the information
336 * in groupPtr could be modified as part of the processing of the
337 * events that we generate and hand to Tk_HandleEvent below. For this
338 * to work smoothly, it is imperative that we extract any information
339 * we need from groupPtr (and from tkwin's, since they could be
340 * deleted) before the first call to Tk_HandleEvent below. The code
341 * below may potentially pass an X window identifier to Tk_HandleEvent
342 * after the window has been deleted, but as long as identifiers
343 * aren't recycled Tk_HandleEvent will simply discard the event if
348 * If the pointer is in a different window now than the last time
349 * we were invoked, send a LeaveNotify event to the old window and
350 * an EnterNotify event to the new window.
353 newEvent
= *eventPtr
;
354 newEvent
.xany
.send_event
= True
;
355 if (tkwin
!= groupPtr
->lastWindow
) {
356 newEvent
= *eventPtr
;
357 newEvent
.xany
.send_event
= True
;
358 newEvent
.xcrossing
.mode
= TK_NOTIFY_SHARE
;
359 newEvent
.xcrossing
.detail
= NotifyAncestor
;
360 newEvent
.xcrossing
.same_screen
= True
;
361 newEvent
.xcrossing
.state
= eventPtr
->xmotion
.state
;
362 if (groupPtr
->lastWindow
!= NULL
) {
363 newEvent
.xcrossing
.type
= LeaveNotify
;
364 newEvent
.xcrossing
.window
= Tk_WindowId(groupPtr
->lastWindow
);
365 Tk_GetRootCoords(groupPtr
->lastWindow
, &newEvent
.xcrossing
.x
,
366 &newEvent
.xcrossing
.y
);
367 newEvent
.xcrossing
.x
= eventPtr
->xmotion
.x_root
368 - newEvent
.xcrossing
.x
369 - Tk_Changes(groupPtr
->lastWindow
)->border_width
;
370 newEvent
.xcrossing
.y
= eventPtr
->xmotion
.y_root
371 - newEvent
.xcrossing
.y
372 - Tk_Changes(groupPtr
->lastWindow
)->border_width
;
373 Tk_HandleEvent(&newEvent
);
376 newEvent
.xcrossing
.type
= EnterNotify
;
377 newEvent
.xcrossing
.window
= window
;
378 newEvent
.xcrossing
.x
= x
;
379 newEvent
.xcrossing
.y
= y
;
380 Tk_HandleEvent(&newEvent
);
382 groupPtr
->lastWindow
= tkwin
;
386 * If the pointer is in the window to which the event was sent,
387 * then we needn't do any forwarding at all. Ditto if the pointer
388 * isn't in any window at all.
391 if ((tkwin
!= NULL
) && (Tk_WindowId(tkwin
) != eventPtr
->xmotion
.window
)) {
392 newEvent
= *eventPtr
;
393 newEvent
.xmotion
.send_event
= True
;
394 newEvent
.xmotion
.window
= window
;
395 newEvent
.xmotion
.x
= x
;
396 newEvent
.xmotion
.y
= y
;
397 Tk_HandleEvent(&newEvent
);
401 * Only restore the activeEvent if the group still exists.
402 * (It could be deleted as a side effect of processing the event.)
405 for (grpPtr
= groupList
; grpPtr
!= NULL
; grpPtr
= grpPtr
->nextPtr
) {
406 if (grpPtr
->groupId
== savedId
) {
407 groupPtr
->activeEvent
= savedActive
;
412 tkShareEventPtr
= savedShareEventPtr
;