/* * tkMacWindowMgr.c -- * * Implements common window manager functions for the Macintosh. * * Copyright (c) 1995-1997 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.59 97/11/20 18:56:39 */ #include #include #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. */ /* * Non-standard event types that can be passed to HandleEvent. * These are defined and used by Netscape's plugin architecture. */ #define getFocusEvent (osEvt + 16) #define loseFocusEvent (osEvt + 17) #define adjustCursorEvent (osEvt + 18) /* * Declarations of static variables used in this file. */ static int gEatButtonUp = 0; /* 1 if we need to eat the next * up event */ static Tk_Window gGrabWinPtr = NULL; /* Current grab window, NULL if no grab. */ static Tk_Window gKeyboardWinPtr = NULL; /* Current keyboard grab window. */ 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 *eventPtr, Window window)); static int GenerateFocusEvent _ANSI_ARGS_((EventRecord *eventPtr, Window window)); static int GenerateKeyEvent _ANSI_ARGS_((EventRecord *eventPtr, Window window)); static int GenerateUpdateEvent _ANSI_ARGS_((EventRecord *eventPtr, Window window)); static void GenerateUpdates _ANSI_ARGS_((RgnHandle updateRgn, TkWindow *winPtr)); static int GeneratePollingEvents _ANSI_ARGS_((void)); static int GeneratePollingEvents2 _ANSI_ARGS_((Window window, int adjustCursor)); static OSErr TellWindowDefProcToCalcRegions _ANSI_ARGS_((WindowRef wRef)); static int WindowManagerMouse _ANSI_ARGS_((EventRecord *theEvent, Window window)); /* *---------------------------------------------------------------------- * * 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( EventRecord *eventPtr, /* Macintosh event record. */ Window window) /* Window pointer. */ { WindowRef whichWindow, frontWindow; Tk_Window tkwin; Point where, where2; int xOffset, yOffset; short windowPart; frontWindow = FrontWindow(); /* * The window manager only needs to know about mouse down events * and sometimes we need to "eat" the mouse up. Otherwise, we * just pass the event to Tk. */ if (eventPtr->what == mouseUp) { if (gEatButtonUp) { gEatButtonUp = false; return false; } return TkGenerateButtonEvent(eventPtr->where.h, eventPtr->where.v, window, TkMacButtonKeyState()); } windowPart = FindWindow(eventPtr->where, &whichWindow); tkwin = Tk_IdToWindow(tkDisplayList->display, window); switch (windowPart) { case inSysWindow: SystemClick(eventPtr, (GrafPort *) whichWindow); return false; case inDrag: if (whichWindow != frontWindow) { if (!(eventPtr->modifiers & cmdKey)) { if ((gGrabWinPtr != NULL) && (gGrabWinPtr != tkwin)) { SysBeep(1); return false; } } } /* * Call DragWindow to move the window around. It will * also eat the mouse up event. */ SetPort((GrafPort *) whichWindow); where.h = where.v = 0; LocalToGlobal(&where); DragWindow(whichWindow, eventPtr->where, &tcl_macQdPtr->screenBits.bounds); gEatButtonUp = false; where2.h = where2.v = 0; LocalToGlobal(&where2); if (EqualPt(where, where2)) { return false; } 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 ) { /* * This click moves the window forward. We don't want * the corasponding mouse-up to be reported to the application * or else it will mess up some Tk scripts. */ if ((gGrabWinPtr != NULL) && (gGrabWinPtr != tkwin)) { SysBeep(1); return false; } BringWindowForward(whichWindow); gEatButtonUp = true; 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, window, TkMacButtonKeyState()); } } case inGoAway: if (TrackGoAway( whichWindow, eventPtr->where)) { if (tkwin == NULL) { return false; } TkGenWMDestroyEvent(tkwin); return true; } return false; case inMenuBar: { int oldMode; KeyMap theKeys; GetKeys(theKeys); oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL); TkMacClearMenubarActive(); TkMacHandleMenuSelect(MenuSelect(eventPtr->where), theKeys[1] & 4); Tcl_SetServiceMode(oldMode); 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( EventRecord *eventPtr, /* Incoming Mac event */ Window window) /* Root X window for event. */ { WindowRef macWindow; register TkWindow *winPtr; winPtr = (TkWindow *) Tk_IdToWindow(tkDisplayList->display, window); if (winPtr == NULL) { return false; } 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 (eventPtr->message != NULL) { macWindow = (WindowRef) TkMacGetDrawablePort(window); BeginUpdate(macWindow); GenerateUpdates(macWindow->visRgn, winPtr); EndUpdate(macWindow); return true; } else { /* * This event didn't come from the system. This might * occur if we are running from inside of Netscape. * In this we shouldn't call BeginUpdate as the vis region * may be NULL. */ RgnHandle rgn; Rect bounds; rgn = NewRgn(); TkMacWinBounds(winPtr, &bounds); RectRgn(rgn, &bounds); GenerateUpdates(rgn, winPtr); DisposeRgn(rgn); return true; } } /* *---------------------------------------------------------------------- * * 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( 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); /* * Generate updates for the children of this window */ for (childPtr = winPtr->childList; childPtr != NULL; childPtr = childPtr->nextPtr) { if (!Tk_IsMapped(childPtr) || Tk_IsTopLevel(childPtr)) { continue; } GenerateUpdates(updateRgn, childPtr); } /* * Generate updates for any contained windows */ if (Tk_IsContainer(winPtr)) { childPtr = TkpGetOtherWindow(winPtr); if (childPtr != NULL && Tk_IsMapped(childPtr)) { GenerateUpdates(updateRgn, childPtr); } /* * NOTE: Here we should handle out of process embedding. */ } 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( int x, /* X location of mouse */ int y, /* Y location of mouse */ Window window, /* X Window containing button event. */ unsigned int state) /* Button Key state suitable for X event */ { WindowRef whichWin, frontWin; Point where; Tk_Window tkwin; int dummy; /* * 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 && gGrabWinPtr == NULL)) { return false; } tkwin = Tk_IdToWindow(tkDisplayList->display, window); GlobalToLocal(&where); if (tkwin != NULL) { tkwin = Tk_TopCoordsToWindow(tkwin, where.h, where.v, &dummy, &dummy); } Tk_UpdatePointer(tkwin, x, y, state); return true; } /* *---------------------------------------------------------------------- * * GenerateActivateEvents -- * * Generate Activate/Deactivate 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( EventRecord *eventPtr, /* Incoming Mac event */ Window window) /* Root X window for event. */ { TkWindow *winPtr; winPtr = (TkWindow *) Tk_IdToWindow(tkDisplayList->display, window); if (winPtr == NULL || winPtr->window == None) { return false; } TkGenerateActivateEvents(winPtr, (eventPtr->modifiers & activeFlag) ? 1 : 0); return true; } /* *---------------------------------------------------------------------- * * XSetInputFocus -- * * Change the focus window for the application. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ void XSetInputFocus( Display* display, Window focus, int revert_to, Time time) { /* * Don't need to do a thing. Tk manages the focus for us. */ } /* *---------------------------------------------------------------------- * * TkpChangeFocus -- * * This procedure is a stub on the Mac because we always own the * focus if we are a front most application. * * Results: * The return value is the serial number of the command that * changed the focus. It may be needed by the caller to filter * out focus change events that were queued before the command. * If the procedure doesn't actually change the focus then * it returns 0. * * Side effects: * None. * *---------------------------------------------------------------------- */ int TkpChangeFocus(winPtr, force) TkWindow *winPtr; /* Window that is to receive the X focus. */ int force; /* Non-zero means claim the focus even * if it didn't originally belong to * topLevelPtr's application. */ { /* * We don't really need to do anything on the Mac. Tk will * keep all this state for us. */ if (winPtr->atts.override_redirect) { return 0; } /* * Remember the current serial number for the X server and issue * a dummy server request. This marks the position at which we * changed the focus, so we can distinguish FocusIn and FocusOut * events on either side of the mark. */ return NextRequest(winPtr->display); } /* *---------------------------------------------------------------------- * * GenerateFocusEvent -- * * Generate 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 GenerateFocusEvent( EventRecord *eventPtr, /* Incoming Mac event */ Window window) /* Root X window for event. */ { XEvent event; Tk_Window tkwin; tkwin = Tk_IdToWindow(tkDisplayList->display, window); if (tkwin == NULL) { return false; } /* * Generate FocusIn and FocusOut events. This event * is only sent to the toplevel window. */ if (eventPtr->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. The window that is passed * should represent the frontmost window - which will recieve the * event. * * Results: * True if event(s) are generated - false otherwise. * * Side effects: * Additional events may be place on the Tk event queue. * *---------------------------------------------------------------------- */ static int GenerateKeyEvent( EventRecord *eventPtr, /* Incoming Mac event */ Window window) /* Root X window for event. */ { Point where; Tk_Window tkwin; XEvent event; /* * The focus must be in the FrontWindow on the Macintosh. * We then query Tk to determine the exact Tk window * that owns the focus. */ tkwin = Tk_IdToWindow(tkDisplayList->display, window); tkwin = (Tk_Window) ((TkWindow *) tkwin)->dispPtr->focusPtr; if (tkwin == NULL) { return false; } where.v = eventPtr->where.v; where.h = eventPtr->where.h; event.xany.send_event = False; event.xkey.same_screen = true; event.xkey.subwindow = None; event.xkey.time = TkpGetMS(); 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.xkey.keycode = eventPtr->message; 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 (eventPtr->what == keyDown) { event.xany.type = KeyPress; Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); } else if (eventPtr->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); } } /* * The following call will generate the appropiate X events and * adjust any state that Tk must remember. */ if ((tkwin == NULL) && (gGrabWinPtr != NULL)) { tkwin = gGrabWinPtr; } Tk_UpdatePointer(tkwin, whereGlobal.h, whereGlobal.v, TkMacButtonKeyState()); /* * Finally, we make sure the proper cursor is installed. The installation * is polled to 1) make our resize hack work, and 2) make sure we have the * proper cursor even if someone else changed the cursor out from under * us. */ if ((gGrabWinPtr == NULL) && (part == inGrow) && TkMacResizable((TkWindow *) tkwin) && (TkMacGetScrollbarGrowWindow((TkWindow *) tkwin) == NULL)) { TkMacInstallCursor(1); } else { TkMacInstallCursor(0); } return true; } /* *---------------------------------------------------------------------- * * GeneratePollingEvents2 -- * * This function polls the mouse position and generates X Motion, * Enter & Leave events. The cursor is also updated at this * time. NOTE: this version is for Netscape!!! * * 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 GeneratePollingEvents2( Window window, int adjustCursor) { Tk_Window tkwin, rootwin; WindowRef whichwindow, frontWin; Point whereLocal, whereGlobal; int local_x, local_y; int generatedEvents = false; Rect bounds; /* * 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); /* * Determine if we are in a Tk window or not. */ whichwindow = (WindowRef) TkMacGetDrawablePort(window); if (whichwindow != frontWin) { tkwin = NULL; } else { rootwin = Tk_IdToWindow(tkDisplayList->display, window); TkMacWinBounds((TkWindow *) rootwin, &bounds); if (!PtInRect(whereLocal, &bounds)) { tkwin = NULL; } else { tkwin = Tk_TopCoordsToWindow(rootwin, whereLocal.h, whereLocal.v, &local_x, &local_y); } } /* * The following call will generate the appropiate X events and * adjust any state that Tk must remember. */ if ((tkwin == NULL) && (gGrabWinPtr != NULL)) { tkwin = gGrabWinPtr; } Tk_UpdatePointer(tkwin, whereGlobal.h, whereGlobal.v, TkMacButtonKeyState()); /* * Finally, we make sure the proper cursor is installed. The installation * is polled to 1) make our resize hack work, and 2) make sure we have the * proper cursor even if someone else changed the cursor out from under * us. */ if (adjustCursor) { TkMacInstallCursor(0); } return true; } /* *---------------------------------------------------------------------- * * TkMacButtonKeyState -- * * Returns the current state of the button & modifier keys. * * 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; } /* *---------------------------------------------------------------------- * * 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* 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* 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* 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(); } /* *---------------------------------------------------------------------- * * 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( EventRecord *eventPtr) { WindowRef whichWindow; Window window; int eventFound = false; switch (eventPtr->what) { case nullEvent: case adjustCursorEvent: if (GeneratePollingEvents()) { eventFound = true; } break; case updateEvt: whichWindow = (WindowRef)eventPtr->message; window = TkMacGetXWindow(whichWindow); if (GenerateUpdateEvent(eventPtr, window)) { eventFound = true; } break; case mouseDown: case mouseUp: FindWindow(eventPtr->where, &whichWindow); window = TkMacGetXWindow(whichWindow); if (WindowManagerMouse(eventPtr, window)) { 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; int oldMode; oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL); menuResult = MenuKey(eventPtr->message & charCodeMask); Tcl_SetServiceMode(oldMode); if (HiWord(menuResult) != 0) { TkMacHandleMenuSelect(menuResult, false); break; } } case keyUp: whichWindow = FrontWindow(); window = TkMacGetXWindow(whichWindow); eventFound |= GenerateKeyEvent(eventPtr, window); break; case activateEvt: window = TkMacGetXWindow((WindowRef) eventPtr->message); eventFound |= GenerateActivateEvents(eventPtr, window); eventFound |= GenerateFocusEvent(eventPtr, window); break; case getFocusEvent: eventPtr->modifiers |= activeFlag; window = TkMacGetXWindow((WindowRef) eventPtr->message); eventFound |= GenerateFocusEvent(eventPtr, window); break; case loseFocusEvent: eventPtr->modifiers &= ~activeFlag; window = TkMacGetXWindow((WindowRef) eventPtr->message); eventFound |= GenerateFocusEvent(eventPtr, window); 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)) { 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; } /* *---------------------------------------------------------------------- * * TkMacConvertTkEvent -- * * This function converts a Macintosh event into zero or more * Tcl events. It is intended for use in Netscape-style embedding. * * Results: * Returns 1 if event added to Tcl queue, 0 otherwse. * * Side effects: * May add events to Tcl's event queue. * *---------------------------------------------------------------------- */ int TkMacConvertTkEvent( EventRecord *eventPtr, Window window) { int eventFound = false; Point where; /* * By default, assume it is legal for us to set the cursor */ Tk_MacTkOwnsCursor(1); switch (eventPtr->what) { case nullEvent: /* * We get NULL events only when the cursor is NOT over * the plugin. Otherwise we get updateCursor events. * We will not generate polling events or move the cursor * in this case. */ eventFound = false; break; case adjustCursorEvent: if (GeneratePollingEvents2(window, 1)) { eventFound = true; } break; case updateEvt: /* * It is possibly not legal for us to set the cursor */ Tk_MacTkOwnsCursor(0); if (GenerateUpdateEvent(eventPtr, window)) { eventFound = true; } break; case mouseDown: case mouseUp: GetMouse(&where); LocalToGlobal(&where); eventFound |= TkGenerateButtonEvent(where.h, where.v, window, TkMacButtonKeyState()); 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, window); break; case activateEvt: /* * It is probably not legal for us to set the cursor * here, since we don't know where the mouse is in the * window that is being activated. */ Tk_MacTkOwnsCursor(0); eventFound |= GenerateActivateEvents(eventPtr, window); eventFound |= GenerateFocusEvent(eventPtr, window); break; case getFocusEvent: eventPtr->modifiers |= activeFlag; eventFound |= GenerateFocusEvent(eventPtr, window); break; case loseFocusEvent: eventPtr->modifiers &= ~activeFlag; eventFound |= GenerateFocusEvent(eventPtr, window); 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) { /* * It is possibly not legal for us to set the cursor. * Netscape sends us these events all the time... */ Tk_MacTkOwnsCursor(0); case mouseMovedMessage: /* if (GeneratePollingEvents2(window, 0)) { eventFound = true; } NEXT LINE IS TEMPORARY */ eventFound = false; break; case suspendResumeMessage: if (!(eventPtr->message & resumeFlag)) { 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; } /* *---------------------------------------------------------------------- * * TkpSetCapture -- * * This function captures the mouse so that all future events * will be reported to this window, even if the mouse is outside * the window. If the specified window is NULL, then the mouse * is released. * * Results: * None. * * Side effects: * Sets the capture flag and captures the mouse. * *---------------------------------------------------------------------- */ void TkpSetCapture( TkWindow *winPtr) /* Capture window, or NULL. */ { while ((winPtr != NULL) && !Tk_IsTopLevel(winPtr)) { winPtr = winPtr->parentPtr; } gGrabWinPtr = (Tk_Window) winPtr; } /* *---------------------------------------------------------------------- * * 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( 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( 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((UniversalProcPtr) *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( WindowRef wRef) { SelectWindow(wRef); } /* *---------------------------------------------------------------------- * * TkpGetMS -- * * Return a relative time in milliseconds. It doesn't matter * when the epoch was. * * Results: * Number of milliseconds. * * Side effects: * None. * *---------------------------------------------------------------------- */ unsigned long TkpGetMS() { long long * int64Ptr; UnsignedWide micros; Microseconds(µs); int64Ptr = (long long *) µs; /* * We need 64 bit math to do this. This is available in CW 11 * and on. Other's will need to use a different scheme. */ *int64Ptr /= 1000; return (long) *int64Ptr; }