|
|
1.1 ! root 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: ! 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: ! 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: ! 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: ! 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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.