/* * tkMacWindowMgr.c -- * * Implements common window manager functions for the Macintosh. * * Copyright (c) 1995-1996 Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * SCCS: @(#) tkMacWindowMgr.c 1.35 96/09/20 14:29:27 */ #include #include #include #include #include #include #include #include "tkInt.h" #include "tkPort.h" #include "tkMacInt.h" #define TK_DEFAULT_ABOUT 128 /* * Declarations of global variables defined in this file. */ int tkMacAppInFront = true; /* Boolean variable for determining * if we are the frontmost app. */ /* * Declarations of static variables used in this file. */ static int gEatButtonUp = 0; /* 1 if we need to eat the next up event */ static int gCaptured = 0; /* 1 if mouse events outside of Tk * windows will be reported, else 0 */ static Tk_Window gGrabWinPtr = NULL; /* Window that defines the top of the * grab tree in a global grab. */ static Tk_Window gKeyboardWinPtr = NULL; /* Current keyboard grab window. */ static Point gLastPointerPos; /* Last known position of mouse. */ static Tk_Window gLastWinPtr = NULL; /* The last window the mouse was in */ static Tk_Window gRestrictWinPtr = NULL; /* Window to which all mouse * events will be reported. */ static RgnHandle gDamageRgn = NULL; /* Damage region used for handling * screen updates. */ /* * Forward declarations of procedures used in this file. */ static void BringWindowForward _ANSI_ARGS_((WindowRef wRef)); static int CheckEventsAvail _ANSI_ARGS_((void)); static int GenerateActivateEvents _ANSI_ARGS_((EventRecord *macEvent)); static int GenerateKeyEvent _ANSI_ARGS_((EventRecord *macEvent)); static int GenerateUpdateEvent _ANSI_ARGS_((EventRecord *macEvent)); static int GenerateEnterLeave _ANSI_ARGS_((Tk_Window tkwin, long x, int y)); static int GenerateMotion _ANSI_ARGS_((Tk_Window tkwin, Point whereLocal, Point whereGlobal)); static void GenerateUpdates _ANSI_ARGS_((RgnHandle updateRgn, TkWindow *winPtr)); static int GeneratePollingEvents _ANSI_ARGS_((void)); static void InitializeCrossingEvent _ANSI_ARGS_((XEvent *eventPtr, TkWindow *winPtr, long x, long y)); static OSErr TellWindowDefProcToCalcRegions _ANSI_ARGS_((WindowRef wRef)); static void UpdateCursor _ANSI_ARGS_((TkWindow *winPtr, WindowRef whichwindow, Point whereLocal)); static int WindowManagerMouse _ANSI_ARGS_((EventRecord *theEvent)); /* *---------------------------------------------------------------------- * * WindowManagerMouse -- * * This function determines if a button event is a "Window Manager" * function or an event that should be passed to Tk's event * queue. * * Results: * Return true if event was placed on Tk's event queue. * * Side effects: * Depends on where the button event occurs. * *---------------------------------------------------------------------- */ static int WindowManagerMouse(eventPtr) EventRecord *eventPtr; { WindowRef whichWindow, frontWindow; Window window; Tk_Window tkwin; Point where, where2; int xOffset, yOffset; short windowPart; frontWindow = FrontWindow(); /* * The window manager only needs to know about mouse down events. */ if (eventPtr->what == mouseUp) { if (gEatButtonUp) { gEatButtonUp = false; return false; } return TkGenerateButtonEvent(eventPtr->where.h, eventPtr->where.v, TkMacButtonKeyState()); } windowPart = FindWindow(eventPtr->where, &whichWindow); switch (windowPart) { case inSysWindow: SystemClick(eventPtr, (GrafPort *) whichWindow); return false; case inDrag: if (whichWindow != frontWindow) { if (!(eventPtr->modifiers & cmdKey)) { BringWindowForward(whichWindow); } } SetPort((GrafPort *) whichWindow); where.h = where.v = 0; LocalToGlobal(&where); DragWindow(whichWindow, eventPtr->where, &qd.screenBits.bounds); where2.h = where2.v = 0; LocalToGlobal(&where2); if (EqualPt(where, where2)) { return false; } window = TkMacGetXWindow(whichWindow); tkwin = Tk_IdToWindow(tkDisplayList->display, window); TkMacWindowOffset(whichWindow, &xOffset, &yOffset); where2.h -= xOffset; where2.v -= yOffset; TkGenWMConfigureEvent(tkwin, where2.h, where2.v, -1, -1, TK_LOCATION_CHANGED); return true; case inGrow: case inContent: if (whichWindow != frontWindow ) { BringWindowForward(whichWindow); SetPort((GrafPort *) whichWindow); return false; } else { /* * Generally the content region is the domain of Tk * sub-windows. However, one exception is the grow * region. A button down in this area will be handled * by the window manager. Note: this means that Tk * may not get button down events in this area! */ if (TkMacGrowToplevel(whichWindow, eventPtr->where) == true) { return true; } else { return TkGenerateButtonEvent(eventPtr->where.h, eventPtr->where.v, TkMacButtonKeyState()); } } case inGoAway: if (TrackGoAway( whichWindow, eventPtr->where)) { Window window; Tk_Window tkwin; window = TkMacGetXWindow(whichWindow); tkwin = Tk_IdToWindow(tkDisplayList->display, window); if (tkwin == NULL) { return false; } TkGenWMDestroyEvent(tkwin); return true; } return false; case inMenuBar: { KeyMap theKeys; GetKeys(theKeys); TkMacHandleMenuSelect(MenuSelect(eventPtr->where), theKeys[1] & 4); return true; /* TODO: may not be on event on queue. */ } case inZoomIn: case inZoomOut: if (TkMacZoomToplevel(whichWindow, eventPtr->where, windowPart) == true) { return true; } else { return false; } default: return false; } } /* *---------------------------------------------------------------------- * * TkAboutDlg -- * * Displays the default Tk About box. This code uses Macintosh * resources to define the content of the About Box. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ void TkAboutDlg() { DialogPtr aboutDlog; short itemHit = -9; aboutDlog = GetNewDialog(128, NULL, (void*)(-1)); if (!aboutDlog) { return; } SelectWindow((WindowRef) aboutDlog); while (itemHit != 1) { ModalDialog( NULL, &itemHit); } DisposDialog(aboutDlog); aboutDlog = NULL; SelectWindow(FrontWindow()); return; } /* *---------------------------------------------------------------------- * * GenerateUpdateEvent -- * * Given a Macintosh update event this function generates all the * X update events needed by Tk. * * Results: * True if event(s) are generated - false otherwise. * * Side effects: * Additional events may be place on the Tk event queue. * *---------------------------------------------------------------------- */ static int GenerateUpdateEvent(macEvent) EventRecord *macEvent; /* Incoming Mac event */ { WindowRef macWindow = (WindowRef)macEvent->message; Window window; register TkWindow *winPtr; window = TkMacGetXWindow(macWindow); winPtr = (TkWindow *) Tk_IdToWindow(tkDisplayList->display, window); if (gDamageRgn == NULL) { gDamageRgn = NewRgn(); } /* * After the call to BeginUpdate the visable region (visRgn) of the * window is equal to the intersection of the real visable region and * the update region for this event. We use this region in all of our * calculations. */ if (winPtr != NULL) { BeginUpdate(macWindow); GenerateUpdates(macWindow->visRgn, winPtr); EndUpdate(macWindow); return true; } return false; } /* *---------------------------------------------------------------------- * * GenerateUpdates -- * * Given a Macintosh update region and a Tk window this function * geneates a X damage event for the window if it is within the * update region. The function will then recursivly have each * damaged window generate damage events for its child windows. * * Results: * None. * * Side effects: * Additional events may be place on the Tk event queue. * *---------------------------------------------------------------------- */ static void GenerateUpdates(updateRgn, winPtr) RgnHandle updateRgn; TkWindow *winPtr; { TkWindow *childPtr; XEvent event; Rect bounds; TkMacWinBounds(winPtr, &bounds); if (bounds.top > (*updateRgn)->rgnBBox.bottom || (*updateRgn)->rgnBBox.top > bounds.bottom || bounds.left > (*updateRgn)->rgnBBox.right || (*updateRgn)->rgnBBox.left > bounds.right || !RectInRgn(&bounds, updateRgn)) { return; } event.xany.serial = Tk_Display(winPtr)->request; event.xany.send_event = false; event.xany.window = Tk_WindowId(winPtr); event.xany.display = Tk_Display(winPtr); event.type = Expose; /* * Compute the bounding box of the area that the damage occured in. */ /* * CopyRgn(TkMacVisableClipRgn(winPtr), rgn); * TODO: this call doesn't work doing resizes!!! */ RectRgn(gDamageRgn, &bounds); SectRgn(gDamageRgn, updateRgn, gDamageRgn); OffsetRgn(gDamageRgn, -bounds.left, -bounds.top); event.xexpose.x = (**gDamageRgn).rgnBBox.left; event.xexpose.y = (**gDamageRgn).rgnBBox.top; event.xexpose.width = (**gDamageRgn).rgnBBox.right - (**gDamageRgn).rgnBBox.left; event.xexpose.height = (**gDamageRgn).rgnBBox.bottom - (**gDamageRgn).rgnBBox.top; event.xexpose.count = 0; Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); for (childPtr = winPtr->childList; childPtr != NULL; childPtr = childPtr->nextPtr) { if (!Tk_IsMapped(childPtr) || Tk_IsTopLevel(childPtr)) { continue; } GenerateUpdates(updateRgn, childPtr); } return; } /* *---------------------------------------------------------------------- * * TkGenerateButtonEvent -- * * Given a global x & y position and the button key status this * procedure generates the appropiate X button event. It also * handles the state changes needed to implement implicit grabs. * * Results: * True if event(s) are generated - false otherwise. * * Side effects: * Additional events may be place on the Tk event queue. * Grab state may also change. * *---------------------------------------------------------------------- */ int TkGenerateButtonEvent(x, y, state) int x; /* X location of mouse */ int y; /* Y location of mouse */ unsigned int state; /* Button Key state suitable for X event */ { WindowRef whichWin, frontWin; Point where; Tk_Window tkwin; Window window; XEvent event; Rect bounds; /* * ButtonDown events will always occur in the front * window. ButtonUp events, however, may occur anywhere * on the screen. ButtonUp events should only be sent * to Tk if in the front window or during an implicit grab. */ where.h = x; where.v = y; FindWindow(where, &whichWin); frontWin = FrontWindow(); if ((frontWin == NULL) || (frontWin != whichWin && gRestrictWinPtr == NULL)) { return false; } event.xany.send_event = False; event.xbutton.x_root = where.h; event.xbutton.y_root = where.v; event.xbutton.subwindow = None; event.xbutton.same_screen = true; GlobalToLocal(&where); window = TkMacGetXWindow(whichWin); tkwin = Tk_IdToWindow(tkDisplayList->display, window); if (tkwin == NULL) { tkwin = gRestrictWinPtr; } else { tkwin = Tk_TopCoordsToWindow(tkwin, where.h, where.v, &event.xbutton.x, &event.xbutton.y); } if (TkPositionInTree((TkWindow *) tkwin, (TkWindow *) gGrabWinPtr) != TK_GRAB_IN_TREE) { tkwin = gGrabWinPtr; } TkMacWinBounds((TkWindow *) tkwin, &bounds); event.xbutton.x = where.h - bounds.left; event.xbutton.y = where.v - bounds.top; event.xany.serial = Tk_Display(tkwin)->request; event.xany.display = Tk_Display(tkwin); event.xbutton.root = XRootWindow(Tk_Display(tkwin), 0); event.xbutton.window = Tk_WindowId(tkwin); event.xbutton.state = state; event.xbutton.button = Button1; /* * Button events will also start or end an implicit grab. Do * different things wether we generate up or down mouse events. */ if (state & Button1Mask) { event.xany.type = ButtonPress; /* * Set mouse capture and the restrict window if we are * currently unrestricted. */ if (!gRestrictWinPtr) { if (!gGrabWinPtr) { gRestrictWinPtr = tkwin; gCaptured = 1; } else { /* * Make sure the new restrict window is inside the * current grab tree. */ if (TkPositionInTree((TkWindow *) tkwin, (TkWindow *) gGrabWinPtr) > 0) { gRestrictWinPtr = tkwin; } else { gRestrictWinPtr = gGrabWinPtr; } gCaptured = 1; } } } else { event.xany.type = ButtonRelease; /* * The function TkMacButtonKeyState which is used to passed the * state into this function will have incorrect information * during a ButtonRelease event. It will report that no * button is depressed - which is true. However, during a * ButtonRelease event the state needs to include the button * that *was* depressed. There for we need to set the * Button1Mask here. */ event.xbutton.state |= Button1Mask; /* * Release the mouse capture when the last button is * released and we aren't in a global grab. */ if (gGrabWinPtr == NULL) { gCaptured = 0; } /* * If we are releasing a restrict window, then we need * to send the button event followed by mouse motion from * the restrict window the the current mouse position. */ if (gRestrictWinPtr) { if (Tk_WindowId(gRestrictWinPtr) != event.xbutton.window) { TkChangeEventWindow(&event, (TkWindow *) gRestrictWinPtr); } gLastWinPtr = gRestrictWinPtr; gRestrictWinPtr = NULL; } } Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); return true; } /* *---------------------------------------------------------------------- * * GenerateActivateEvents -- * * Generate Activate/Deactivate and FocusIn/FocusOut events from * a Macintosh Activate event. Note, the activate-on-foreground * bit must be set in the SIZE flags to ensure we get * Activate/Deactivate in addition to Susspend/Resume events. * * Results: * Returns true if events were generate. * * Side effects: * Queue events on Tk's event queue. * *---------------------------------------------------------------------- */ static int GenerateActivateEvents(macEvent) EventRecord *macEvent; /* Incoming Mac event */ { XEvent event; Tk_Window tkwin; Window window; window = TkMacGetXWindow((WindowRef) macEvent->message); tkwin = Tk_IdToWindow(tkDisplayList->display, window); if (tkwin == NULL) { return false; } /* * Generate Activate and Deactivate events. This event * is sent to every subwindow in a toplevel window. */ if (macEvent->modifiers & activeFlag) { event.xany.type = ActivateNotify; } else { event.xany.type = DeactivateNotify; } event.xany.serial = tkDisplayList->display->request; event.xany.send_event = False; event.xany.display = tkDisplayList->display; event.xany.window = window; TkQueueEventForAllChildren(tkwin, &event); /* * Generate FocusIn and FocusOut events. This event * is only sent to the toplevel window. */ if (macEvent->modifiers & activeFlag) { event.xany.type = FocusIn; } else { event.xany.type = FocusOut; } event.xany.serial = tkDisplayList->display->request; event.xany.send_event = False; event.xfocus.display = tkDisplayList->display; event.xfocus.window = window; event.xfocus.mode = NotifyNormal; event.xfocus.detail = NotifyDetailNone; Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); return true; } /* *---------------------------------------------------------------------- * * GenerateKeyEvent -- * * Given Macintosh keyUp, keyDown & autoKey events this function * generates the appropiate X key events. * * Results: * True if event(s) are generated - false otherwise. * * Side effects: * Additional events may be place on the Tk event queue. * *---------------------------------------------------------------------- */ static int GenerateKeyEvent(macEvent) EventRecord *macEvent; /* Incoming Mac event */ { Point where; Tk_Window tkwin; XEvent event; /* * Tk keeps track of the current focus window. If Tk doesn't * claim to have any focus - then we ignore the event. */ tkwin = (Tk_Window) tkDisplayList->focusWinPtr; if (tkwin == NULL) { return false; } event.xany.send_event = False; event.xkey.same_screen = true; event.xkey.subwindow = None; event.xkey.time = TkMacGenerateTime(); event.xkey.keycode = macEvent->message; where.v = macEvent->where.v; where.h = macEvent->where.h; event.xkey.x_root = where.h; event.xkey.y_root = where.v; GlobalToLocal(&where); Tk_TopCoordsToWindow(tkwin, where.h, where.v, &event.xkey.x, &event.xkey.y); event.xany.serial = Tk_Display(tkwin)->request; event.xkey.window = Tk_WindowId(tkwin); event.xkey.display = Tk_Display(tkwin); event.xkey.root = XRootWindow(Tk_Display(tkwin), 0); event.xkey.state = TkMacButtonKeyState(); if (macEvent->what == keyDown) { event.xany.type = KeyPress; Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); } else if (macEvent->what == keyUp) { event.xany.type = KeyRelease; Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); } else { /* * Autokey events send multiple XKey events. * * Note: the last KeyRelease will always be missed with * this scheme. However, most Tk scripts don't look for * KeyUp events so we should be OK. */ event.xany.type = KeyRelease; Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); event.xany.type = KeyPress; Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); } return true; } /* *---------------------------------------------------------------------- * * GeneratePollingEvents -- * * This function polls the mouse position and generates X Motion, * Enter & Leave events. The cursor is also updated at this * time. * * Results: * True if event(s) are generated - false otherwise. * * Side effects: * Additional events may be place on the Tk event queue. * The cursor may be changed. * *---------------------------------------------------------------------- */ static int GeneratePollingEvents() { Tk_Window tkwin, rootwin; Window window; WindowRef whichwindow, frontWin; Point whereLocal, whereGlobal; Boolean inContentRgn; short part; int local_x, local_y; int generatedEvents = false; /* * First we get the current mouse position and determine * what Tk window the mouse is over (if any). */ frontWin = FrontWindow(); if (frontWin == NULL) { return false; } SetPort((GrafPort *) frontWin); GetMouse(&whereLocal); whereGlobal = whereLocal; LocalToGlobal(&whereGlobal); part = FindWindow(whereGlobal, &whichwindow); inContentRgn = (part == inContent || part == inGrow); if ((frontWin != whichwindow) || !inContentRgn) { tkwin = NULL; } else { window = TkMacGetXWindow(whichwindow); rootwin = Tk_IdToWindow(tkDisplayList->display, window); if (rootwin == NULL) { tkwin = NULL; } else { tkwin = Tk_TopCoordsToWindow(rootwin, whereLocal.h, whereLocal.v, &local_x, &local_y); } } /* * Then check to see if the window the mouse is positioned * over has changed. Generate enter and leave events if it * has. We can also generate the cursor at this time. */ generatedEvents = GenerateEnterLeave(tkwin, whereGlobal.h, whereGlobal.v); UpdateCursor((TkWindow *) gLastWinPtr, whichwindow, whereLocal); /* * Now we generate motion events - assuming the mouse moved. */ if (EqualPt(gLastPointerPos, whereGlobal)) { return generatedEvents; } else { generatedEvents |= GenerateMotion(tkwin, whereLocal, whereGlobal); gLastPointerPos = whereGlobal; } return generatedEvents; } /* *---------------------------------------------------------------------- * * TkMacButtonKeyState -- * * Returns the current state of the button & modifier keys. This * value is usually used in the XEvent structure. * * Results: * A bitwise inclusive OR of a subset of the following: * Button1Mask, ShiftMask, LockMask, ControlMask, Mod?Mask, * Mod?Mask. * * Side effects: * None. * *---------------------------------------------------------------------- */ unsigned int TkMacButtonKeyState() { unsigned int state = 0; KeyMap theKeys; if (Button() & !gEatButtonUp) { state |= Button1Mask; } GetKeys(theKeys); if (theKeys[1] & 2) { state |= LockMask; } if (theKeys[1] & 1) { state |= ShiftMask; } if (theKeys[1] & 8) { state |= ControlMask; } if (theKeys[1] & 32768) { state |= Mod1Mask; /* command key */ } if (theKeys[1] & 4) { state |= Mod2Mask; /* option key */ } return state; } /* *---------------------------------------------------------------------- * * UpdateCursor -- * * This function updates the cursor based on the current window the * the cursor is over. It also generates the resize cursor for the * resize region in the lower right hand corner of the window. * * Results: * None. * * Side effects: * The cursor may change shape. * *---------------------------------------------------------------------- */ static void UpdateCursor(winPtr, whichwindow, whereLocal) TkWindow *winPtr; WindowRef whichwindow; Point whereLocal; { WindowRef frontWin; CursHandle cursor; if (tkMacAppInFront == false) { return; } /* * The cursor will change during an implicit grab only under * a few special cases - such as bindings. */ if (gRestrictWinPtr != NULL) { if (TkPositionInTree(winPtr, (TkWindow *) gRestrictWinPtr) == TK_GRAB_IN_TREE) { TkUpdateCursor(winPtr); } else { TkUpdateCursor((TkWindow *) gRestrictWinPtr); } return; } /* * The cursor should be the arrow if outside the active window. */ frontWin = FrontWindow(); if (frontWin != whichwindow) { TkUpdateCursor(NULL); return; } /* * One special case is the grow region. Because a Tk window may * not have allocated space for the grow region the grow region * floats above the rest of the Tk window. This is shown by * changing the cursor over the grow region. This is not needed * if the window is not resizable or a scrollbar is growing the * drag region for the window. */ if ((gGrabWinPtr == NULL) && TkMacResizable(winPtr) && (TkMacGetScrollbarGrowWindow(winPtr) == NULL)) { if (whereLocal.h > (whichwindow->portRect.right - 16) && whereLocal.v > (whichwindow->portRect.bottom - 16)) { cursor = (CursHandle) GetNamedResource('CURS', "\presize"); SetCursor(*cursor); return; } } /* * Set the cursor to the value set for the given window. If * the value is None - set to the arrow cursor */ TkUpdateCursor(winPtr); } /* *---------------------------------------------------------------------- * * GenerateMotion -- * * Given a Tk window and the current mouse position this * function will generate the appropiate X Motion events. * * Results: * True if event(s) are generated - false otherwise. * * Side effects: * Additional events may be place on the Tk event queue. * *---------------------------------------------------------------------- */ static int GenerateMotion(tkwin, whereLocal, whereGlobal) Tk_Window tkwin; /* current Tk window (or NULL) */ Point whereLocal; /* current mouse position local coords */ Point whereGlobal; /* current mouse position global coords */ { XEvent event; int local_x, local_y; Rect bounds; /* * Mouse moved events generated only when mouse is in the * content of the front window. We also need to take into * account any grabs that may be in effect. */ if (gRestrictWinPtr) { tkwin = gRestrictWinPtr; } else if (gGrabWinPtr && !tkwin) { tkwin = gGrabWinPtr; } if (tkwin == NULL) { return false; } TkMacWinBounds((TkWindow *) tkwin, &bounds); local_x = whereLocal.h - bounds.left; local_y = whereLocal.v - bounds.top; event.xany.type = MotionNotify; event.xany.serial = Tk_Display(tkwin)->request; event.xany.send_event = False; event.xany.display = Tk_Display(tkwin); event.xmotion.window = Tk_WindowId(tkwin); event.xmotion.root = XRootWindow(Tk_Display(tkwin), 0); event.xmotion.state = TkMacButtonKeyState(); event.xmotion.subwindow = None; event.xmotion.time = TkMacGenerateTime(); event.xmotion.x_root = whereGlobal.h; event.xmotion.y_root = whereGlobal.v; event.xmotion.x = local_x; event.xmotion.y = local_y; event.xmotion.same_screen = true; event.xmotion.is_hint = NotifyNormal; Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); return true; } /* *---------------------------------------------------------------------- * * InitializeCrossingEvent -- * * Initializes the common fields for enter/leave events. * * Results: * None. * * Side effects: * Fills in the specified event structure. * *---------------------------------------------------------------------- */ static void InitializeCrossingEvent(eventPtr, winPtr, x, y) XEvent* eventPtr; /* Event structure to initialize. */ TkWindow *winPtr; /* Window to make event relative to. */ long x, y; /* Root coords of event. */ { eventPtr->xcrossing.serial = Tk_Display(winPtr)->request; eventPtr->xcrossing.send_event = 0; eventPtr->xcrossing.display = winPtr->display; eventPtr->xcrossing.root = RootWindow(winPtr->display, winPtr->screenNum); eventPtr->xcrossing.time = TkMacGenerateTime(); eventPtr->xcrossing.x_root = x; eventPtr->xcrossing.y_root = y; eventPtr->xcrossing.state = TkMacButtonKeyState(); eventPtr->xcrossing.mode = NotifyNormal; eventPtr->xcrossing.focus = False; } /* *---------------------------------------------------------------------- * * GenerateEnterLeave -- * * Given a Tk window and the current mouse position this * function will generate the appropiate X Enter and Leave * events. * * Results: * True if event(s) are generated - false otherwise. * * Side effects: * Additional events may be place on the Tk event queue. * *---------------------------------------------------------------------- */ static int GenerateEnterLeave(tkwin, x, y) Tk_Window tkwin; /* current Tk window (or NULL) */ long x; /* current mouse position in */ long y; /* root coordinates */ { int crossed = 0; /* 1 if mouse crossed a window boundary */ if (tkwin != gLastWinPtr) { if (gRestrictWinPtr) { int newPos, oldPos; newPos = TkPositionInTree((TkWindow *) tkwin, (TkWindow *) gRestrictWinPtr); oldPos = TkPositionInTree((TkWindow *) gLastWinPtr, (TkWindow *) gRestrictWinPtr); /* * Check if the mouse crossed into or out of the restrict * window. If so, we need to generate an Enter or Leave event. */ if ((newPos != oldPos) && ((newPos == TK_GRAB_IN_TREE) || (oldPos == TK_GRAB_IN_TREE))) { XEvent event; InitializeCrossingEvent(&event, gRestrictWinPtr, x, y); if (newPos == TK_GRAB_IN_TREE) { event.type = EnterNotify; } else { event.type = LeaveNotify; } if ((oldPos == TK_GRAB_ANCESTOR) || (newPos == TK_GRAB_ANCESTOR)) { event.xcrossing.detail = NotifyAncestor; } else { event.xcrossing.detail = NotifyVirtual; } TkChangeEventWindow(&event, (TkWindow *) gRestrictWinPtr); Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); } } else { Tk_Window targetPtr; if ((gLastWinPtr == NULL) || (Tk_WindowId(gLastWinPtr) == None)) { targetPtr = tkwin; } else { targetPtr = gLastWinPtr; } if (targetPtr && (Tk_WindowId(targetPtr) != None)) { XEvent event; /* * Generate appropriate Enter/Leave events. */ InitializeCrossingEvent(&event, targetPtr, x, y); TkInOutEvents(&event, (TkWindow *) gLastWinPtr, (TkWindow *) tkwin, LeaveNotify, EnterNotify, TCL_QUEUE_TAIL); crossed = 1; } } gLastWinPtr = tkwin; } return crossed; } /* *---------------------------------------------------------------------- * * XGrabPointer -- * * Capture the mouse so event are reported outside of toplevels. * Note that this is a very limited implementation that only * supports GrabModeAsync and owner_events True. * * Results: * Always returns GrabSuccess. * * Side effects: * Turns on mouse capture, sets the global grab pointer, and * clears any window restrictions. * *---------------------------------------------------------------------- */ int XGrabPointer(display, grab_window, owner_events, event_mask, pointer_mode, keyboard_mode, confine_to, cursor, time) Display* display; Window grab_window; Bool owner_events; unsigned int event_mask; int pointer_mode; int keyboard_mode; Window confine_to; Cursor cursor; Time time; { gCaptured = 1; gGrabWinPtr = Tk_IdToWindow(display, grab_window); gRestrictWinPtr = NULL; if (TkPositionInTree((TkWindow *) gLastWinPtr, (TkWindow *) gGrabWinPtr) != TK_GRAB_IN_TREE) { TkUpdateCursor((TkWindow *) gGrabWinPtr); } return GrabSuccess; } /* *---------------------------------------------------------------------- * * XUngrabPointer -- * * Release the current grab. * * Results: * None. * * Side effects: * Releases the mouse capture. * *---------------------------------------------------------------------- */ void XUngrabPointer(display, time) Display* display; Time time; { gCaptured = 0; gGrabWinPtr = NULL; gRestrictWinPtr = NULL; TkUpdateCursor((TkWindow *) gLastWinPtr); } /* *---------------------------------------------------------------------- * * XGrabKeyboard -- * * Simulates a keyboard grab by setting the focus. * * Results: * Always returns GrabSuccess. * * Side effects: * Sets the keyboard focus to the specified window. * *---------------------------------------------------------------------- */ int XGrabKeyboard(display, grab_window, owner_events, pointer_mode, keyboard_mode, time) Display* display; Window grab_window; Bool owner_events; int pointer_mode; int keyboard_mode; Time time; { gKeyboardWinPtr = Tk_IdToWindow(display, grab_window); return GrabSuccess; } /* *---------------------------------------------------------------------- * * XUngrabKeyboard -- * * Releases the simulated keyboard grab. * * Results: * None. * * Side effects: * Sets the keyboard focus back to the value before the grab. * *---------------------------------------------------------------------- */ void XUngrabKeyboard(display, time) Display* display; Time time; { gKeyboardWinPtr = NULL; } /* *---------------------------------------------------------------------- * * XQueryPointer -- * * Check the current state of the mouse. This is not a complete * implementation of this function. It only computes the root * coordinates and the current mask. * * Results: * Sets root_x_return, root_y_return, and mask_return. Returns * true on success. * * Side effects: * None. * *---------------------------------------------------------------------- */ Bool XQueryPointer(display, w, root_return, child_return, root_x_return, root_y_return, win_x_return, win_y_return, mask_return) Display* display; Window w; Window* root_return; Window* child_return; int* root_x_return; int* root_y_return; int* win_x_return; int* win_y_return; unsigned int* mask_return; { Point where; GetMouse(&where); LocalToGlobal(&where); *root_x_return = where.h; *root_y_return = where.v; *mask_return = TkMacButtonKeyState(); return True; } /* *---------------------------------------------------------------------- * * TkMacGenerateTime -- * * Returns the total number of ticks from startup This function * is used to generate the time of generated X events. * * Results: * Returns the current time (ticks from startup). * * Side effects: * None. * *---------------------------------------------------------------------- */ Time TkMacGenerateTime() { return (Time) LMGetTicks(); } /* *---------------------------------------------------------------------- * * TkMacPointerDeadWindow -- * * Clean up pointer module state when a window is destroyed. * * Results: * None. * * Side effects: * May change the grab module settings. * *---------------------------------------------------------------------- */ void TkMacPointerDeadWindow(winPtr) TkWindow *winPtr; { if ((Tk_Window) winPtr == gLastWinPtr) { gLastWinPtr = NULL; } if ((Tk_Window) winPtr == gGrabWinPtr) { gGrabWinPtr = NULL; } if ((Tk_Window) winPtr == gRestrictWinPtr) { gRestrictWinPtr = NULL; } if (!(gRestrictWinPtr || gGrabWinPtr)) { gCaptured = 0; } } /* *---------------------------------------------------------------------- * * TkMacConvertEvent -- * * This function converts a Macintosh event into zero or more * Tcl events. * * Results: * Returns 1 if event added to Tcl queue, 0 otherwse. * * Side effects: * May add events to Tcl's event queue. * *---------------------------------------------------------------------- */ int TkMacConvertEvent(eventPtr) EventRecord *eventPtr; { int eventFound = false; switch (eventPtr->what) { case nullEvent: if (GeneratePollingEvents()) { eventFound = true; } break; case updateEvt: if (GenerateUpdateEvent(eventPtr)) { eventFound = true; } break; case mouseDown: case mouseUp: if (WindowManagerMouse(eventPtr)) { eventFound = true; } break; case autoKey: case keyDown: /* * Handle menu-key events here. If it is *not* * a menu key - just fall through to handle as a * normal key event. */ if ((eventPtr->modifiers & cmdKey) == cmdKey) { long menuResult = MenuKey(eventPtr->message & charCodeMask); if (HiWord(menuResult) != 0) { TkMacHandleMenuSelect(menuResult, false); break; } } case keyUp: eventFound |= GenerateKeyEvent(eventPtr); break; case activateEvt: eventFound |= GenerateActivateEvents(eventPtr); break; case kHighLevelEvent: TkMacDoHLEvent(eventPtr); /* TODO: should return true if events were placed on event queue. */ break; case osEvt: /* * Do clipboard conversion. */ switch ((eventPtr->message & osEvtMessageMask) >> 24) { case mouseMovedMessage: if (GeneratePollingEvents()) { eventFound = true; } break; case suspendResumeMessage: if (eventPtr->message & resumeFlag) { if (eventPtr->message & convertClipboardFlag) { TkResumeClipboard(); } } else { TkSuspendClipboard(); } tkMacAppInFront = (eventPtr->message & resumeFlag); break; } break; case diskEvt: /* * Disk insertion. */ if (HiWord(eventPtr->message) != noErr) { Point pt; DILoad(); pt.v = pt.h = 120; /* parameter ignored in sys 7 */ DIBadMount(pt, eventPtr->message); DIUnload(); } break; } return eventFound; } /* *---------------------------------------------------------------------- * * CheckEventsAvail -- * * Checks to see if events are available on the Macintosh queue. * This function looks for both queued events (eg. key & button) * and generated events (update). * * Results: * True is events exist, false otherwise. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int CheckEventsAvail() { QHdrPtr evPtr; WindowPeek macWinPtr; evPtr = GetEvQHdr(); if (evPtr->qHead != NULL) { return true; } macWinPtr = (WindowPeek) FrontWindow(); while (macWinPtr != NULL) { if (!EmptyRgn(macWinPtr->updateRgn)) { return true; } macWinPtr = macWinPtr->nextWindow; } return false; } /* *---------------------------------------------------------------------- * * TkMacWindowOffset -- * * Determines the x and y offset from the orgin of the toplevel * window dressing (the structure region, ie. title bar) and the * orgin of the content area. * * Results: * The x & y offset in pixels. * * Side effects: * None. * *---------------------------------------------------------------------- */ void TkMacWindowOffset(wRef, xOffset, yOffset) WindowRef wRef; int *xOffset; int *yOffset; { OSErr err = noErr; WindowPeek wPeek = (WindowPeek) wRef; RgnHandle strucRgn = wPeek->strucRgn; RgnHandle contRgn = wPeek->contRgn; Rect strucRect, contRect; if (!EmptyRgn(strucRgn) && !EmptyRgn(contRgn)) { strucRect = (**strucRgn).rgnBBox; contRect = (**contRgn).rgnBBox; } else { /* * The current window's regions are not up to date. * Probably because the window isn't visable. What we * will do is save the old regions, have the window calculate * what the regions should be, and then restore it self. */ strucRgn = NewRgn( ); contRgn = NewRgn( ); if (!strucRgn || !contRgn) { err = MemError( ); } else { CopyRgn(wPeek->strucRgn, strucRgn); CopyRgn(wPeek->contRgn, contRgn); if (!(err = TellWindowDefProcToCalcRegions(wRef))) { strucRect = (**(wPeek->strucRgn)).rgnBBox; contRect = (**(wPeek->contRgn)).rgnBBox; } CopyRgn(strucRgn, wPeek->strucRgn); CopyRgn(contRgn, wPeek->contRgn); } if (contRgn) { DisposeRgn(contRgn); } if (strucRgn) { DisposeRgn(strucRgn); } } if (!err) { *xOffset = contRect.left - strucRect.left; *yOffset = contRect.top - strucRect.top; } else { *xOffset = 0; *yOffset = 0; } return; } /* *---------------------------------------------------------------------- * * TellWindowDefProcToCalcRegions -- * * Force a Macintosh window to recalculate it's content and * structure regions. * * Results: * An OS error. * * Side effects: * The windows content and structure regions may be updated. * *---------------------------------------------------------------------- */ static OSErr TellWindowDefProcToCalcRegions(wRef) WindowRef wRef; { OSErr err = noErr; SInt8 hState; Handle wdef = ((WindowPeek) wRef)->windowDefProc; /* * Load and lock the window definition procedure for * the window. */ hState = HGetState(wdef); if (!(err = MemError())) { LoadResource(wdef); if (!(err = ResError())) { MoveHHi(wdef); err = MemError(); if (err == memLockedErr) { err = noErr; } else if (!err) { HLock(wdef); err = MemError(); } } } /* * Assuming there are no errors we now call the window definition * procedure to tell it to calculate the regions for the window. */ if (err == noErr) { (void) CallWindowDefProc((WindowDefProcPtr) *wdef, GetWVariant(wRef), wRef, wCalcRgns, 0); HSetState(wdef, hState); if (!err) { err = MemError(); } } return err; } /* *---------------------------------------------------------------------- * * BringWindowForward -- * * Bring this background window to the front. We also set state * so Tk thinks the button is currently up. * * Results: * None. * * Side effects: * The window is brought forward. * *---------------------------------------------------------------------- */ static void BringWindowForward(wRef) WindowRef wRef; { SelectWindow(wRef); gEatButtonUp = true; }