src/solaris/classes/sun/awt/X11/XEmbedCanvasPeer.java

Print this page




 106             applicationActive = toplevel.isFocused();
 107         }
 108     }
 109 
 110     void deinstallActivateListener() {
 111         Window toplevel = getTopLevel(target);
 112         if (toplevel != null) {
 113             toplevel.removeWindowFocusListener(this);
 114         }
 115     }
 116 
 117     boolean isXEmbedActive() {
 118         return xembed.handle != 0;
 119     }
 120 
 121     boolean isApplicationActive() {
 122         return applicationActive;
 123     }
 124 
 125     void initDispatching() {
 126         if (xembedLog.isLoggable(PlatformLogger.FINE)) {
 127             xembedLog.fine("Init embedding for " + Long.toHexString(xembed.handle));
 128         }
 129         XToolkit.awtLock();
 130         try {
 131             XToolkit.addEventDispatcher(xembed.handle, xembed);
 132             XlibWrapper.XSelectInput(XToolkit.getDisplay(), xembed.handle,
 133                                      XConstants.StructureNotifyMask | XConstants.PropertyChangeMask);
 134 
 135             XDropTargetRegistry.getRegistry().registerXEmbedClient(getWindow(), xembed.handle);
 136         } finally {
 137             XToolkit.awtUnlock();
 138         }
 139         xembed.processXEmbedInfo();
 140 
 141         notifyChildEmbedded();
 142     }
 143 
 144     void endDispatching() {
 145         if (xembedLog.isLoggable(PlatformLogger.FINE)) {
 146             xembedLog.fine("End dispatching for " + Long.toHexString(xembed.handle));
 147         }
 148         XToolkit.awtLock();
 149         try {
 150             XDropTargetRegistry.getRegistry().unregisterXEmbedClient(getWindow(), xembed.handle);
 151             // We can't deselect input since someone else might be interested in it
 152             XToolkit.removeEventDispatcher(xembed.handle, xembed);
 153         } finally {
 154             XToolkit.awtUnlock();
 155         }
 156     }
 157 
 158     void embedChild(long child) {
 159         if (xembed.handle != 0) {
 160             detachChild();
 161         }
 162         xembed.handle = child;
 163         initDispatching();
 164     }
 165 
 166     void childDestroyed() {
 167         if (xembedLog.isLoggable(PlatformLogger.FINE)) {
 168             xembedLog.fine("Child " + Long.toHexString(xembed.handle) + " has self-destroyed.");
 169         }
 170         endDispatching();
 171         xembed.handle = 0;
 172     }
 173 
 174     public void handleEvent(AWTEvent e) {
 175         super.handleEvent(e);
 176         if (isXEmbedActive()) {
 177             switch (e.getID()) {
 178               case FocusEvent.FOCUS_GAINED:
 179                   canvasFocusGained((FocusEvent)e);
 180                   break;
 181               case FocusEvent.FOCUS_LOST:
 182                   canvasFocusLost((FocusEvent)e);
 183                   break;
 184               case KeyEvent.KEY_PRESSED:
 185               case KeyEvent.KEY_RELEASED:
 186                   if (!((InputEvent)e).isConsumed()) {
 187                       forwardKeyEvent((KeyEvent)e);
 188                   }
 189                   break;
 190             }
 191         }
 192     }
 193 
 194     public void dispatchEvent(XEvent ev) {
 195         super.dispatchEvent(ev);
 196         switch (ev.get_type()) {
 197           case XConstants.CreateNotify:
 198               XCreateWindowEvent cr = ev.get_xcreatewindow();
 199               if (xembedLog.isLoggable(PlatformLogger.FINEST)) {
 200                   xembedLog.finest("Message on embedder: " + cr);
 201               }
 202               if (xembedLog.isLoggable(PlatformLogger.FINER)) {
 203                   xembedLog.finer("Create notify for parent " + Long.toHexString(cr.get_parent()) +
 204                                   ", window " + Long.toHexString(cr.get_window()));
 205               }
 206               embedChild(cr.get_window());
 207               break;
 208           case XConstants.DestroyNotify:
 209               XDestroyWindowEvent dn = ev.get_xdestroywindow();
 210               if (xembedLog.isLoggable(PlatformLogger.FINEST)) {
 211                   xembedLog.finest("Message on embedder: " + dn);
 212               }
 213               if (xembedLog.isLoggable(PlatformLogger.FINER)) {
 214                   xembedLog.finer("Destroy notify for parent: " + dn);
 215               }
 216               childDestroyed();
 217               break;
 218           case XConstants.ReparentNotify:
 219               XReparentEvent rep = ev.get_xreparent();
 220               if (xembedLog.isLoggable(PlatformLogger.FINEST)) {
 221                   xembedLog.finest("Message on embedder: " + rep);
 222               }
 223               if (xembedLog.isLoggable(PlatformLogger.FINER)) {
 224                   xembedLog.finer("Reparent notify for parent " + Long.toHexString(rep.get_parent()) +
 225                                   ", window " + Long.toHexString(rep.get_window()) +
 226                                   ", event " + Long.toHexString(rep.get_event()));
 227               }
 228               if (rep.get_parent() == getWindow()) {
 229                   // Reparented into us - embed it
 230                   embedChild(rep.get_window());
 231               } else {
 232                   // Reparented out of us - detach it
 233                   childDestroyed();
 234               }
 235               break;
 236         }
 237     }
 238 
 239     public Dimension getPreferredSize() {
 240         if (isXEmbedActive()) {
 241             XToolkit.awtLock();
 242             try {
 243                 long p_hints = XlibWrapper.XAllocSizeHints();


 306                                                               xembed.handle, wattr.pData);
 307 
 308                 XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
 309 
 310                 if ((status == 0) ||
 311                     ((XErrorHandlerUtil.saved_error != null) &&
 312                     (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success))) {
 313                     return null;
 314                 }
 315 
 316                 return new Rectangle(wattr.get_x(), wattr.get_y(), wattr.get_width(), wattr.get_height());
 317             } finally {
 318                 wattr.dispose();
 319             }
 320         } finally {
 321             XToolkit.awtUnlock();
 322         }
 323     }
 324 
 325     void childResized() {
 326         if (xembedLog.isLoggable(PlatformLogger.FINER)) {
 327             Rectangle bounds = getClientBounds();
 328             xembedLog.finer("Child resized: " + bounds);
 329             // It is not required to update embedder's size when client size changes
 330             // However, since there is no any means to get client size it seems to be the
 331             // only way to provide it. However, it contradicts with Java layout concept -
 332             // so it is disabled for now.
 333 //             Rectangle my_bounds = getBounds();
 334 //             setBounds(my_bounds.x, my_bounds.y, bounds.width, bounds.height, SET_BOUNDS);
 335         }
 336         XToolkit.postEvent(XToolkit.targetToAppContext(target), new ComponentEvent(target, ComponentEvent.COMPONENT_RESIZED));
 337     }
 338 
 339     void focusNext() {
 340         if (isXEmbedActive()) {
 341             xembedLog.fine("Requesting focus for the next component after embedder");
 342             postEvent(new InvocationEvent(target, new Runnable() {
 343                     public void run() {
 344                         KeyboardFocusManager.getCurrentKeyboardFocusManager().focusNextComponent(target);
 345                     }
 346                 }));


 371                     }
 372                 }));
 373         } else {
 374             xembedLog.fine("XEmbed is not active - denying request focus");
 375         }
 376     }
 377 
 378     void notifyChildEmbedded() {
 379         xembed.sendMessage(xembed.handle, XEMBED_EMBEDDED_NOTIFY, getWindow(), Math.min(xembed.version, XEMBED_VERSION), 0);
 380         if (isApplicationActive()) {
 381             xembedLog.fine("Sending WINDOW_ACTIVATE during initialization");
 382             xembed.sendMessage(xembed.handle, XEMBED_WINDOW_ACTIVATE);
 383             if (hasFocus()) {
 384                 xembedLog.fine("Sending FOCUS_GAINED during initialization");
 385                 xembed.sendMessage(xembed.handle, XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT, 0, 0);
 386             }
 387         }
 388     }
 389 
 390     void detachChild() {
 391         if (xembedLog.isLoggable(PlatformLogger.FINE)) {
 392             xembedLog.fine("Detaching child " + Long.toHexString(xembed.handle));
 393         }
 394         /**
 395          *  XEmbed specification:
 396          *  "The embedder can unmap the client and reparent the client window to the root window. If the
 397          *  client receives an ReparentNotify event, it should check the parent field of the XReparentEvent
 398          *  structure. If this is the root window of the window's screen, then the protocol is finished and
 399          *  there is no further interaction. If it is a window other than the root window, then the protocol
 400          *  continues with the new parent acting as the embedder window."
 401          */
 402         XToolkit.awtLock();
 403         try {
 404             XlibWrapper.XUnmapWindow(XToolkit.getDisplay(), xembed.handle);
 405             XlibWrapper.XReparentWindow(XToolkit.getDisplay(), xembed.handle, XToolkit.getDefaultRootWindow(), 0, 0);
 406         } finally {
 407             XToolkit.awtUnlock();
 408         }
 409         endDispatching();
 410         xembed.handle = 0;
 411     }


 454                 }
 455             }
 456             xembed.sendMessage(xembed.handle, XEMBED_FOCUS_OUT, num, 0, 0);
 457         }
 458     }
 459 
 460     static byte[] getBData(KeyEvent e) {
 461         return AWTAccessor.getAWTEventAccessor().getBData(e);
 462     }
 463 
 464     void forwardKeyEvent(KeyEvent e) {
 465         xembedLog.fine("Try to forward key event");
 466         byte[] bdata = getBData(e);
 467         long data = Native.toData(bdata);
 468         if (data == 0) {
 469             return;
 470         }
 471         try {
 472             XKeyEvent ke = new XKeyEvent(data);
 473             ke.set_window(xembed.handle);
 474             if (xembedLog.isLoggable(PlatformLogger.FINE)) {
 475                 xembedLog.fine("Forwarding native key event: " + ke);
 476             }
 477             XToolkit.awtLock();
 478             try {
 479                 XlibWrapper.XSendEvent(XToolkit.getDisplay(), xembed.handle, false, XConstants.NoEventMask, data);
 480             } finally {
 481                 XToolkit.awtUnlock();
 482             }
 483         } finally {
 484             XlibWrapper.unsafe.freeMemory(data);
 485         }
 486     }
 487 
 488 
 489     /**
 490      * Grab/ungrab key functionality is an unofficial API supported by
 491      * GTK.  Unfortunately, it doesn't support accelerator API, so,
 492      * since this is the ONLY shortcut-processing API available, we
 493      * must support it.  See XEmbed.NON_STANDARD_XEMBED_GTK_*
 494      * messages.  The format of these messages is as follows:
 495      * - request from client:
 496      * data[1] = NON_STANDARD_XEMBED_GTK_GRAB_KEY or NON_STANDARD_XEMBED_GTK_UNGRAB_KEY
 497      * data[3] = X keysym
 498      * data[4] = X modifiers
 499      *
 500      * - response from server (in case the grabbed key has been pressed):
 501      * forwarded XKeyEvent that matches keysym/modifiers pair
 502      */
 503     void grabKey(final long keysym, final long modifiers) {
 504         postEvent(new InvocationEvent(target, new Runnable() {
 505                 public void run() {
 506                     GrabbedKey grab = new GrabbedKey(keysym, modifiers);
 507                     if (xembedLog.isLoggable(PlatformLogger.FINE)) {
 508                         xembedLog.fine("Grabbing key: " + grab);
 509                     }
 510                     synchronized(GRAB_LOCK) {
 511                         grabbed_keys.add(grab);
 512                     }
 513                 }
 514             }));
 515     }
 516 
 517     void ungrabKey(final long keysym, final long modifiers) {
 518         postEvent(new InvocationEvent(target, new Runnable() {
 519                 public void run() {
 520                     GrabbedKey grab = new GrabbedKey(keysym, modifiers);
 521                     if (xembedLog.isLoggable(PlatformLogger.FINE)) {
 522                         xembedLog.fine("UnGrabbing key: " + grab);
 523                     }
 524                     synchronized(GRAB_LOCK) {
 525                         grabbed_keys.remove(grab);
 526                     }
 527                 }
 528             }));
 529     }
 530 
 531     void registerAccelerator(final long accel_id, final long keysym, final long modifiers) {
 532         postEvent(new InvocationEvent(target, new Runnable() {
 533                 public void run() {
 534                     AWTKeyStroke stroke = xembed.getKeyStrokeForKeySym(keysym, modifiers);
 535                     if (stroke != null) {
 536                         if (xembedLog.isLoggable(PlatformLogger.FINE)) {
 537                             xembedLog.fine("Registering accelerator " + accel_id + " for " + stroke);
 538                         }
 539                         synchronized(ACCEL_LOCK) {
 540                             accelerators.put(accel_id, stroke);
 541                             accel_lookup.put(stroke, accel_id);
 542                         }
 543                     }
 544                     propogateRegisterAccelerator(stroke);
 545                 }
 546             }));
 547     }
 548 
 549     void unregisterAccelerator(final long accel_id) {
 550         postEvent(new InvocationEvent(target, new Runnable() {
 551                 public void run() {
 552                     AWTKeyStroke stroke = null;
 553                     synchronized(ACCEL_LOCK) {
 554                         stroke = accelerators.get(accel_id);
 555                         if (stroke != null) {
 556                             if (xembedLog.isLoggable(PlatformLogger.FINE)) {
 557                                 xembedLog.fine("Unregistering accelerator: " + accel_id);
 558                             }
 559                             accelerators.remove(accel_id);
 560                             accel_lookup.remove(stroke); // FIXME: How about several accelerators with the same stroke?
 561                         }
 562                     }
 563                     propogateUnRegisterAccelerator(stroke);
 564                 }
 565             }));
 566     }
 567 
 568     void propogateRegisterAccelerator(AWTKeyStroke stroke) {
 569         // Find the top-level and see if it is XEmbed client. If so, ask him to
 570         // register the accelerator
 571         XWindowPeer parent = getToplevelXWindow();
 572         if (parent != null && parent instanceof XEmbeddedFramePeer) {
 573             XEmbeddedFramePeer embedded = (XEmbeddedFramePeer)parent;
 574             embedded.registerAccelerator(stroke);
 575         }
 576     }


 584             embedded.unregisterAccelerator(stroke);
 585         }
 586     }
 587 
 588     public boolean postProcessKeyEvent(KeyEvent e) {
 589         // Processing events only if we are in the focused window but
 590         // we are not focus owner since otherwise we will get
 591         // duplicate shortcut events in the client - one is from
 592         // activate_accelerator, another from forwarded event
 593         // FIXME: This is probably an incompatibility, protocol
 594         // doesn't say anything about disable accelerators when client
 595         // is focused.
 596 
 597         XWindowPeer parent = getToplevelXWindow();
 598         if (parent == null || !((Window)parent.getTarget()).isFocused() || target.isFocusOwner()) {
 599             return false;
 600         }
 601 
 602         boolean result = false;
 603 
 604         if (xembedLog.isLoggable(PlatformLogger.FINER)) {
 605             xembedLog.finer("Post-processing event " + e);
 606         }
 607 
 608         // Process ACCELERATORS
 609         AWTKeyStroke stroke = AWTKeyStroke.getAWTKeyStrokeForEvent(e);
 610         long accel_id = 0;
 611         boolean exists = false;
 612         synchronized(ACCEL_LOCK) {
 613             exists = accel_lookup.containsKey(stroke);
 614             if (exists) {
 615                 accel_id = accel_lookup.get(stroke).longValue();
 616             }
 617         }
 618         if (exists) {
 619             if (xembedLog.isLoggable(PlatformLogger.FINE)) {
 620                 xembedLog.fine("Activating accelerator " + accel_id);
 621             }
 622             xembed.sendMessage(xembed.handle, XEMBED_ACTIVATE_ACCELERATOR, accel_id, 0, 0); // FIXME: How about overloaded?
 623             result = true;
 624         }
 625 
 626         // Process Grabs, unofficial GTK feature
 627         exists = false;
 628         GrabbedKey key = new GrabbedKey(e);
 629         synchronized(GRAB_LOCK) {
 630             exists = grabbed_keys.contains(key);
 631         }
 632         if (exists) {
 633             if (xembedLog.isLoggable(PlatformLogger.FINE)) {
 634                 xembedLog.fine("Forwarding grabbed key " + e);
 635             }
 636             forwardKeyEvent(e);
 637             result = true;
 638         }
 639 
 640         return result;
 641     }
 642 
 643     public void modalityPushed(ModalityEvent ev) {
 644         xembed.sendMessage(xembed.handle, XEMBED_MODALITY_ON);
 645     }
 646 
 647     public void modalityPopped(ModalityEvent ev) {
 648         xembed.sendMessage(xembed.handle, XEMBED_MODALITY_OFF);
 649     }
 650 
 651     public void handleClientMessage(XEvent xev) {
 652         super.handleClientMessage(xev);
 653         XClientMessageEvent msg = xev.get_xclient();
 654         if (xembedLog.isLoggable(PlatformLogger.FINER)) {
 655             xembedLog.finer("Client message to embedder: " + msg);
 656         }
 657         if (msg.get_message_type() == xembed.XEmbed.getAtom()) {
 658             if (xembedLog.isLoggable(PlatformLogger.FINE)) {
 659                 xembedLog.fine(xembed.XEmbedMessageToString(msg));
 660             }
 661         }
 662         if (isXEmbedActive()) {
 663             switch ((int)msg.get_data(1)) {
 664               case XEMBED_REQUEST_FOCUS:
 665                   requestXEmbedFocus();
 666                   break;
 667               case XEMBED_FOCUS_NEXT:
 668                   focusNext();
 669                   break;
 670               case XEMBED_FOCUS_PREV:
 671                   focusPrev();
 672                   break;
 673               case XEMBED_REGISTER_ACCELERATOR:
 674                   registerAccelerator(msg.get_data(2), msg.get_data(3), msg.get_data(4));
 675                   break;
 676               case XEMBED_UNREGISTER_ACCELERATOR:
 677                   unregisterAccelerator(msg.get_data(2));
 678                   break;


 706                 public void run() {
 707                     target.setDropTarget(new XEmbedDropTarget());
 708                 }
 709             };
 710         SunToolkit.executeOnEventHandlerThread(target, r);
 711     }
 712 
 713     public void removeXEmbedDropTarget() {
 714         // Unregister a drop site on the top level.
 715         Runnable r = new Runnable() {
 716                 public void run() {
 717                     if (target.getDropTarget() instanceof XEmbedDropTarget) {
 718                         target.setDropTarget(null);
 719                     }
 720                 }
 721             };
 722         SunToolkit.executeOnEventHandlerThread(target, r);
 723     }
 724 
 725     public boolean processXEmbedDnDEvent(long ctxt, int eventID) {
 726         if (xembedLog.isLoggable(PlatformLogger.FINEST)) {
 727             xembedLog.finest("     Drop target=" + target.getDropTarget());
 728         }
 729         if (target.getDropTarget() instanceof XEmbedDropTarget) {
 730             AppContext appContext = XToolkit.targetToAppContext(getTarget());
 731             XDropTargetContextPeer peer =
 732                 XDropTargetContextPeer.getPeer(appContext);
 733             peer.forwardEventToEmbedded(xembed.handle, ctxt, eventID);
 734             return true;
 735         } else {
 736             return false;
 737         }
 738     }
 739 
 740     class XEmbedServer extends XEmbedHelper implements XEventDispatcher {
 741         long handle; // Handle to XEmbed client
 742         long version;
 743         long flags;
 744 
 745         boolean processXEmbedInfo() {
 746             long xembed_info_data = Native.allocateLongArray(2);
 747             try {
 748                 if (!XEmbedInfo.getAtomData(handle, xembed_info_data, 2)) {
 749                     // No more XEMBED_INFO? This is not XEmbed client!
 750                     // Unfortunately this is the initial state of the most clients
 751                     // FIXME: add 5-state processing
 752                     //childDestroyed();
 753                     xembedLog.finer("Unable to get XEMBED_INFO atom data");
 754                     return false;
 755                 }
 756                 version = Native.getCard32(xembed_info_data, 0);
 757                 flags = Native.getCard32(xembed_info_data, 1);
 758                 boolean new_mapped = (flags & XEMBED_MAPPED) != 0;
 759                 boolean currently_mapped = XlibUtil.getWindowMapState(handle) != XConstants.IsUnmapped;
 760                 if (new_mapped != currently_mapped) {
 761                     if (xembedLog.isLoggable(PlatformLogger.FINER)) {
 762                         xembedLog.finer("Mapping state of the client has changed, old state: " + currently_mapped + ", new state: " + new_mapped);
 763                     }
 764                     if (new_mapped) {
 765                         XToolkit.awtLock();
 766                         try {
 767                             XlibWrapper.XMapWindow(XToolkit.getDisplay(), handle);
 768                         } finally {
 769                             XToolkit.awtUnlock();
 770                         }
 771                     } else {
 772                         XToolkit.awtLock();
 773                         try {
 774                             XlibWrapper.XUnmapWindow(XToolkit.getDisplay(), handle);
 775                         } finally {
 776                             XToolkit.awtUnlock();
 777                         }
 778                     }
 779                 } else {
 780                     if (xembedLog.isLoggable(PlatformLogger.FINER)) {
 781                         xembedLog.finer("Mapping state didn't change, mapped: " + currently_mapped);
 782                     }
 783                 }
 784                 return true;
 785             } finally {
 786                 XlibWrapper.unsafe.freeMemory(xembed_info_data);
 787             }
 788         }
 789 
 790         public void handlePropertyNotify(XEvent xev) {
 791             if (isXEmbedActive()) {
 792                 XPropertyEvent ev = xev.get_xproperty();
 793                 if (xembedLog.isLoggable(PlatformLogger.FINER)) {
 794                     xembedLog.finer("Property change on client: " + ev);
 795                 }
 796                 if (ev.get_atom() == XAtom.XA_WM_NORMAL_HINTS) {
 797                     childResized();
 798                 } else if (ev.get_atom() == XEmbedInfo.getAtom()) {
 799                     processXEmbedInfo();
 800                 } else if (ev.get_atom() ==
 801                            XDnDConstants.XA_XdndAware.getAtom()) {
 802                     XDropTargetRegistry.getRegistry().unregisterXEmbedClient(getWindow(),
 803                                                                              xembed.handle);
 804                     if (ev.get_state() == XConstants.PropertyNewValue) {
 805                         XDropTargetRegistry.getRegistry().registerXEmbedClient(getWindow(),
 806                                                                                 xembed.handle);
 807                     }
 808                 }
 809             } else {
 810                 xembedLog.finer("XEmbed is not active");
 811             }
 812         }
 813         void handleConfigureNotify(XEvent xev) {
 814             if (isXEmbedActive()) {
 815                 XConfigureEvent ev = xev.get_xconfigure();
 816                 if (xembedLog.isLoggable(PlatformLogger.FINER)) {
 817                     xembedLog.finer("Bounds change on client: " + ev);
 818                 }
 819                 if (xev.get_xany().get_window() == handle) {
 820                     childResized();
 821                 }
 822             }
 823         }
 824         public void dispatchEvent(XEvent xev) {
 825             int type = xev.get_type();
 826             switch (type) {
 827               case XConstants.PropertyNotify:
 828                   handlePropertyNotify(xev);
 829                   break;
 830               case XConstants.ConfigureNotify:
 831                   handleConfigureNotify(xev);
 832                   break;
 833               case XConstants.ClientMessage:
 834                   handleClientMessage(xev);
 835                   break;
 836             }


 849             init(ev);
 850         }
 851 
 852         private void init(KeyEvent e) {
 853             byte[] bdata = getBData(e);
 854             long data = Native.toData(bdata);
 855             if (data == 0) {
 856                 return;
 857             }
 858             try {
 859                 XToolkit.awtLock();
 860                 try {
 861                     keysym = XWindow.getKeySymForAWTKeyCode(e.getKeyCode());
 862                 } finally {
 863                     XToolkit.awtUnlock();
 864                 }
 865                 XKeyEvent ke = new XKeyEvent(data);
 866 
 867                 // We recognize only these masks
 868                 modifiers = ke.get_state() & (XConstants.ShiftMask | XConstants.ControlMask | XConstants.LockMask);
 869                 if (xembedLog.isLoggable(PlatformLogger.FINEST)) {
 870                     xembedLog.finest("Mapped " + e + " to " + this);
 871                 }
 872             } finally {
 873                 XlibWrapper.unsafe.freeMemory(data);
 874             }
 875         }
 876 
 877         public int hashCode() {
 878             return (int)keysym & 0xFFFFFFFF;
 879         }
 880 
 881         public boolean equals(Object o) {
 882             if (!(o instanceof GrabbedKey)) {
 883                 return false;
 884             }
 885             GrabbedKey key = (GrabbedKey)o;
 886             return (keysym == key.keysym && modifiers == key.modifiers);
 887         }
 888 
 889         public String toString() {


 106             applicationActive = toplevel.isFocused();
 107         }
 108     }
 109 
 110     void deinstallActivateListener() {
 111         Window toplevel = getTopLevel(target);
 112         if (toplevel != null) {
 113             toplevel.removeWindowFocusListener(this);
 114         }
 115     }
 116 
 117     boolean isXEmbedActive() {
 118         return xembed.handle != 0;
 119     }
 120 
 121     boolean isApplicationActive() {
 122         return applicationActive;
 123     }
 124 
 125     void initDispatching() {
 126         if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
 127             xembedLog.fine("Init embedding for " + Long.toHexString(xembed.handle));
 128         }
 129         XToolkit.awtLock();
 130         try {
 131             XToolkit.addEventDispatcher(xembed.handle, xembed);
 132             XlibWrapper.XSelectInput(XToolkit.getDisplay(), xembed.handle,
 133                                      XConstants.StructureNotifyMask | XConstants.PropertyChangeMask);
 134 
 135             XDropTargetRegistry.getRegistry().registerXEmbedClient(getWindow(), xembed.handle);
 136         } finally {
 137             XToolkit.awtUnlock();
 138         }
 139         xembed.processXEmbedInfo();
 140 
 141         notifyChildEmbedded();
 142     }
 143 
 144     void endDispatching() {
 145         if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
 146             xembedLog.fine("End dispatching for " + Long.toHexString(xembed.handle));
 147         }
 148         XToolkit.awtLock();
 149         try {
 150             XDropTargetRegistry.getRegistry().unregisterXEmbedClient(getWindow(), xembed.handle);
 151             // We can't deselect input since someone else might be interested in it
 152             XToolkit.removeEventDispatcher(xembed.handle, xembed);
 153         } finally {
 154             XToolkit.awtUnlock();
 155         }
 156     }
 157 
 158     void embedChild(long child) {
 159         if (xembed.handle != 0) {
 160             detachChild();
 161         }
 162         xembed.handle = child;
 163         initDispatching();
 164     }
 165 
 166     void childDestroyed() {
 167         if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
 168             xembedLog.fine("Child " + Long.toHexString(xembed.handle) + " has self-destroyed.");
 169         }
 170         endDispatching();
 171         xembed.handle = 0;
 172     }
 173 
 174     public void handleEvent(AWTEvent e) {
 175         super.handleEvent(e);
 176         if (isXEmbedActive()) {
 177             switch (e.getID()) {
 178               case FocusEvent.FOCUS_GAINED:
 179                   canvasFocusGained((FocusEvent)e);
 180                   break;
 181               case FocusEvent.FOCUS_LOST:
 182                   canvasFocusLost((FocusEvent)e);
 183                   break;
 184               case KeyEvent.KEY_PRESSED:
 185               case KeyEvent.KEY_RELEASED:
 186                   if (!((InputEvent)e).isConsumed()) {
 187                       forwardKeyEvent((KeyEvent)e);
 188                   }
 189                   break;
 190             }
 191         }
 192     }
 193 
 194     public void dispatchEvent(XEvent ev) {
 195         super.dispatchEvent(ev);
 196         switch (ev.get_type()) {
 197           case XConstants.CreateNotify:
 198               XCreateWindowEvent cr = ev.get_xcreatewindow();
 199               if (xembedLog.isLoggable(PlatformLogger.Level.FINEST)) {
 200                   xembedLog.finest("Message on embedder: " + cr);
 201               }
 202               if (xembedLog.isLoggable(PlatformLogger.Level.FINER)) {
 203                   xembedLog.finer("Create notify for parent " + Long.toHexString(cr.get_parent()) +
 204                                   ", window " + Long.toHexString(cr.get_window()));
 205               }
 206               embedChild(cr.get_window());
 207               break;
 208           case XConstants.DestroyNotify:
 209               XDestroyWindowEvent dn = ev.get_xdestroywindow();
 210               if (xembedLog.isLoggable(PlatformLogger.Level.FINEST)) {
 211                   xembedLog.finest("Message on embedder: " + dn);
 212               }
 213               if (xembedLog.isLoggable(PlatformLogger.Level.FINER)) {
 214                   xembedLog.finer("Destroy notify for parent: " + dn);
 215               }
 216               childDestroyed();
 217               break;
 218           case XConstants.ReparentNotify:
 219               XReparentEvent rep = ev.get_xreparent();
 220               if (xembedLog.isLoggable(PlatformLogger.Level.FINEST)) {
 221                   xembedLog.finest("Message on embedder: " + rep);
 222               }
 223               if (xembedLog.isLoggable(PlatformLogger.Level.FINER)) {
 224                   xembedLog.finer("Reparent notify for parent " + Long.toHexString(rep.get_parent()) +
 225                                   ", window " + Long.toHexString(rep.get_window()) +
 226                                   ", event " + Long.toHexString(rep.get_event()));
 227               }
 228               if (rep.get_parent() == getWindow()) {
 229                   // Reparented into us - embed it
 230                   embedChild(rep.get_window());
 231               } else {
 232                   // Reparented out of us - detach it
 233                   childDestroyed();
 234               }
 235               break;
 236         }
 237     }
 238 
 239     public Dimension getPreferredSize() {
 240         if (isXEmbedActive()) {
 241             XToolkit.awtLock();
 242             try {
 243                 long p_hints = XlibWrapper.XAllocSizeHints();


 306                                                               xembed.handle, wattr.pData);
 307 
 308                 XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
 309 
 310                 if ((status == 0) ||
 311                     ((XErrorHandlerUtil.saved_error != null) &&
 312                     (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success))) {
 313                     return null;
 314                 }
 315 
 316                 return new Rectangle(wattr.get_x(), wattr.get_y(), wattr.get_width(), wattr.get_height());
 317             } finally {
 318                 wattr.dispose();
 319             }
 320         } finally {
 321             XToolkit.awtUnlock();
 322         }
 323     }
 324 
 325     void childResized() {
 326         if (xembedLog.isLoggable(PlatformLogger.Level.FINER)) {
 327             Rectangle bounds = getClientBounds();
 328             xembedLog.finer("Child resized: " + bounds);
 329             // It is not required to update embedder's size when client size changes
 330             // However, since there is no any means to get client size it seems to be the
 331             // only way to provide it. However, it contradicts with Java layout concept -
 332             // so it is disabled for now.
 333 //             Rectangle my_bounds = getBounds();
 334 //             setBounds(my_bounds.x, my_bounds.y, bounds.width, bounds.height, SET_BOUNDS);
 335         }
 336         XToolkit.postEvent(XToolkit.targetToAppContext(target), new ComponentEvent(target, ComponentEvent.COMPONENT_RESIZED));
 337     }
 338 
 339     void focusNext() {
 340         if (isXEmbedActive()) {
 341             xembedLog.fine("Requesting focus for the next component after embedder");
 342             postEvent(new InvocationEvent(target, new Runnable() {
 343                     public void run() {
 344                         KeyboardFocusManager.getCurrentKeyboardFocusManager().focusNextComponent(target);
 345                     }
 346                 }));


 371                     }
 372                 }));
 373         } else {
 374             xembedLog.fine("XEmbed is not active - denying request focus");
 375         }
 376     }
 377 
 378     void notifyChildEmbedded() {
 379         xembed.sendMessage(xembed.handle, XEMBED_EMBEDDED_NOTIFY, getWindow(), Math.min(xembed.version, XEMBED_VERSION), 0);
 380         if (isApplicationActive()) {
 381             xembedLog.fine("Sending WINDOW_ACTIVATE during initialization");
 382             xembed.sendMessage(xembed.handle, XEMBED_WINDOW_ACTIVATE);
 383             if (hasFocus()) {
 384                 xembedLog.fine("Sending FOCUS_GAINED during initialization");
 385                 xembed.sendMessage(xembed.handle, XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT, 0, 0);
 386             }
 387         }
 388     }
 389 
 390     void detachChild() {
 391         if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
 392             xembedLog.fine("Detaching child " + Long.toHexString(xembed.handle));
 393         }
 394         /**
 395          *  XEmbed specification:
 396          *  "The embedder can unmap the client and reparent the client window to the root window. If the
 397          *  client receives an ReparentNotify event, it should check the parent field of the XReparentEvent
 398          *  structure. If this is the root window of the window's screen, then the protocol is finished and
 399          *  there is no further interaction. If it is a window other than the root window, then the protocol
 400          *  continues with the new parent acting as the embedder window."
 401          */
 402         XToolkit.awtLock();
 403         try {
 404             XlibWrapper.XUnmapWindow(XToolkit.getDisplay(), xembed.handle);
 405             XlibWrapper.XReparentWindow(XToolkit.getDisplay(), xembed.handle, XToolkit.getDefaultRootWindow(), 0, 0);
 406         } finally {
 407             XToolkit.awtUnlock();
 408         }
 409         endDispatching();
 410         xembed.handle = 0;
 411     }


 454                 }
 455             }
 456             xembed.sendMessage(xembed.handle, XEMBED_FOCUS_OUT, num, 0, 0);
 457         }
 458     }
 459 
 460     static byte[] getBData(KeyEvent e) {
 461         return AWTAccessor.getAWTEventAccessor().getBData(e);
 462     }
 463 
 464     void forwardKeyEvent(KeyEvent e) {
 465         xembedLog.fine("Try to forward key event");
 466         byte[] bdata = getBData(e);
 467         long data = Native.toData(bdata);
 468         if (data == 0) {
 469             return;
 470         }
 471         try {
 472             XKeyEvent ke = new XKeyEvent(data);
 473             ke.set_window(xembed.handle);
 474             if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
 475                 xembedLog.fine("Forwarding native key event: " + ke);
 476             }
 477             XToolkit.awtLock();
 478             try {
 479                 XlibWrapper.XSendEvent(XToolkit.getDisplay(), xembed.handle, false, XConstants.NoEventMask, data);
 480             } finally {
 481                 XToolkit.awtUnlock();
 482             }
 483         } finally {
 484             XlibWrapper.unsafe.freeMemory(data);
 485         }
 486     }
 487 
 488 
 489     /**
 490      * Grab/ungrab key functionality is an unofficial API supported by
 491      * GTK.  Unfortunately, it doesn't support accelerator API, so,
 492      * since this is the ONLY shortcut-processing API available, we
 493      * must support it.  See XEmbed.NON_STANDARD_XEMBED_GTK_*
 494      * messages.  The format of these messages is as follows:
 495      * - request from client:
 496      * data[1] = NON_STANDARD_XEMBED_GTK_GRAB_KEY or NON_STANDARD_XEMBED_GTK_UNGRAB_KEY
 497      * data[3] = X keysym
 498      * data[4] = X modifiers
 499      *
 500      * - response from server (in case the grabbed key has been pressed):
 501      * forwarded XKeyEvent that matches keysym/modifiers pair
 502      */
 503     void grabKey(final long keysym, final long modifiers) {
 504         postEvent(new InvocationEvent(target, new Runnable() {
 505                 public void run() {
 506                     GrabbedKey grab = new GrabbedKey(keysym, modifiers);
 507                     if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
 508                         xembedLog.fine("Grabbing key: " + grab);
 509                     }
 510                     synchronized(GRAB_LOCK) {
 511                         grabbed_keys.add(grab);
 512                     }
 513                 }
 514             }));
 515     }
 516 
 517     void ungrabKey(final long keysym, final long modifiers) {
 518         postEvent(new InvocationEvent(target, new Runnable() {
 519                 public void run() {
 520                     GrabbedKey grab = new GrabbedKey(keysym, modifiers);
 521                     if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
 522                         xembedLog.fine("UnGrabbing key: " + grab);
 523                     }
 524                     synchronized(GRAB_LOCK) {
 525                         grabbed_keys.remove(grab);
 526                     }
 527                 }
 528             }));
 529     }
 530 
 531     void registerAccelerator(final long accel_id, final long keysym, final long modifiers) {
 532         postEvent(new InvocationEvent(target, new Runnable() {
 533                 public void run() {
 534                     AWTKeyStroke stroke = xembed.getKeyStrokeForKeySym(keysym, modifiers);
 535                     if (stroke != null) {
 536                         if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
 537                             xembedLog.fine("Registering accelerator " + accel_id + " for " + stroke);
 538                         }
 539                         synchronized(ACCEL_LOCK) {
 540                             accelerators.put(accel_id, stroke);
 541                             accel_lookup.put(stroke, accel_id);
 542                         }
 543                     }
 544                     propogateRegisterAccelerator(stroke);
 545                 }
 546             }));
 547     }
 548 
 549     void unregisterAccelerator(final long accel_id) {
 550         postEvent(new InvocationEvent(target, new Runnable() {
 551                 public void run() {
 552                     AWTKeyStroke stroke = null;
 553                     synchronized(ACCEL_LOCK) {
 554                         stroke = accelerators.get(accel_id);
 555                         if (stroke != null) {
 556                             if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
 557                                 xembedLog.fine("Unregistering accelerator: " + accel_id);
 558                             }
 559                             accelerators.remove(accel_id);
 560                             accel_lookup.remove(stroke); // FIXME: How about several accelerators with the same stroke?
 561                         }
 562                     }
 563                     propogateUnRegisterAccelerator(stroke);
 564                 }
 565             }));
 566     }
 567 
 568     void propogateRegisterAccelerator(AWTKeyStroke stroke) {
 569         // Find the top-level and see if it is XEmbed client. If so, ask him to
 570         // register the accelerator
 571         XWindowPeer parent = getToplevelXWindow();
 572         if (parent != null && parent instanceof XEmbeddedFramePeer) {
 573             XEmbeddedFramePeer embedded = (XEmbeddedFramePeer)parent;
 574             embedded.registerAccelerator(stroke);
 575         }
 576     }


 584             embedded.unregisterAccelerator(stroke);
 585         }
 586     }
 587 
 588     public boolean postProcessKeyEvent(KeyEvent e) {
 589         // Processing events only if we are in the focused window but
 590         // we are not focus owner since otherwise we will get
 591         // duplicate shortcut events in the client - one is from
 592         // activate_accelerator, another from forwarded event
 593         // FIXME: This is probably an incompatibility, protocol
 594         // doesn't say anything about disable accelerators when client
 595         // is focused.
 596 
 597         XWindowPeer parent = getToplevelXWindow();
 598         if (parent == null || !((Window)parent.getTarget()).isFocused() || target.isFocusOwner()) {
 599             return false;
 600         }
 601 
 602         boolean result = false;
 603 
 604         if (xembedLog.isLoggable(PlatformLogger.Level.FINER)) {
 605             xembedLog.finer("Post-processing event " + e);
 606         }
 607 
 608         // Process ACCELERATORS
 609         AWTKeyStroke stroke = AWTKeyStroke.getAWTKeyStrokeForEvent(e);
 610         long accel_id = 0;
 611         boolean exists = false;
 612         synchronized(ACCEL_LOCK) {
 613             exists = accel_lookup.containsKey(stroke);
 614             if (exists) {
 615                 accel_id = accel_lookup.get(stroke).longValue();
 616             }
 617         }
 618         if (exists) {
 619             if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
 620                 xembedLog.fine("Activating accelerator " + accel_id);
 621             }
 622             xembed.sendMessage(xembed.handle, XEMBED_ACTIVATE_ACCELERATOR, accel_id, 0, 0); // FIXME: How about overloaded?
 623             result = true;
 624         }
 625 
 626         // Process Grabs, unofficial GTK feature
 627         exists = false;
 628         GrabbedKey key = new GrabbedKey(e);
 629         synchronized(GRAB_LOCK) {
 630             exists = grabbed_keys.contains(key);
 631         }
 632         if (exists) {
 633             if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
 634                 xembedLog.fine("Forwarding grabbed key " + e);
 635             }
 636             forwardKeyEvent(e);
 637             result = true;
 638         }
 639 
 640         return result;
 641     }
 642 
 643     public void modalityPushed(ModalityEvent ev) {
 644         xembed.sendMessage(xembed.handle, XEMBED_MODALITY_ON);
 645     }
 646 
 647     public void modalityPopped(ModalityEvent ev) {
 648         xembed.sendMessage(xembed.handle, XEMBED_MODALITY_OFF);
 649     }
 650 
 651     public void handleClientMessage(XEvent xev) {
 652         super.handleClientMessage(xev);
 653         XClientMessageEvent msg = xev.get_xclient();
 654         if (xembedLog.isLoggable(PlatformLogger.Level.FINER)) {
 655             xembedLog.finer("Client message to embedder: " + msg);
 656         }
 657         if (msg.get_message_type() == xembed.XEmbed.getAtom()) {
 658             if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
 659                 xembedLog.fine(xembed.XEmbedMessageToString(msg));
 660             }
 661         }
 662         if (isXEmbedActive()) {
 663             switch ((int)msg.get_data(1)) {
 664               case XEMBED_REQUEST_FOCUS:
 665                   requestXEmbedFocus();
 666                   break;
 667               case XEMBED_FOCUS_NEXT:
 668                   focusNext();
 669                   break;
 670               case XEMBED_FOCUS_PREV:
 671                   focusPrev();
 672                   break;
 673               case XEMBED_REGISTER_ACCELERATOR:
 674                   registerAccelerator(msg.get_data(2), msg.get_data(3), msg.get_data(4));
 675                   break;
 676               case XEMBED_UNREGISTER_ACCELERATOR:
 677                   unregisterAccelerator(msg.get_data(2));
 678                   break;


 706                 public void run() {
 707                     target.setDropTarget(new XEmbedDropTarget());
 708                 }
 709             };
 710         SunToolkit.executeOnEventHandlerThread(target, r);
 711     }
 712 
 713     public void removeXEmbedDropTarget() {
 714         // Unregister a drop site on the top level.
 715         Runnable r = new Runnable() {
 716                 public void run() {
 717                     if (target.getDropTarget() instanceof XEmbedDropTarget) {
 718                         target.setDropTarget(null);
 719                     }
 720                 }
 721             };
 722         SunToolkit.executeOnEventHandlerThread(target, r);
 723     }
 724 
 725     public boolean processXEmbedDnDEvent(long ctxt, int eventID) {
 726         if (xembedLog.isLoggable(PlatformLogger.Level.FINEST)) {
 727             xembedLog.finest("     Drop target=" + target.getDropTarget());
 728         }
 729         if (target.getDropTarget() instanceof XEmbedDropTarget) {
 730             AppContext appContext = XToolkit.targetToAppContext(getTarget());
 731             XDropTargetContextPeer peer =
 732                 XDropTargetContextPeer.getPeer(appContext);
 733             peer.forwardEventToEmbedded(xembed.handle, ctxt, eventID);
 734             return true;
 735         } else {
 736             return false;
 737         }
 738     }
 739 
 740     class XEmbedServer extends XEmbedHelper implements XEventDispatcher {
 741         long handle; // Handle to XEmbed client
 742         long version;
 743         long flags;
 744 
 745         boolean processXEmbedInfo() {
 746             long xembed_info_data = Native.allocateLongArray(2);
 747             try {
 748                 if (!XEmbedInfo.getAtomData(handle, xembed_info_data, 2)) {
 749                     // No more XEMBED_INFO? This is not XEmbed client!
 750                     // Unfortunately this is the initial state of the most clients
 751                     // FIXME: add 5-state processing
 752                     //childDestroyed();
 753                     xembedLog.finer("Unable to get XEMBED_INFO atom data");
 754                     return false;
 755                 }
 756                 version = Native.getCard32(xembed_info_data, 0);
 757                 flags = Native.getCard32(xembed_info_data, 1);
 758                 boolean new_mapped = (flags & XEMBED_MAPPED) != 0;
 759                 boolean currently_mapped = XlibUtil.getWindowMapState(handle) != XConstants.IsUnmapped;
 760                 if (new_mapped != currently_mapped) {
 761                     if (xembedLog.isLoggable(PlatformLogger.Level.FINER)) {
 762                         xembedLog.finer("Mapping state of the client has changed, old state: " + currently_mapped + ", new state: " + new_mapped);
 763                     }
 764                     if (new_mapped) {
 765                         XToolkit.awtLock();
 766                         try {
 767                             XlibWrapper.XMapWindow(XToolkit.getDisplay(), handle);
 768                         } finally {
 769                             XToolkit.awtUnlock();
 770                         }
 771                     } else {
 772                         XToolkit.awtLock();
 773                         try {
 774                             XlibWrapper.XUnmapWindow(XToolkit.getDisplay(), handle);
 775                         } finally {
 776                             XToolkit.awtUnlock();
 777                         }
 778                     }
 779                 } else {
 780                     if (xembedLog.isLoggable(PlatformLogger.Level.FINER)) {
 781                         xembedLog.finer("Mapping state didn't change, mapped: " + currently_mapped);
 782                     }
 783                 }
 784                 return true;
 785             } finally {
 786                 XlibWrapper.unsafe.freeMemory(xembed_info_data);
 787             }
 788         }
 789 
 790         public void handlePropertyNotify(XEvent xev) {
 791             if (isXEmbedActive()) {
 792                 XPropertyEvent ev = xev.get_xproperty();
 793                 if (xembedLog.isLoggable(PlatformLogger.Level.FINER)) {
 794                     xembedLog.finer("Property change on client: " + ev);
 795                 }
 796                 if (ev.get_atom() == XAtom.XA_WM_NORMAL_HINTS) {
 797                     childResized();
 798                 } else if (ev.get_atom() == XEmbedInfo.getAtom()) {
 799                     processXEmbedInfo();
 800                 } else if (ev.get_atom() ==
 801                            XDnDConstants.XA_XdndAware.getAtom()) {
 802                     XDropTargetRegistry.getRegistry().unregisterXEmbedClient(getWindow(),
 803                                                                              xembed.handle);
 804                     if (ev.get_state() == XConstants.PropertyNewValue) {
 805                         XDropTargetRegistry.getRegistry().registerXEmbedClient(getWindow(),
 806                                                                                 xembed.handle);
 807                     }
 808                 }
 809             } else {
 810                 xembedLog.finer("XEmbed is not active");
 811             }
 812         }
 813         void handleConfigureNotify(XEvent xev) {
 814             if (isXEmbedActive()) {
 815                 XConfigureEvent ev = xev.get_xconfigure();
 816                 if (xembedLog.isLoggable(PlatformLogger.Level.FINER)) {
 817                     xembedLog.finer("Bounds change on client: " + ev);
 818                 }
 819                 if (xev.get_xany().get_window() == handle) {
 820                     childResized();
 821                 }
 822             }
 823         }
 824         public void dispatchEvent(XEvent xev) {
 825             int type = xev.get_type();
 826             switch (type) {
 827               case XConstants.PropertyNotify:
 828                   handlePropertyNotify(xev);
 829                   break;
 830               case XConstants.ConfigureNotify:
 831                   handleConfigureNotify(xev);
 832                   break;
 833               case XConstants.ClientMessage:
 834                   handleClientMessage(xev);
 835                   break;
 836             }


 849             init(ev);
 850         }
 851 
 852         private void init(KeyEvent e) {
 853             byte[] bdata = getBData(e);
 854             long data = Native.toData(bdata);
 855             if (data == 0) {
 856                 return;
 857             }
 858             try {
 859                 XToolkit.awtLock();
 860                 try {
 861                     keysym = XWindow.getKeySymForAWTKeyCode(e.getKeyCode());
 862                 } finally {
 863                     XToolkit.awtUnlock();
 864                 }
 865                 XKeyEvent ke = new XKeyEvent(data);
 866 
 867                 // We recognize only these masks
 868                 modifiers = ke.get_state() & (XConstants.ShiftMask | XConstants.ControlMask | XConstants.LockMask);
 869                 if (xembedLog.isLoggable(PlatformLogger.Level.FINEST)) {
 870                     xembedLog.finest("Mapped " + e + " to " + this);
 871                 }
 872             } finally {
 873                 XlibWrapper.unsafe.freeMemory(data);
 874             }
 875         }
 876 
 877         public int hashCode() {
 878             return (int)keysym & 0xFFFFFFFF;
 879         }
 880 
 881         public boolean equals(Object o) {
 882             if (!(o instanceof GrabbedKey)) {
 883                 return false;
 884             }
 885             GrabbedKey key = (GrabbedKey)o;
 886             return (keysym == key.keysym && modifiers == key.modifiers);
 887         }
 888 
 889         public String toString() {