1/* 2 * tkMacOSXMouseEvent.c -- 3 * 4 * This file implements functions that decode & handle mouse events 5 * on MacOS X. 6 * 7 * Copyright 2001, Apple Computer, Inc. 8 * Copyright (c) 2005-2007 Daniel A. Steffen <das@users.sourceforge.net> 9 * 10 * See the file "license.terms" for information on usage and redistribution of 11 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 12 * 13 * The following terms apply to all files originating from Apple 14 * Computer, Inc. ("Apple") and associated with the software 15 * unless explicitly disclaimed in individual files. 16 * 17 * 18 * Apple hereby grants permission to use, copy, modify, 19 * distribute, and license this software and its documentation 20 * for any purpose, provided that existing copyright notices are 21 * retained in all copies and that this notice is included 22 * verbatim in any distributions. No written agreement, license, 23 * or royalty fee is required for any of the authorized 24 * uses. Modifications to this software may be copyrighted by 25 * their authors and need not follow the licensing terms 26 * described here, provided that the new terms are clearly 27 * indicated on the first page of each file where they apply. 28 * 29 * 30 * IN NO EVENT SHALL APPLE, THE AUTHORS OR DISTRIBUTORS OF THE 31 * SOFTWARE BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, 32 * INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF 33 * THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, 34 * EVEN IF APPLE OR THE AUTHORS HAVE BEEN ADVISED OF THE 35 * POSSIBILITY OF SUCH DAMAGE. APPLE, THE AUTHORS AND 36 * DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING, 37 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, 38 * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS 39 * SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND APPLE,THE 40 * AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE 41 * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 42 * 43 * GOVERNMENT USE: If you are acquiring this software on behalf 44 * of the U.S. government, the Government shall have only 45 * "Restricted Rights" in the software and related documentation 46 * as defined in the Federal Acquisition Regulations (FARs) in 47 * Clause 52.227.19 (c) (2). If you are acquiring the software 48 * on behalf of the Department of Defense, the software shall be 49 * classified as "Commercial Computer Software" and the 50 * Government shall have only "Restricted Rights" as defined in 51 * Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the 52 * foregoing, the authors grant the U.S. Government and others 53 * acting in its behalf permission to use and distribute the 54 * software in accordance with the terms specified in this 55 * license. 56 * 57 * RCS: @(#) $Id: tkMacOSXMouseEvent.c,v 1.6.2.23 2007/12/18 18:21:30 das Exp $ 58 */ 59 60#include "tkMacOSXPrivate.h" 61#include "tkMacOSXWm.h" 62#include "tkMacOSXEvent.h" 63#include "tkMacOSXDebug.h" 64 65typedef struct { 66 WindowRef whichWin; 67 WindowRef activeNonFloating; 68 WindowPartCode windowPart; 69 unsigned int state; 70 long delta; 71 Window window; 72 Point global; 73 Point local; 74} MouseEventData; 75 76/* 77 * Declarations of static variables used in this file. 78 */ 79 80static int gEatButtonUp = 0; /* 1 if we need to eat the next up event */ 81 82/* 83 * Declarations of functions used only in this file. 84 */ 85 86static void BringWindowForward(WindowRef wRef, int isFrontProcess, 87 int frontWindowOnly); 88static int GeneratePollingEvents(MouseEventData * medPtr); 89static int GenerateMouseWheelEvent(MouseEventData * medPtr); 90static int GenerateButtonEvent(MouseEventData * medPtr); 91static int GenerateToolbarButtonEvent(MouseEventData * medPtr); 92static int HandleWindowTitlebarMouseDown(MouseEventData * medPtr, Tk_Window tkwin); 93static unsigned int ButtonModifiers2State(UInt32 buttonState, UInt32 keyModifiers); 94static Tk_Window GetGrabWindowForWindow(Tk_Window tkwin); 95 96static int TkMacOSXGetEatButtonUp(void); 97static void TkMacOSXSetEatButtonUp(int f); 98 99 100/* 101 *---------------------------------------------------------------------- 102 * 103 * TkMacOSXProcessMouseEvent -- 104 * 105 * This routine processes the event in eventPtr, and 106 * generates the appropriate Tk events from it. 107 * 108 * Results: 109 * True if event(s) are generated - false otherwise. 110 * 111 * Side effects: 112 * Additional events may be place on the Tk event queue. 113 * 114 *---------------------------------------------------------------------- 115 */ 116 117MODULE_SCOPE int 118TkMacOSXProcessMouseEvent(TkMacOSXEvent *eventPtr, MacEventStatus * statusPtr) 119{ 120 Tk_Window tkwin; 121 Point where, where2; 122 int result; 123 TkDisplay * dispPtr; 124 OSStatus err; 125 MouseEventData mouseEventData, * medPtr = &mouseEventData; 126 int isFrontProcess; 127 128 switch (eventPtr->eKind) { 129 case kEventMouseDown: 130 case kEventMouseUp: 131 case kEventMouseMoved: 132 case kEventMouseDragged: 133 case kEventMouseWheelMoved: 134 break; 135 default: 136 return false; 137 break; 138 } 139 err = ChkErr(GetEventParameter, eventPtr->eventRef, 140 kEventParamMouseLocation, 141 typeQDPoint, NULL, 142 sizeof(where), NULL, 143 &where); 144 if (err != noErr) { 145 GetGlobalMouse(&where); 146 } 147 err = ChkErr(GetEventParameter, eventPtr->eventRef, 148 kEventParamWindowRef, 149 typeWindowRef, NULL, 150 sizeof(WindowRef), NULL, 151 &medPtr->whichWin); 152 if (err == noErr) { 153 err = ChkErr(GetEventParameter, eventPtr->eventRef, 154 kEventParamWindowPartCode, 155 typeWindowPartCode, NULL, 156 sizeof(WindowPartCode), NULL, 157 &medPtr->windowPart); 158 } 159 if (err != noErr) { 160 medPtr->windowPart = FindWindow(where, &medPtr->whichWin); 161 } 162 medPtr->window = TkMacOSXGetXWindow(medPtr->whichWin); 163 if (medPtr->whichWin != NULL && medPtr->window == None) { 164 return false; 165 } 166 if (eventPtr->eKind == kEventMouseDown) { 167 if (IsWindowActive(medPtr->whichWin) && IsWindowPathSelectEvent( 168 medPtr->whichWin, eventPtr->eventRef)) { 169 ChkErr(WindowPathSelect, medPtr->whichWin, NULL, NULL); 170 return false; 171 } 172 if (medPtr->windowPart == inProxyIcon) { 173 TkMacOSXTrackingLoop(1); 174 err = ChkErr(TrackWindowProxyDrag, medPtr->whichWin, where); 175 TkMacOSXTrackingLoop(0); 176 if (err == errUserWantsToDragWindow) { 177 medPtr->windowPart = inDrag; 178 } else { 179 return false; 180 } 181 } 182 } 183 isFrontProcess = Tk_MacOSXIsAppInFront(); 184 if (isFrontProcess) { 185 medPtr->state = ButtonModifiers2State(GetCurrentEventButtonState(), 186 GetCurrentEventKeyModifiers()); 187 } else { 188 medPtr->state = ButtonModifiers2State(GetCurrentButtonState(), 189 GetCurrentKeyModifiers()); 190 } 191 medPtr->global = where; 192 err = ChkErr(GetEventParameter, eventPtr->eventRef, 193 kEventParamWindowMouseLocation, 194 typeQDPoint, NULL, 195 sizeof(Point), NULL, 196 &medPtr->local); 197 if (err == noErr) { 198 if (medPtr->whichWin) { 199 Rect widths; 200 GetWindowStructureWidths(medPtr->whichWin, &widths); 201 medPtr->local.h -= widths.left; 202 medPtr->local.v -= widths.top; 203 } 204 } else { 205 medPtr->local = where; 206 if (medPtr->whichWin) { 207 QDGlobalToLocalPoint(GetWindowPort(medPtr->whichWin), 208 &medPtr->local); 209 } 210 } 211 medPtr->activeNonFloating = ActiveNonFloatingWindow(); 212 dispPtr = TkGetDisplayList(); 213 tkwin = Tk_IdToWindow(dispPtr->display, medPtr->window); 214 215 if (eventPtr->eKind != kEventMouseDown) { 216 int res = false; 217 218 switch (eventPtr->eKind) { 219 case kEventMouseUp: 220 /* 221 * The window manager only needs to know about mouse down 222 * events and sometimes we need to "eat" the mouse up. 223 * Otherwise, we just pass the event to Tk. 224 */ 225 if (TkMacOSXGetEatButtonUp()) { 226 TkMacOSXSetEatButtonUp(false); 227 } else { 228 res = GenerateButtonEvent(medPtr); 229 } 230 break; 231 case kEventMouseWheelMoved: 232 err = ChkErr(GetEventParameter, eventPtr->eventRef, 233 kEventParamMouseWheelDelta, typeLongInteger, NULL, 234 sizeof(long), NULL, &medPtr->delta); 235 if (err != noErr ) { 236 statusPtr->err = 1; 237 } else { 238 EventMouseWheelAxis axis; 239 err = ChkErr(GetEventParameter, eventPtr->eventRef, 240 kEventParamMouseWheelAxis, typeMouseWheelAxis, 241 NULL, sizeof(EventMouseWheelAxis), NULL, &axis); 242 if (err == noErr && axis == kEventMouseWheelAxisX) { 243 medPtr->state |= ShiftMask; 244 } 245 res = GenerateMouseWheelEvent(medPtr); 246 } 247 break; 248 case kEventMouseMoved: 249 case kEventMouseDragged: 250 res = GeneratePollingEvents(medPtr); 251 break; 252 default: 253 Tcl_Panic("Unknown mouse event !"); 254 } 255 if (res) { 256 statusPtr->stopProcessing = 1; 257 } 258 return res; 259 } 260 TkMacOSXSetEatButtonUp(false); 261 if (medPtr->whichWin) { 262 /* 263 * We got a mouse down in a window 264 * See if this is the activate click 265 * This click moves the window forward. We don't want 266 * the corresponding mouse-up to be reported to the application 267 * or else it will mess up some Tk scripts. 268 */ 269 270 if (!(TkpIsWindowFloating(medPtr->whichWin)) 271 && (medPtr->whichWin != medPtr->activeNonFloating 272 || !isFrontProcess)) { 273 int frontWindowOnly = 1; 274 int cmdDragGrow = ((medPtr->windowPart == inDrag || 275 medPtr->windowPart == inGrow) && medPtr->state & Mod1Mask); 276 277 if (!cmdDragGrow) { 278 Tk_Window grabWin = GetGrabWindowForWindow(tkwin); 279 280 frontWindowOnly = !grabWin; 281 if (grabWin && grabWin != tkwin) { 282 TkMacOSXSetEatButtonUp(true); 283 BringWindowForward(TkMacOSXDrawableWindow( 284 ((TkWindow*)grabWin)->window), isFrontProcess, 285 frontWindowOnly); 286 return false; 287 } 288 } 289 290 /* 291 * Clicks in the titlebar widgets are handled without bringing the 292 * window forward. 293 */ 294 if ((result = HandleWindowTitlebarMouseDown(medPtr, tkwin)) != -1) { 295 statusPtr->stopProcessing = 1; 296 return result; 297 } else { 298 /* 299 * Only windows with the kWindowNoActivatesAttribute can 300 * receive mouse events in the background. 301 */ 302 if (!(((TkWindow *)tkwin)->wmInfoPtr->attributes & 303 kWindowNoActivatesAttribute)) { 304 /* 305 * Allow background window dragging & growing with Command 306 * down. 307 */ 308 if (!cmdDragGrow) { 309 TkMacOSXSetEatButtonUp(true); 310 BringWindowForward(medPtr->whichWin, isFrontProcess, 311 frontWindowOnly); 312 } 313 /* 314 * Allow dragging & growing of windows that were/are in the 315 * background. 316 */ 317 if (!(medPtr->windowPart == inDrag || 318 medPtr->windowPart == inGrow)) { 319 return false; 320 } 321 } 322 } 323 } else { 324 if ((result = HandleWindowTitlebarMouseDown(medPtr, tkwin)) != -1) { 325 statusPtr->stopProcessing = 1; 326 return result; 327 } 328 } 329 switch (medPtr->windowPart) { 330 case inDrag: { 331 WindowAttributes attributes; 332 333 GetWindowAttributes(medPtr->whichWin, &attributes); 334 if (!(attributes & kWindowAsyncDragAttribute)) { 335 TkMacOSXTrackingLoop(1); 336 DragWindow(medPtr->whichWin, where, NULL); 337 TkMacOSXTrackingLoop(0); 338 where2.h = where2.v = 0; 339 QDLocalToGlobalPoint(GetWindowPort(medPtr->whichWin), 340 &where2); 341 if (EqualPt(where, where2)) { 342 return false; 343 } 344 return true; 345 } 346 break; 347 } 348 case inGrow: 349 /* 350 * Generally the content region is the domain of Tk 351 * sub-windows. However, one exception is the grow 352 * region. A button down in this area will be handled 353 * by the window manager. Note: this means that Tk 354 * may not get button down events in this area! 355 */ 356 if (TkMacOSXGrowToplevel(medPtr->whichWin, where) == true) { 357 statusPtr->stopProcessing = 1; 358 return true; 359 } else { 360 return GenerateButtonEvent(medPtr); 361 } 362 break; 363 case inContent: 364 return GenerateButtonEvent(medPtr); 365 break; 366 default: 367 return false; 368 break; 369 } 370 } 371 return false; 372} 373 374/* 375 *---------------------------------------------------------------------- 376 * 377 * HandleWindowTitlebarMouseDown -- 378 * 379 * Handle clicks in window titlebar. 380 * 381 * Results: 382 * 1 if event was handled, 0 if event was not handled, 383 * -1 if MouseDown was not in window titlebar. 384 * 385 * Side effects: 386 * Additional events may be place on the Tk event queue. 387 * 388 *---------------------------------------------------------------------- 389 */ 390 391int 392HandleWindowTitlebarMouseDown(MouseEventData * medPtr, Tk_Window tkwin) 393{ 394 int result = INT_MAX; 395 396 switch (medPtr->windowPart) { 397 case inGoAway: 398 case inCollapseBox: 399 case inZoomIn: 400 case inZoomOut: 401 case inToolbarButton: 402 if (!IsWindowActive(medPtr->whichWin)) { 403 WindowRef frontWindow = FrontNonFloatingWindow(); 404 WindowModality frontWindowModality = kWindowModalityNone; 405 406 if (frontWindow && frontWindow != medPtr->whichWin) { 407 ChkErr(GetWindowModality, frontWindow, 408 &frontWindowModality, NULL); 409 } 410 if (frontWindowModality == kWindowModalityAppModal) { 411 result = 0; 412 } 413 } 414 break; 415 default: 416 result = -1; 417 break; 418 } 419 420 if (result == INT_MAX) { 421 result = 0; 422 TkMacOSXTrackingLoop(1); 423 switch (medPtr->windowPart) { 424 case inGoAway: 425 if (TrackGoAway(medPtr->whichWin, medPtr->global) && tkwin) { 426 TkGenWMDestroyEvent(tkwin); 427 result = 1; 428 } 429 break; 430 case inCollapseBox: 431 if (TrackBox(medPtr->whichWin, medPtr->global, 432 medPtr->windowPart) && tkwin) { 433 TkpWmSetState((TkWindow *)tkwin, IconicState); 434 result = 1; 435 } 436 break; 437 case inZoomIn: 438 case inZoomOut: 439 if (TrackBox(medPtr->whichWin, medPtr->global, 440 medPtr->windowPart)) { 441 result = TkMacOSXZoomToplevel(medPtr->whichWin, 442 medPtr->windowPart); 443 } 444 break; 445 case inToolbarButton: 446 if (TrackBox(medPtr->whichWin, medPtr->global, 447 medPtr->windowPart)) { 448 result = GenerateToolbarButtonEvent(medPtr); 449 } 450 break; 451 } 452 TkMacOSXTrackingLoop(0); 453 } 454 455 return result; 456} 457 458/* 459 *---------------------------------------------------------------------- 460 * 461 * GeneratePollingEvents -- 462 * 463 * This function polls the mouse position and generates X Motion, 464 * Enter & Leave events. The cursor is also updated at this 465 * time. 466 * 467 * Results: 468 * True if event(s) are generated - false otherwise. 469 * 470 * Side effects: 471 * Additional events may be place on the Tk event queue. 472 * The cursor may be changed. 473 * 474 *---------------------------------------------------------------------- 475 */ 476 477static int 478GeneratePollingEvents(MouseEventData * medPtr) 479{ 480 Tk_Window tkwin, rootwin, grabWin; 481 int local_x, local_y; 482 TkDisplay *dispPtr; 483 484 485 grabWin = TkMacOSXGetCapture(); 486 487 if ((!TkpIsWindowFloating(medPtr->whichWin) 488 && (medPtr->activeNonFloating != medPtr->whichWin))) { 489 /* 490 * If the window for this event is not floating, and is not the 491 * active non-floating window, don't generate polling events. 492 * We don't send events to backgrounded windows. So either send 493 * it to the grabWin, or NULL if there is no grabWin. 494 */ 495 496 tkwin = grabWin; 497 } else { 498 /* 499 * First check whether the toplevel containing this mouse 500 * event is the grab window. If not, then send the event 501 * to the grab window. Otherwise, set tkWin to the subwindow 502 * which most closely contains the mouse event. 503 */ 504 505 dispPtr = TkGetDisplayList(); 506 rootwin = Tk_IdToWindow(dispPtr->display, medPtr->window); 507 if ((rootwin == NULL) 508 || ((grabWin != NULL) && (rootwin != grabWin))) { 509 tkwin = grabWin; 510 } else { 511 tkwin = Tk_TopCoordsToWindow(rootwin, 512 medPtr->local.h, medPtr->local.v, 513 &local_x, &local_y); 514 } 515 } 516 517 /* 518 * The following call will generate the appropiate X events and 519 * adjust any state that Tk must remember. 520 */ 521 522 Tk_UpdatePointer(tkwin, medPtr->global.h, medPtr->global.v, 523 medPtr->state); 524 525 return true; 526} 527 528/* 529 *---------------------------------------------------------------------- 530 * 531 * BringWindowForward -- 532 * 533 * Bring this background window to the front. 534 * 535 * Results: 536 * None. 537 * 538 * Side effects: 539 * The window is brought forward. 540 * 541 *---------------------------------------------------------------------- 542 */ 543 544static void 545BringWindowForward( 546 WindowRef wRef, 547 int isFrontProcess, 548 int frontWindowOnly) 549{ 550 if (wRef && !TkpIsWindowFloating(wRef) && IsValidWindowPtr(wRef)) { 551 WindowRef frontWindow = FrontNonFloatingWindow(); 552 WindowModality frontWindowModality = kWindowModalityNone; 553 554 if (frontWindow && frontWindow != wRef) { 555 ChkErr(GetWindowModality, frontWindow, &frontWindowModality, NULL); 556 } 557 if (frontWindowModality != kWindowModalityAppModal) { 558 Window window = TkMacOSXGetXWindow(wRef); 559 560 if (window != None) { 561 TkDisplay *dispPtr = TkGetDisplayList(); 562 TkWindow * winPtr = (TkWindow *)Tk_IdToWindow(dispPtr->display, 563 window); 564 565 if (winPtr && winPtr->wmInfoPtr && 566 winPtr->wmInfoPtr->master != None) { 567 TkWindow *masterWinPtr = (TkWindow *)Tk_IdToWindow( 568 dispPtr->display, winPtr->wmInfoPtr->master); 569 570 if (masterWinPtr && masterWinPtr->window != None && 571 TkMacOSXHostToplevelExists(masterWinPtr)) { 572 WindowRef masterMacWin = 573 TkMacOSXDrawableWindow(masterWinPtr->window); 574 575 if (masterMacWin) { 576 BringToFront(masterMacWin); 577 } 578 } 579 } 580 } 581 SelectWindow(wRef); 582 } else { 583 frontWindowOnly = 0; 584 } 585 } 586 if (!isFrontProcess) { 587 ProcessSerialNumber ourPsn = {0, kCurrentProcess}; 588 589 ChkErr(SetFrontProcessWithOptions, &ourPsn, frontWindowOnly ? 590 kSetFrontProcessFrontWindowOnly : 0); 591 } 592} 593 594/* 595 *---------------------------------------------------------------------- 596 * 597 * TkMacOSXBringWindowForward -- 598 * 599 * Bring this window to the front in response to a mouse click. If 600 * a grab is in effect, bring the grab window to the front instead. 601 * 602 * Results: 603 * None. 604 * 605 * Side effects: 606 * The window is brought forward. 607 * 608 *---------------------------------------------------------------------- 609 */ 610 611void 612TkMacOSXBringWindowForward( 613 WindowRef wRef) 614{ 615 TkDisplay *dispPtr = TkGetDisplayList(); 616 Tk_Window tkwin = Tk_IdToWindow(dispPtr->display,TkMacOSXGetXWindow(wRef)); 617 Tk_Window grabWin = GetGrabWindowForWindow(tkwin); 618 619 if (grabWin && grabWin != tkwin) { 620 wRef = TkMacOSXDrawableWindow(((TkWindow*)grabWin)->window); 621 } 622 TkMacOSXSetEatButtonUp(true); 623 BringWindowForward(wRef, Tk_MacOSXIsAppInFront(), !grabWin); 624} 625 626/* 627 *---------------------------------------------------------------------- 628 * 629 * GetGrabWindowForWindow -- 630 * 631 * Get the grab window for the given window, if any. 632 * 633 * Results: 634 * Grab Tk_Window or None. 635 * 636 * Side effects: 637 * None. 638 * 639 *---------------------------------------------------------------------- 640 */ 641 642Tk_Window 643GetGrabWindowForWindow( 644 Tk_Window tkwin) 645{ 646 Tk_Window grabWin = TkMacOSXGetCapture(); 647 648 if (!grabWin) { 649 int grabState = TkGrabState((TkWindow*)tkwin); 650 651 if (grabState != TK_GRAB_NONE && grabState != TK_GRAB_IN_TREE) { 652 grabWin = (Tk_Window) (((TkWindow*)tkwin)->dispPtr->grabWinPtr); 653 } 654 } 655 656 return grabWin; 657} 658 659/* 660 *---------------------------------------------------------------------- 661 * 662 * GenerateMouseWheelEvent -- 663 * 664 * Generates a "MouseWheel" Tk event. 665 * 666 * Results: 667 * None. 668 * 669 * Side effects: 670 * Places a mousewheel event on the event queue. 671 * 672 *---------------------------------------------------------------------- 673 */ 674 675static int 676GenerateMouseWheelEvent(MouseEventData * medPtr) 677{ 678 Tk_Window tkwin, rootwin; 679 TkDisplay *dispPtr; 680 TkWindow *winPtr; 681 XEvent xEvent; 682 683 dispPtr = TkGetDisplayList(); 684 rootwin = Tk_IdToWindow(dispPtr->display, medPtr->window); 685 if (rootwin == NULL) { 686 tkwin = NULL; 687 } else { 688 tkwin = Tk_TopCoordsToWindow(rootwin, 689 medPtr->local.h, medPtr->local.v, 690 &xEvent.xbutton.x, &xEvent.xbutton.y); 691 } 692 693 /* 694 * The following call will generate the appropiate X events and 695 * adjust any state that Tk must remember. 696 */ 697 698 if (!tkwin) { 699 tkwin = TkMacOSXGetCapture(); 700 } 701 if (!tkwin) { 702 return false; 703 } 704 winPtr = (TkWindow *) tkwin; 705 xEvent.type = MouseWheelEvent; 706 xEvent.xkey.keycode = medPtr->delta; 707 xEvent.xbutton.x_root = medPtr->global.h; 708 xEvent.xbutton.y_root = medPtr->global.v; 709 xEvent.xbutton.state = medPtr->state; 710 xEvent.xany.serial = LastKnownRequestProcessed(winPtr->display); 711 xEvent.xany.send_event = false; 712 xEvent.xany.display = winPtr->display; 713 xEvent.xany.window = Tk_WindowId(winPtr); 714 Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL); 715 716 return true; 717} 718 719/* 720 *---------------------------------------------------------------------- 721 * 722 * TkMacOSXGetEatButtonUp -- 723 * 724 * Results: 725 * Returns the flag indicating if we need to eat the 726 * next mouse up event 727 * 728 * Side effects: 729 * None. 730 * 731 *---------------------------------------------------------------------- 732 */ 733int 734TkMacOSXGetEatButtonUp(void) 735{ 736 return gEatButtonUp; 737} 738 739/* 740 *---------------------------------------------------------------------- 741 * 742 * TkMacOSXSetEatButtonUp -- 743 * 744 * Results: 745 * None. 746 * 747 * Side effects: 748 * Sets the flag indicating if we need to eat the 749 * next mouse up event 750 * 751 *---------------------------------------------------------------------- 752 */ 753void 754TkMacOSXSetEatButtonUp(int f) 755{ 756 gEatButtonUp = f; 757} 758 759/* 760 *---------------------------------------------------------------------- 761 * 762 * TkMacOSXKeyModifiers -- 763 * 764 * Returns the current state of the modifier keys. 765 * 766 * Results: 767 * An OS Modifier state. 768 * 769 * Side effects: 770 * None. 771 * 772 *---------------------------------------------------------------------- 773 */ 774 775EventModifiers 776TkMacOSXModifierState(void) 777{ 778 UInt32 keyModifiers; 779 int isFrontProcess = (GetCurrentEvent() && Tk_MacOSXIsAppInFront()); 780 781 keyModifiers = isFrontProcess ? GetCurrentEventKeyModifiers() : 782 GetCurrentKeyModifiers(); 783 784 return (EventModifiers)(keyModifiers & USHRT_MAX); 785} 786 787/* 788 *---------------------------------------------------------------------- 789 * 790 * TkMacOSXButtonKeyState -- 791 * 792 * Returns the current state of the button & modifier keys. 793 * 794 * Results: 795 * A bitwise inclusive OR of a subset of the following: 796 * Button1Mask, ShiftMask, LockMask, ControlMask, Mod*Mask. 797 * 798 * Side effects: 799 * None. 800 * 801 *---------------------------------------------------------------------- 802 */ 803 804unsigned int 805TkMacOSXButtonKeyState(void) 806{ 807 UInt32 buttonState = 0, keyModifiers; 808 int isFrontProcess = (GetCurrentEvent() && Tk_MacOSXIsAppInFront()); 809 810 if (!TkMacOSXGetEatButtonUp()) { 811 buttonState = isFrontProcess ? GetCurrentEventButtonState() : 812 GetCurrentButtonState(); 813 } 814 keyModifiers = isFrontProcess ? GetCurrentEventKeyModifiers() : 815 GetCurrentKeyModifiers(); 816 817 return ButtonModifiers2State(buttonState, keyModifiers); 818} 819 820/* 821 *---------------------------------------------------------------------- 822 * 823 * ButtonModifiers2State -- 824 * 825 * Converts Carbon mouse button state and modifier values into a Tk 826 * button/modifier state. 827 * 828 * Results: 829 * None. 830 * 831 * Side effects: 832 * None. 833 * 834 *---------------------------------------------------------------------- 835 */ 836 837static unsigned int 838ButtonModifiers2State(UInt32 buttonState, UInt32 keyModifiers) 839{ 840 unsigned int state; 841 842 /* Tk supports at most 5 buttons */ 843 state = (buttonState & ((1<<5) - 1)) << 8; 844 845 if (keyModifiers & alphaLock) { 846 state |= LockMask; 847 } 848 if (keyModifiers & shiftKey) { 849 state |= ShiftMask; 850 } 851 if (keyModifiers & controlKey) { 852 state |= ControlMask; 853 } 854 if (keyModifiers & cmdKey) { 855 state |= Mod1Mask; /* command key */ 856 } 857 if (keyModifiers & optionKey) { 858 state |= Mod2Mask; /* option key */ 859 } 860 if (keyModifiers & kEventKeyModifierNumLockMask) { 861 state |= Mod3Mask; 862 } 863 if (keyModifiers & kEventKeyModifierFnMask) { 864 state |= Mod4Mask; 865 } 866 867 return state; 868} 869 870/* 871 *---------------------------------------------------------------------- 872 * 873 * XQueryPointer -- 874 * 875 * Check the current state of the mouse. This is not a complete 876 * implementation of this function. It only computes the root 877 * coordinates and the current mask. 878 * 879 * Results: 880 * Sets root_x_return, root_y_return, and mask_return. Returns 881 * true on success. 882 * 883 * Side effects: 884 * None. 885 * 886 *---------------------------------------------------------------------- 887 */ 888 889Bool 890XQueryPointer( 891 Display* display, 892 Window w, 893 Window* root_return, 894 Window* child_return, 895 int* root_x_return, 896 int* root_y_return, 897 int* win_x_return, 898 int* win_y_return, 899 unsigned int* mask_return) 900{ 901 int getGlobal = (root_x_return && root_y_return); 902 int getLocal = (win_x_return && win_y_return); 903 904 if (getGlobal || getLocal) { 905 Point where, local; 906 OSStatus err = noErr; 907 int gotMouseLoc = 0; 908 EventRef ev = GetCurrentEvent(); 909 910 if (ev && getLocal) { 911 err = ChkErr(GetEventParameter, ev, kEventParamWindowMouseLocation, 912 typeQDPoint, NULL, sizeof(Point), NULL, &local); 913 gotMouseLoc = (err == noErr); 914 } 915 if (getGlobal || !gotMouseLoc) { 916 if (ev) { 917 err = ChkErr(GetEventParameter, ev, kEventParamMouseLocation, 918 typeQDPoint, NULL, sizeof(Point), NULL, &where); 919 } 920 if (!ev || err != noErr) { 921 GetGlobalMouse(&where); 922 } 923 } 924 if (getLocal) { 925 WindowRef whichWin; 926 if (ev) { 927 err = ChkErr(GetEventParameter, ev, kEventParamWindowRef, 928 typeWindowRef, NULL, sizeof(WindowRef), NULL, 929 &whichWin); 930 } 931 if (!ev || err != noErr) { 932 FindWindow(where, &whichWin); 933 } 934 if (gotMouseLoc) { 935 if (whichWin) { 936 Rect widths; 937 938 ChkErr(GetWindowStructureWidths, whichWin, &widths); 939 local.h -= widths.left; 940 local.v -= widths.top; 941 } 942 } else { 943 local = where; 944 if (whichWin) { 945 QDGlobalToLocalPoint(GetWindowPort(whichWin), &local); 946 } 947 } 948 } 949 if (getGlobal) { 950 *root_x_return = where.h; 951 *root_y_return = where.v; 952 } 953 if (getLocal) { 954 *win_x_return = local.h; 955 *win_y_return = local.v; 956 } 957 } 958 if (mask_return) { 959 *mask_return = TkMacOSXButtonKeyState(); 960 } 961 return True; 962} 963 964/* 965 *---------------------------------------------------------------------- 966 * 967 * TkGenerateButtonEventForXPointer -- 968 * 969 * This procedure generates an X button event for the current 970 * pointer state as reported by XQueryPointer(). 971 * 972 * Results: 973 * True if event(s) are generated - false otherwise. 974 * 975 * Side effects: 976 * Additional events may be place on the Tk event queue. 977 * Grab state may also change. 978 * 979 *---------------------------------------------------------------------- 980 */ 981 982MODULE_SCOPE int 983TkGenerateButtonEventForXPointer( 984 Window window) /* X Window containing button event. */ 985{ 986 MouseEventData med; 987 int global_x, global_y, local_x, local_y; 988 989 bzero(&med, sizeof(MouseEventData)); 990 XQueryPointer(NULL, None, NULL, NULL, &global_x, &global_y, 991 &local_x, &local_y, &med.state); 992 med.global.h = global_x; 993 med.global.v = global_y; 994 med.local.h = local_x; 995 med.local.v = local_y; 996 med.window = window; 997 med.activeNonFloating = ActiveNonFloatingWindow(); 998 999 return GenerateButtonEvent(&med); 1000} 1001 1002/* 1003 *---------------------------------------------------------------------- 1004 * 1005 * TkGenerateButtonEvent -- 1006 * 1007 * Given a global x & y position and the button key status this 1008 * procedure generates the appropiate X button event. It also 1009 * handles the state changes needed to implement implicit grabs. 1010 * 1011 * Results: 1012 * True if event(s) are generated - false otherwise. 1013 * 1014 * Side effects: 1015 * Additional events may be place on the Tk event queue. 1016 * Grab state may also change. 1017 * 1018 *---------------------------------------------------------------------- 1019 */ 1020 1021int 1022TkGenerateButtonEvent( 1023 int x, /* X location of mouse */ 1024 int y, /* Y location of mouse */ 1025 Window window, /* X Window containing button event. */ 1026 unsigned int state) /* Button Key state suitable for X event */ 1027{ 1028 MouseEventData med; 1029 1030 bzero(&med, sizeof(MouseEventData)); 1031 med.state = state; 1032 med.window = window; 1033 med.global.h = x; 1034 med.global.v = y; 1035 FindWindow(med.global, &med.whichWin); 1036 med.activeNonFloating = ActiveNonFloatingWindow(); 1037 med.local = med.global; 1038 QDGlobalToLocalPoint(GetWindowPort(med.whichWin), &med.local); 1039 1040 return GenerateButtonEvent(&med); 1041} 1042 1043/* 1044 *---------------------------------------------------------------------- 1045 * 1046 * GenerateButtonEvent -- 1047 * 1048 * Generate an X button event from a MouseEventData structure. 1049 * Handles the state changes needed to implement implicit grabs. 1050 * 1051 * Results: 1052 * True if event(s) are generated - false otherwise. 1053 * 1054 * Side effects: 1055 * Additional events may be place on the Tk event queue. 1056 * Grab state may also change. 1057 * 1058 *---------------------------------------------------------------------- 1059 */ 1060 1061static int 1062GenerateButtonEvent(MouseEventData * medPtr) 1063{ 1064 Tk_Window tkwin; 1065 int dummy; 1066 TkDisplay *dispPtr; 1067 1068#if UNUSED 1069 /* 1070 * ButtonDown events will always occur in the front 1071 * window. ButtonUp events, however, may occur anywhere 1072 * on the screen. ButtonUp events should only be sent 1073 * to Tk if in the front window or during an implicit grab. 1074 */ 1075 if ((medPtr->activeNonFloating == NULL) 1076 || ((!(TkpIsWindowFloating(medPtr->whichWin)) 1077 && (medPtr->activeNonFloating != medPtr->whichWin)) 1078 && TkMacOSXGetCapture() == NULL)) { 1079 return false; 1080 } 1081#endif 1082 1083 dispPtr = TkGetDisplayList(); 1084 tkwin = Tk_IdToWindow(dispPtr->display, medPtr->window); 1085 1086 if (tkwin != NULL) { 1087 tkwin = Tk_TopCoordsToWindow(tkwin, medPtr->local.h, medPtr->local.v, 1088 &dummy, &dummy); 1089 } 1090 1091 Tk_UpdatePointer(tkwin, medPtr->global.h, medPtr->global.v, medPtr->state); 1092 1093 return true; 1094} 1095 1096/* 1097 *---------------------------------------------------------------------- 1098 * 1099 * GenerateToolbarButtonEvent -- 1100 * 1101 * Generates a "ToolbarButton" virtual event. 1102 * This can be used to manage disappearing toolbars. 1103 * 1104 * Results: 1105 * None. 1106 * 1107 * Side effects: 1108 * Places a virtual event on the event queue. 1109 * 1110 *---------------------------------------------------------------------- 1111 */ 1112 1113static int 1114GenerateToolbarButtonEvent(MouseEventData * medPtr) 1115{ 1116 Tk_Window rootwin, tkwin = NULL; 1117 TkDisplay *dispPtr; 1118 TkWindow *winPtr; 1119 XVirtualEvent event; 1120 1121 dispPtr = TkGetDisplayList(); 1122 rootwin = Tk_IdToWindow(dispPtr->display, medPtr->window); 1123 if (rootwin) { 1124 tkwin = Tk_TopCoordsToWindow(rootwin, 1125 medPtr->local.h, medPtr->local.v, &event.x, &event.y); 1126 } 1127 if (!tkwin) { 1128 return true; 1129 } 1130 winPtr = (TkWindow *)tkwin; 1131 1132 bzero(&event, sizeof(XVirtualEvent)); 1133 event.type = VirtualEvent; 1134 event.serial = LastKnownRequestProcessed(winPtr->display); 1135 event.send_event = false; 1136 event.display = winPtr->display; 1137 event.event = winPtr->window; 1138 event.root = XRootWindow(winPtr->display, 0); 1139 event.subwindow = None; 1140 event.time = TkpGetMS(); 1141 event.x_root = medPtr->global.h; 1142 event.y_root = medPtr->global.v; 1143 event.state = medPtr->state; 1144 event.same_screen = true; 1145 event.name = Tk_GetUid("ToolbarButton"); 1146 1147 Tk_QueueWindowEvent((XEvent *) &event, TCL_QUEUE_TAIL); 1148 return true; 1149} 1150