1 /*
   2  * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.awt.X11;
  27 
  28 import java.awt.*;
  29 import java.awt.dnd.DropTarget;
  30 import java.awt.dnd.DropTargetListener;
  31 import java.awt.event.*;
  32 import sun.awt.*;
  33 import sun.awt.AWTAccessor;
  34 import sun.util.logging.PlatformLogger;
  35 import java.util.*;
  36 import static sun.awt.X11.XEmbedHelper.*;
  37 
  38 import java.security.AccessController;
  39 import sun.security.action.GetBooleanAction;
  40 
  41 public class XEmbedCanvasPeer extends XCanvasPeer implements WindowFocusListener, KeyEventPostProcessor, ModalityListener, WindowIDProvider {
  42     private static final PlatformLogger xembedLog = PlatformLogger.getLogger("sun.awt.X11.xembed.XEmbedCanvasPeer");
  43 
  44     boolean applicationActive; // Whether the application is active(has focus)
  45     XEmbedServer xembed = new XEmbedServer(); // Helper object, contains XEmbed intrinsics
  46     Map<Long, AWTKeyStroke> accelerators = new HashMap<Long, AWTKeyStroke>(); // Maps accelerator ID into AWTKeyStroke
  47     Map<AWTKeyStroke, Long> accel_lookup = new HashMap<AWTKeyStroke, Long>(); // Maps AWTKeyStroke into accelerator ID
  48     Set<GrabbedKey> grabbed_keys = new HashSet<GrabbedKey>(); // A set of keys grabbed by client
  49     Object ACCEL_LOCK = accelerators; // Lock object for working with accelerators;
  50     Object GRAB_LOCK = grabbed_keys; // Lock object for working with keys grabbed by client
  51 
  52     XEmbedCanvasPeer() {}
  53 
  54     XEmbedCanvasPeer(XCreateWindowParams params) {
  55         super(params);
  56     }
  57 
  58     XEmbedCanvasPeer(Component target) {
  59         super(target);
  60     }
  61 
  62     protected void postInit(XCreateWindowParams params) {
  63         super.postInit(params);
  64 
  65         installActivateListener();
  66         installAcceleratorListener();
  67         installModalityListener();
  68 
  69         // XEmbed canvas should be non-traversable.
  70         // FIXME: Probably should be removed and enforced setting of it by the users
  71         target.setFocusTraversalKeysEnabled(false);
  72     }
  73 
  74     protected void preInit(XCreateWindowParams params) {
  75         super.preInit(params);
  76 
  77         params.put(EVENT_MASK,
  78                    XConstants.KeyPressMask       | XConstants.KeyReleaseMask
  79                    | XConstants.FocusChangeMask  | XConstants.ButtonPressMask | XConstants.ButtonReleaseMask
  80                    | XConstants.EnterWindowMask  | XConstants.LeaveWindowMask | XConstants.PointerMotionMask
  81                    | XConstants.ButtonMotionMask | XConstants.ExposureMask    | XConstants.StructureNotifyMask | XConstants.SubstructureNotifyMask);
  82 
  83     }
  84 
  85     void installModalityListener() {
  86         ((SunToolkit)Toolkit.getDefaultToolkit()).addModalityListener(this);
  87     }
  88 
  89     void deinstallModalityListener() {
  90         ((SunToolkit)Toolkit.getDefaultToolkit()).removeModalityListener(this);
  91     }
  92 
  93     void installAcceleratorListener() {
  94         KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventPostProcessor(this);
  95     }
  96 
  97     void deinstallAcceleratorListener() {
  98         KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventPostProcessor(this);
  99     }
 100 
 101     void installActivateListener() {
 102         // FIXME: should watch for hierarchy changes
 103         Window toplevel = getTopLevel(target);
 104         if (toplevel != null) {
 105             toplevel.addWindowFocusListener(this);
 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();
 244                 XSizeHints hints = new XSizeHints(p_hints);
 245                 XlibWrapper.XGetWMNormalHints(XToolkit.getDisplay(), xembed.handle, p_hints, XlibWrapper.larg1);
 246                 Dimension res = new Dimension(hints.get_width(), hints.get_height());
 247                 XlibWrapper.XFree(p_hints);
 248                 return res;
 249             } finally {
 250                 XToolkit.awtUnlock();
 251             }
 252         } else {
 253             return super.getPreferredSize();
 254         }
 255     }
 256     public Dimension getMinimumSize() {
 257         if (isXEmbedActive()) {
 258             XToolkit.awtLock();
 259             try {
 260                 long p_hints = XlibWrapper.XAllocSizeHints();
 261                 XSizeHints hints = new XSizeHints(p_hints);
 262                 XlibWrapper.XGetWMNormalHints(XToolkit.getDisplay(), xembed.handle, p_hints, XlibWrapper.larg1);
 263                 Dimension res = new Dimension(hints.get_min_width(), hints.get_min_height());
 264                 XlibWrapper.XFree(p_hints);
 265                 return res;
 266             } finally {
 267                 XToolkit.awtUnlock();
 268             }
 269         } else {
 270             return super.getMinimumSize();
 271         }
 272     }
 273     public void dispose() {
 274         if (isXEmbedActive()) {
 275             detachChild();
 276         }
 277         deinstallActivateListener();
 278         deinstallModalityListener();
 279         deinstallAcceleratorListener();
 280 
 281         // BUG: Focus traversal doesn't become enabled after the one round of embedding
 282         //target.setFocusTraversalKeysEnabled(true);
 283 
 284         super.dispose();
 285     }
 286 
 287     // Focusable is true in order to enable focus traversal through this Canvas
 288     public boolean isFocusable() {
 289         return true;
 290     }
 291 
 292     Window getTopLevel(Component comp) {
 293         while (comp != null && !(comp instanceof Window)) {
 294             comp = comp.getParent();
 295         }
 296         return (Window)comp;
 297     }
 298 
 299     Rectangle getClientBounds() {
 300         XToolkit.awtLock();
 301         try {
 302             XWindowAttributes wattr = new XWindowAttributes();
 303             try {
 304                 XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 305                 int status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
 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                 }));
 347         } else {
 348             xembedLog.fine("XEmbed is not active - denying focus next");
 349         }
 350     }
 351 
 352     void focusPrev() {
 353         if (isXEmbedActive()) {
 354             xembedLog.fine("Requesting focus for the next component after embedder");
 355             postEvent(new InvocationEvent(target, new Runnable() {
 356                     public void run() {
 357                         KeyboardFocusManager.getCurrentKeyboardFocusManager().focusPreviousComponent(target);
 358                     }
 359                 }));
 360         } else {
 361             xembedLog.fine("XEmbed is not active - denying focus prev");
 362         }
 363     }
 364 
 365     void requestXEmbedFocus() {
 366         if (isXEmbedActive()) {
 367             xembedLog.fine("Requesting focus for client");
 368             postEvent(new InvocationEvent(target, new Runnable() {
 369                     public void run() {
 370                         target.requestFocus();
 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     }
 412 
 413     public void windowGainedFocus(WindowEvent e) {
 414         applicationActive = true;
 415         if (isXEmbedActive()) {
 416             xembedLog.fine("Sending WINDOW_ACTIVATE");
 417             xembed.sendMessage(xembed.handle, XEMBED_WINDOW_ACTIVATE);
 418         }
 419     }
 420 
 421     public void windowLostFocus(WindowEvent e) {
 422         applicationActive = false;
 423         if (isXEmbedActive()) {
 424             xembedLog.fine("Sending WINDOW_DEACTIVATE");
 425             xembed.sendMessage(xembed.handle, XEMBED_WINDOW_DEACTIVATE);
 426         }
 427     }
 428 
 429     void canvasFocusGained(FocusEvent e) {
 430         if (isXEmbedActive()) {
 431             xembedLog.fine("Forwarding FOCUS_GAINED");
 432             int flavor = XEMBED_FOCUS_CURRENT;
 433             if (e.getCause() == FocusEvent.Cause.TRAVERSAL_FORWARD) {
 434                 flavor = XEMBED_FOCUS_FIRST;
 435             } else if (e.getCause() == FocusEvent.Cause.TRAVERSAL_BACKWARD) {
 436                 flavor = XEMBED_FOCUS_LAST;
 437             }
 438             xembed.sendMessage(xembed.handle, XEMBED_FOCUS_IN, flavor, 0, 0);
 439         }
 440     }
 441 
 442     void canvasFocusLost(FocusEvent e) {
 443         if (isXEmbedActive() && !e.isTemporary()) {
 444             xembedLog.fine("Forwarding FOCUS_LOST");
 445             int num = 0;
 446             if (AccessController.doPrivileged(new GetBooleanAction("sun.awt.xembed.testing"))) {
 447                 Component opp = e.getOppositeComponent();
 448                 try {
 449                     num = Integer.parseInt(opp.getName());
 450                 } catch (NumberFormatException nfe) {
 451                 }
 452             }
 453             xembed.sendMessage(xembed.handle, XEMBED_FOCUS_OUT, num, 0, 0);
 454         }
 455     }
 456 
 457     static byte[] getBData(KeyEvent e) {
 458         return AWTAccessor.getAWTEventAccessor().getBData(e);
 459     }
 460 
 461     void forwardKeyEvent(KeyEvent e) {
 462         xembedLog.fine("Try to forward key event");
 463         byte[] bdata = getBData(e);
 464         long data = Native.toData(bdata);
 465         if (data == 0) {
 466             return;
 467         }
 468         try {
 469             XKeyEvent ke = new XKeyEvent(data);
 470             ke.set_window(xembed.handle);
 471             if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
 472                 xembedLog.fine("Forwarding native key event: " + ke);
 473             }
 474             XToolkit.awtLock();
 475             try {
 476                 XlibWrapper.XSendEvent(XToolkit.getDisplay(), xembed.handle, false, XConstants.NoEventMask, data);
 477             } finally {
 478                 XToolkit.awtUnlock();
 479             }
 480         } finally {
 481             XlibWrapper.unsafe.freeMemory(data);
 482         }
 483     }
 484 
 485 
 486     /**
 487      * Grab/ungrab key functionality is an unofficial API supported by
 488      * GTK.  Unfortunately, it doesn't support accelerator API, so,
 489      * since this is the ONLY shortcut-processing API available, we
 490      * must support it.  See XEmbed.NON_STANDARD_XEMBED_GTK_*
 491      * messages.  The format of these messages is as follows:
 492      * - request from client:
 493      * data[1] = NON_STANDARD_XEMBED_GTK_GRAB_KEY or NON_STANDARD_XEMBED_GTK_UNGRAB_KEY
 494      * data[3] = X keysym
 495      * data[4] = X modifiers
 496      *
 497      * - response from server (in case the grabbed key has been pressed):
 498      * forwarded XKeyEvent that matches keysym/modifiers pair
 499      */
 500     void grabKey(final long keysym, final long modifiers) {
 501         postEvent(new InvocationEvent(target, new Runnable() {
 502                 public void run() {
 503                     GrabbedKey grab = new GrabbedKey(keysym, modifiers);
 504                     if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
 505                         xembedLog.fine("Grabbing key: " + grab);
 506                     }
 507                     synchronized(GRAB_LOCK) {
 508                         grabbed_keys.add(grab);
 509                     }
 510                 }
 511             }));
 512     }
 513 
 514     void ungrabKey(final long keysym, final long modifiers) {
 515         postEvent(new InvocationEvent(target, new Runnable() {
 516                 public void run() {
 517                     GrabbedKey grab = new GrabbedKey(keysym, modifiers);
 518                     if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
 519                         xembedLog.fine("UnGrabbing key: " + grab);
 520                     }
 521                     synchronized(GRAB_LOCK) {
 522                         grabbed_keys.remove(grab);
 523                     }
 524                 }
 525             }));
 526     }
 527 
 528     void registerAccelerator(final long accel_id, final long keysym, final long modifiers) {
 529         postEvent(new InvocationEvent(target, new Runnable() {
 530                 public void run() {
 531                     AWTKeyStroke stroke = xembed.getKeyStrokeForKeySym(keysym, modifiers);
 532                     if (stroke != null) {
 533                         if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
 534                             xembedLog.fine("Registering accelerator " + accel_id + " for " + stroke);
 535                         }
 536                         synchronized(ACCEL_LOCK) {
 537                             accelerators.put(accel_id, stroke);
 538                             accel_lookup.put(stroke, accel_id);
 539                         }
 540                     }
 541                     propogateRegisterAccelerator(stroke);
 542                 }
 543             }));
 544     }
 545 
 546     void unregisterAccelerator(final long accel_id) {
 547         postEvent(new InvocationEvent(target, new Runnable() {
 548                 public void run() {
 549                     AWTKeyStroke stroke = null;
 550                     synchronized(ACCEL_LOCK) {
 551                         stroke = accelerators.get(accel_id);
 552                         if (stroke != null) {
 553                             if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
 554                                 xembedLog.fine("Unregistering accelerator: " + accel_id);
 555                             }
 556                             accelerators.remove(accel_id);
 557                             accel_lookup.remove(stroke); // FIXME: How about several accelerators with the same stroke?
 558                         }
 559                     }
 560                     propogateUnRegisterAccelerator(stroke);
 561                 }
 562             }));
 563     }
 564 
 565     void propogateRegisterAccelerator(AWTKeyStroke stroke) {
 566         // Find the top-level and see if it is XEmbed client. If so, ask him to
 567         // register the accelerator
 568         XWindowPeer parent = getToplevelXWindow();
 569         if (parent != null && parent instanceof XEmbeddedFramePeer) {
 570             XEmbeddedFramePeer embedded = (XEmbeddedFramePeer)parent;
 571             embedded.registerAccelerator(stroke);
 572         }
 573     }
 574 
 575     void propogateUnRegisterAccelerator(AWTKeyStroke stroke) {
 576         // Find the top-level and see if it is XEmbed client. If so, ask him to
 577         // register the accelerator
 578         XWindowPeer parent = getToplevelXWindow();
 579         if (parent != null && parent instanceof XEmbeddedFramePeer) {
 580             XEmbeddedFramePeer embedded = (XEmbeddedFramePeer)parent;
 581             embedded.unregisterAccelerator(stroke);
 582         }
 583     }
 584 
 585     public boolean postProcessKeyEvent(KeyEvent e) {
 586         // Processing events only if we are in the focused window but
 587         // we are not focus owner since otherwise we will get
 588         // duplicate shortcut events in the client - one is from
 589         // activate_accelerator, another from forwarded event
 590         // FIXME: This is probably an incompatibility, protocol
 591         // doesn't say anything about disable accelerators when client
 592         // is focused.
 593 
 594         XWindowPeer parent = getToplevelXWindow();
 595         if (parent == null || !((Window)parent.getTarget()).isFocused() || target.isFocusOwner()) {
 596             return false;
 597         }
 598 
 599         boolean result = false;
 600 
 601         if (xembedLog.isLoggable(PlatformLogger.Level.FINER)) {
 602             xembedLog.finer("Post-processing event " + e);
 603         }
 604 
 605         // Process ACCELERATORS
 606         AWTKeyStroke stroke = AWTKeyStroke.getAWTKeyStrokeForEvent(e);
 607         long accel_id = 0;
 608         boolean exists = false;
 609         synchronized(ACCEL_LOCK) {
 610             exists = accel_lookup.containsKey(stroke);
 611             if (exists) {
 612                 accel_id = accel_lookup.get(stroke).longValue();
 613             }
 614         }
 615         if (exists) {
 616             if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
 617                 xembedLog.fine("Activating accelerator " + accel_id);
 618             }
 619             xembed.sendMessage(xembed.handle, XEMBED_ACTIVATE_ACCELERATOR, accel_id, 0, 0); // FIXME: How about overloaded?
 620             result = true;
 621         }
 622 
 623         // Process Grabs, unofficial GTK feature
 624         exists = false;
 625         GrabbedKey key = new GrabbedKey(e);
 626         synchronized(GRAB_LOCK) {
 627             exists = grabbed_keys.contains(key);
 628         }
 629         if (exists) {
 630             if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
 631                 xembedLog.fine("Forwarding grabbed key " + e);
 632             }
 633             forwardKeyEvent(e);
 634             result = true;
 635         }
 636 
 637         return result;
 638     }
 639 
 640     public void modalityPushed(ModalityEvent ev) {
 641         xembed.sendMessage(xembed.handle, XEMBED_MODALITY_ON);
 642     }
 643 
 644     public void modalityPopped(ModalityEvent ev) {
 645         xembed.sendMessage(xembed.handle, XEMBED_MODALITY_OFF);
 646     }
 647 
 648     public void handleClientMessage(XEvent xev) {
 649         super.handleClientMessage(xev);
 650         XClientMessageEvent msg = xev.get_xclient();
 651         if (xembedLog.isLoggable(PlatformLogger.Level.FINER)) {
 652             xembedLog.finer("Client message to embedder: " + msg);
 653         }
 654         if (msg.get_message_type() == XEmbedHelper.XEmbed.getAtom()) {
 655             if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
 656                 xembedLog.fine(XEmbedHelper.XEmbedMessageToString(msg));
 657             }
 658         }
 659         if (isXEmbedActive()) {
 660             switch ((int)msg.get_data(1)) {
 661               case XEMBED_REQUEST_FOCUS:
 662                   requestXEmbedFocus();
 663                   break;
 664               case XEMBED_FOCUS_NEXT:
 665                   focusNext();
 666                   break;
 667               case XEMBED_FOCUS_PREV:
 668                   focusPrev();
 669                   break;
 670               case XEMBED_REGISTER_ACCELERATOR:
 671                   registerAccelerator(msg.get_data(2), msg.get_data(3), msg.get_data(4));
 672                   break;
 673               case XEMBED_UNREGISTER_ACCELERATOR:
 674                   unregisterAccelerator(msg.get_data(2));
 675                   break;
 676               case NON_STANDARD_XEMBED_GTK_GRAB_KEY:
 677                   grabKey(msg.get_data(3), msg.get_data(4));
 678                   break;
 679               case NON_STANDARD_XEMBED_GTK_UNGRAB_KEY:
 680                   ungrabKey(msg.get_data(3), msg.get_data(4));
 681                   break;
 682             }
 683         } else {
 684             xembedLog.finer("But XEmbed is not Active!");
 685         }
 686     }
 687 
 688     @SuppressWarnings("serial") // JDK-implementation class
 689     private static class XEmbedDropTarget extends DropTarget {
 690         public void addDropTargetListener(DropTargetListener dtl)
 691           throws TooManyListenersException {
 692             // Drop target listeners registered with this target will never be
 693             // notified, since all drag notifications are routed to the XEmbed
 694             // client. To avoid confusion we prohibit listeners registration
 695             // by throwing TooManyListenersException as if there is a listener
 696             // registered with this target already.
 697             throw new TooManyListenersException();
 698         }
 699     }
 700 
 701     public void setXEmbedDropTarget() {
 702         // Register a drop site on the top level.
 703         Runnable r = new Runnable() {
 704                 public void run() {
 705                     target.setDropTarget(new XEmbedDropTarget());
 706                 }
 707             };
 708         SunToolkit.executeOnEventHandlerThread(target, r);
 709     }
 710 
 711     public void removeXEmbedDropTarget() {
 712         // Unregister a drop site on the top level.
 713         Runnable r = new Runnable() {
 714                 public void run() {
 715                     if (target.getDropTarget() instanceof XEmbedDropTarget) {
 716                         target.setDropTarget(null);
 717                     }
 718                 }
 719             };
 720         SunToolkit.executeOnEventHandlerThread(target, r);
 721     }
 722 
 723     public boolean processXEmbedDnDEvent(long ctxt, int eventID) {
 724         if (xembedLog.isLoggable(PlatformLogger.Level.FINEST)) {
 725             xembedLog.finest("     Drop target=" + target.getDropTarget());
 726         }
 727         if (target.getDropTarget() instanceof XEmbedDropTarget) {
 728             AppContext appContext = XToolkit.targetToAppContext(getTarget());
 729             XDropTargetContextPeer peer =
 730                 XDropTargetContextPeer.getPeer(appContext);
 731             peer.forwardEventToEmbedded(xembed.handle, ctxt, eventID);
 732             return true;
 733         } else {
 734             return false;
 735         }
 736     }
 737 
 738     class XEmbedServer extends XEmbedHelper implements XEventDispatcher {
 739         long handle; // Handle to XEmbed client
 740         long version;
 741         long flags;
 742 
 743         boolean processXEmbedInfo() {
 744             long xembed_info_data = Native.allocateLongArray(2);
 745             try {
 746                 if (!XEmbedInfo.getAtomData(handle, xembed_info_data, 2)) {
 747                     // No more XEMBED_INFO? This is not XEmbed client!
 748                     // Unfortunately this is the initial state of the most clients
 749                     // FIXME: add 5-state processing
 750                     //childDestroyed();
 751                     xembedLog.finer("Unable to get XEMBED_INFO atom data");
 752                     return false;
 753                 }
 754                 version = Native.getCard32(xembed_info_data, 0);
 755                 flags = Native.getCard32(xembed_info_data, 1);
 756                 boolean new_mapped = (flags & XEMBED_MAPPED) != 0;
 757                 boolean currently_mapped = XlibUtil.getWindowMapState(handle) != XConstants.IsUnmapped;
 758                 if (new_mapped != currently_mapped) {
 759                     if (xembedLog.isLoggable(PlatformLogger.Level.FINER)) {
 760                         xembedLog.finer("Mapping state of the client has changed, old state: " + currently_mapped + ", new state: " + new_mapped);
 761                     }
 762                     if (new_mapped) {
 763                         XToolkit.awtLock();
 764                         try {
 765                             XlibWrapper.XMapWindow(XToolkit.getDisplay(), handle);
 766                         } finally {
 767                             XToolkit.awtUnlock();
 768                         }
 769                     } else {
 770                         XToolkit.awtLock();
 771                         try {
 772                             XlibWrapper.XUnmapWindow(XToolkit.getDisplay(), handle);
 773                         } finally {
 774                             XToolkit.awtUnlock();
 775                         }
 776                     }
 777                 } else {
 778                     if (xembedLog.isLoggable(PlatformLogger.Level.FINER)) {
 779                         xembedLog.finer("Mapping state didn't change, mapped: " + currently_mapped);
 780                     }
 781                 }
 782                 return true;
 783             } finally {
 784                 XlibWrapper.unsafe.freeMemory(xembed_info_data);
 785             }
 786         }
 787 
 788         public void handlePropertyNotify(XEvent xev) {
 789             if (isXEmbedActive()) {
 790                 XPropertyEvent ev = xev.get_xproperty();
 791                 if (xembedLog.isLoggable(PlatformLogger.Level.FINER)) {
 792                     xembedLog.finer("Property change on client: " + ev);
 793                 }
 794                 if (ev.get_atom() == XAtom.XA_WM_NORMAL_HINTS) {
 795                     childResized();
 796                 } else if (ev.get_atom() == XEmbedInfo.getAtom()) {
 797                     processXEmbedInfo();
 798                 } else if (ev.get_atom() ==
 799                            XDnDConstants.XA_XdndAware.getAtom()) {
 800                     XDropTargetRegistry.getRegistry().unregisterXEmbedClient(getWindow(),
 801                                                                              xembed.handle);
 802                     if (ev.get_state() == XConstants.PropertyNewValue) {
 803                         XDropTargetRegistry.getRegistry().registerXEmbedClient(getWindow(),
 804                                                                                 xembed.handle);
 805                     }
 806                 }
 807             } else {
 808                 xembedLog.finer("XEmbed is not active");
 809             }
 810         }
 811         void handleConfigureNotify(XEvent xev) {
 812             if (isXEmbedActive()) {
 813                 XConfigureEvent ev = xev.get_xconfigure();
 814                 if (xembedLog.isLoggable(PlatformLogger.Level.FINER)) {
 815                     xembedLog.finer("Bounds change on client: " + ev);
 816                 }
 817                 if (xev.get_xany().get_window() == handle) {
 818                     childResized();
 819                 }
 820             }
 821         }
 822         public void dispatchEvent(XEvent xev) {
 823             int type = xev.get_type();
 824             switch (type) {
 825               case XConstants.PropertyNotify:
 826                   handlePropertyNotify(xev);
 827                   break;
 828               case XConstants.ConfigureNotify:
 829                   handleConfigureNotify(xev);
 830                   break;
 831               case XConstants.ClientMessage:
 832                   handleClientMessage(xev);
 833                   break;
 834             }
 835         }
 836     }
 837 
 838     static class GrabbedKey {
 839         long keysym;
 840         long modifiers;
 841         GrabbedKey(long keysym, long modifiers) {
 842             this.keysym = keysym;
 843             this.modifiers = modifiers;
 844         }
 845 
 846         GrabbedKey(KeyEvent ev) {
 847             init(ev);
 848         }
 849 
 850         private void init(KeyEvent e) {
 851             byte[] bdata = getBData(e);
 852             long data = Native.toData(bdata);
 853             if (data == 0) {
 854                 return;
 855             }
 856             try {
 857                 XToolkit.awtLock();
 858                 try {
 859                     keysym = XWindow.getKeySymForAWTKeyCode(e.getKeyCode());
 860                 } finally {
 861                     XToolkit.awtUnlock();
 862                 }
 863                 XKeyEvent ke = new XKeyEvent(data);
 864 
 865                 // We recognize only these masks
 866                 modifiers = ke.get_state() & (XConstants.ShiftMask | XConstants.ControlMask | XConstants.LockMask);
 867                 if (xembedLog.isLoggable(PlatformLogger.Level.FINEST)) {
 868                     xembedLog.finest("Mapped " + e + " to " + this);
 869                 }
 870             } finally {
 871                 XlibWrapper.unsafe.freeMemory(data);
 872             }
 873         }
 874 
 875         public int hashCode() {
 876             return (int)keysym & 0xFFFFFFFF;
 877         }
 878 
 879         public boolean equals(Object o) {
 880             if (!(o instanceof GrabbedKey)) {
 881                 return false;
 882             }
 883             GrabbedKey key = (GrabbedKey)o;
 884             return (keysym == key.keysym && modifiers == key.modifiers);
 885         }
 886 
 887         public String toString() {
 888             return "Key combination[keysym=" + keysym + ", mods=" + modifiers + "]";
 889         }
 890     }
 891 }