archie/tk4.2/mac/tkMacWindowMgr.c
2024-05-27 16:40:40 +02:00

1619 lines
40 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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 <Events.h>
#include <Dialogs.h>
#include <EPPC.h>
#include <Windows.h>
#include <ToolUtils.h>
#include <DiskInit.h>
#include <LowMem.h>
#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;
}