1 /*
   2  * Copyright (c) 2003, 2010, 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 java.util.logging.*;
  34 import java.util.*;
  35 import static sun.awt.X11.XEmbedHelper.*;
  36 
  37 import java.security.AccessController;
  38 import sun.security.action.GetBooleanAction;
  39 
  40 public class XEmbedCanvasPeer extends XCanvasPeer implements WindowFocusListener, KeyEventPostProcessor, ModalityListener, WindowIDProvider {
  41     private static final Logger xembedLog = Logger.getLogger("sun.awt.X11.xembed.XEmbedCanvasPeer");
  42 
  43     boolean applicationActive; // Whether the application is active(has focus)
  44     XEmbedServer xembed = new XEmbedServer(); // Helper object, contains XEmbed intrinsics
  45     Map<Long, AWTKeyStroke> accelerators = new HashMap<Long, AWTKeyStroke>(); // Maps accelerator ID into AWTKeyStroke
  46     Map<AWTKeyStroke, Long> accel_lookup = new HashMap<AWTKeyStroke, Long>(); // Maps AWTKeyStroke into accelerator ID
  47     Set<GrabbedKey> grabbed_keys = new HashSet<GrabbedKey>(); // A set of keys grabbed by client
  48     Object ACCEL_LOCK = accelerators; // Lock object for working with accelerators;
  49     Object GRAB_LOCK = grabbed_keys; // Lock object for working with keys grabbed by client
  50 
  51     XEmbedCanvasPeer() {}
  52 
  53     XEmbedCanvasPeer(XCreateWindowParams params) {
  54         super(params);
  55     }
  56 
  57     XEmbedCanvasPeer(Component target) {
  58         super(target);
  59     }
  60 
  61     protected void postInit(XCreateWindowParams params) {
  62         super.postInit(params);
  63 
  64         installActivateListener();
  65         installAcceleratorListener();
  66         installModalityListener();
  67 
  68         // XEmbed canvas should be non-traversable.
  69         // FIXME: Probably should be removed and enforced setting of it by the users
  70         target.setFocusTraversalKeysEnabled(false);
  71     }
  72 
  73     protected void preInit(XCreateWindowParams params) {
  74         super.preInit(params);
  75 
  76         params.put(EVENT_MASK,
  77                    KeyPressMask       | KeyReleaseMask
  78                    | FocusChangeMask  | ButtonPressMask | ButtonReleaseMask
  79                    | EnterWindowMask  | LeaveWindowMask | PointerMotionMask
  80                    | ButtonMotionMask | ExposureMask    | StructureNotifyMask | SubstructureNotifyMask);
  81 
  82     }
  83 
  84     void installModalityListener() {
  85         ((SunToolkit)Toolkit.getDefaultToolkit()).addModalityListener(this);
  86     }
  87 
  88     void deinstallModalityListener() {
  89         ((SunToolkit)Toolkit.getDefaultToolkit()).removeModalityListener(this);
  90     }
  91 
  92     void installAcceleratorListener() {
  93         KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventPostProcessor(this);
  94     }
  95 
  96     void deinstallAcceleratorListener() {
  97         KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventPostProcessor(this);
  98     }
  99 
 100     void installActivateListener() {
 101         // FIXME: should watch for hierarchy changes
 102         Window toplevel = getTopLevel(target);
 103         if (toplevel != null) {
 104             toplevel.addWindowFocusListener(this);
 105             applicationActive = toplevel.isFocused();
 106         }
 107     }
 108 
 109     void deinstallActivateListener() {
 110         Window toplevel = getTopLevel(target);
 111         if (toplevel != null) {
 112             toplevel.removeWindowFocusListener(this);
 113         }
 114     }
 115 
 116     boolean isXEmbedActive() {
 117         return xembed.handle != 0;
 118     }
 119 
 120     boolean isApplicationActive() {
 121         return applicationActive;
 122     }
 123 
 124     void initDispatching() {
 125         if (xembedLog.isLoggable(Level.FINE)) xembedLog.fine("Init embedding for " + Long.toHexString(xembed.handle));
 126         XToolkit.awtLock();
 127         try {
 128             XToolkit.addEventDispatcher(xembed.handle, xembed);
 129             XlibWrapper.XSelectInput(XToolkit.getDisplay(), xembed.handle,
 130                                      XlibWrapper.StructureNotifyMask | XlibWrapper.PropertyChangeMask);
 131 
 132             XDropTargetRegistry.getRegistry().registerXEmbedClient(getWindow(), xembed.handle);
 133         } finally {
 134             XToolkit.awtUnlock();
 135         }
 136         xembed.processXEmbedInfo();
 137 
 138         notifyChildEmbedded();
 139     }
 140 
 141     void endDispatching() {
 142         xembedLog.fine("End dispatching for " + Long.toHexString(xembed.handle));
 143         XToolkit.awtLock();
 144         try {
 145             XDropTargetRegistry.getRegistry().unregisterXEmbedClient(getWindow(), xembed.handle);
 146             // We can't deselect input since someone else might be interested in it
 147             XToolkit.removeEventDispatcher(xembed.handle, xembed);
 148         } finally {
 149             XToolkit.awtUnlock();
 150         }
 151     }
 152 
 153     void embedChild(long child) {
 154         if (xembed.handle != 0) {
 155             detachChild();
 156         }
 157         xembed.handle = child;
 158         initDispatching();
 159     }
 160 
 161     void childDestroyed() {
 162         xembedLog.fine("Child " + Long.toHexString(xembed.handle) + " has self-destroyed.");
 163         endDispatching();
 164         xembed.handle = 0;
 165     }
 166 
 167     public void handleEvent(AWTEvent e) {
 168         super.handleEvent(e);
 169         if (isXEmbedActive()) {
 170             switch (e.getID()) {
 171               case FocusEvent.FOCUS_GAINED:
 172                   canvasFocusGained((FocusEvent)e);
 173                   break;
 174               case FocusEvent.FOCUS_LOST:
 175                   canvasFocusLost((FocusEvent)e);
 176                   break;
 177               case KeyEvent.KEY_PRESSED:
 178               case KeyEvent.KEY_RELEASED:
 179                   if (!((InputEvent)e).isConsumed()) {
 180                       forwardKeyEvent((KeyEvent)e);
 181                   }
 182                   break;
 183             }
 184         }
 185     }
 186 
 187     public void dispatchEvent(XEvent ev) {
 188         super.dispatchEvent(ev);
 189         switch (ev.get_type()) {
 190           case CreateNotify:
 191               XCreateWindowEvent cr = ev.get_xcreatewindow();
 192               if (xembedLog.isLoggable(Level.FINEST)) {
 193                   xembedLog.finest("Message on embedder: " + cr);
 194               }
 195               if (xembedLog.isLoggable(Level.FINER)) {
 196                   xembedLog.finer("Create notify for parent " + Long.toHexString(cr.get_parent()) +
 197                                   ", window " + Long.toHexString(cr.get_window()));
 198               }
 199               embedChild(cr.get_window());
 200               break;
 201           case DestroyNotify:
 202               XDestroyWindowEvent dn = ev.get_xdestroywindow();
 203               if (xembedLog.isLoggable(Level.FINEST)) {
 204                   xembedLog.finest("Message on embedder: " + dn);
 205               }
 206               if (xembedLog.isLoggable(Level.FINER)) {
 207                   xembedLog.finer("Destroy notify for parent: " + dn);
 208               }
 209               childDestroyed();
 210               break;
 211           case ReparentNotify:
 212               XReparentEvent rep = ev.get_xreparent();
 213               if (xembedLog.isLoggable(Level.FINEST)) {
 214                   xembedLog.finest("Message on embedder: " + rep);
 215               }
 216               if (xembedLog.isLoggable(Level.FINER)) {
 217                   xembedLog.finer("Reparent notify for parent " + Long.toHexString(rep.get_parent()) +
 218                                   ", window " + Long.toHexString(rep.get_window()) +
 219                                   ", event " + Long.toHexString(rep.get_event()));
 220               }
 221               if (rep.get_parent() == getWindow()) {
 222                   // Reparented into us - embed it
 223                   embedChild(rep.get_window());
 224               } else {
 225                   // Reparented out of us - detach it
 226                   childDestroyed();
 227               }
 228               break;
 229         }
 230     }
 231 
 232     public Dimension getPreferredSize() {
 233         if (isXEmbedActive()) {
 234             XToolkit.awtLock();
 235             try {
 236                 long p_hints = XlibWrapper.XAllocSizeHints();
 237                 XSizeHints hints = new XSizeHints(p_hints);
 238                 XlibWrapper.XGetWMNormalHints(XToolkit.getDisplay(), xembed.handle, p_hints, XlibWrapper.larg1);
 239                 Dimension res = new Dimension(hints.get_width(), hints.get_height());
 240                 XlibWrapper.XFree(p_hints);
 241                 return res;
 242             } finally {
 243                 XToolkit.awtUnlock();
 244             }
 245         } else {
 246             return super.getPreferredSize();
 247         }
 248     }
 249     public Dimension getMinimumSize() {
 250         if (isXEmbedActive()) {
 251             XToolkit.awtLock();
 252             try {
 253                 long p_hints = XlibWrapper.XAllocSizeHints();
 254                 XSizeHints hints = new XSizeHints(p_hints);
 255                 XlibWrapper.XGetWMNormalHints(XToolkit.getDisplay(), xembed.handle, p_hints, XlibWrapper.larg1);
 256                 Dimension res = new Dimension(hints.get_min_width(), hints.get_min_height());
 257                 XlibWrapper.XFree(p_hints);
 258                 return res;
 259             } finally {
 260                 XToolkit.awtUnlock();
 261             }
 262         } else {
 263             return super.getMinimumSize();
 264         }
 265     }
 266     public void dispose() {
 267         if (isXEmbedActive()) {
 268             detachChild();
 269         }
 270         deinstallActivateListener();
 271         deinstallModalityListener();
 272         deinstallAcceleratorListener();
 273 
 274         // BUG: Focus traversal doesn't become enabled after the one round of embedding
 275         //target.setFocusTraversalKeysEnabled(true);
 276 
 277         super.dispose();
 278     }
 279 
 280     // Focusable is true in order to enable focus traversal through this Canvas
 281     public boolean isFocusable() {
 282         return true;
 283     }
 284 
 285     Window getTopLevel(Component comp) {
 286         while (comp != null && !(comp instanceof Window)) {
 287             comp = comp.getParent();
 288         }
 289         return (Window)comp;
 290     }
 291 
 292     Rectangle getClientBounds() {
 293         XToolkit.awtLock();
 294         try {
 295             XWindowAttributes wattr = new XWindowAttributes();
 296             try {
 297                 XToolkit.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 298                 int status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
 299                                                               xembed.handle, wattr.pData);
 300 
 301                 XToolkit.RESTORE_XERROR_HANDLER();
 302 
 303                 if (status == 0 ||
 304                     (XToolkit.saved_error != null &&
 305                      XToolkit.saved_error.get_error_code() != XlibWrapper.Success)) {
 306                     return null;
 307                 }
 308 
 309                 return new Rectangle(wattr.get_x(), wattr.get_y(), wattr.get_width(), wattr.get_height());
 310             } finally {
 311                 wattr.dispose();
 312             }
 313         } finally {
 314             XToolkit.awtUnlock();
 315         }
 316     }
 317 
 318     void childResized() {
 319         if (xembedLog.isLoggable(Level.FINER)) {
 320             Rectangle bounds = getClientBounds();
 321             xembedLog.finer("Child resized: " + bounds);
 322             // It is not required to update embedder's size when client size changes
 323             // However, since there is no any means to get client size it seems to be the
 324             // only way to provide it. However, it contradicts with Java layout concept -
 325             // so it is disabled for now.
 326 //             Rectangle my_bounds = getBounds();
 327 //             setBounds(my_bounds.x, my_bounds.y, bounds.width, bounds.height, SET_BOUNDS);
 328         }
 329         XToolkit.postEvent(XToolkit.targetToAppContext(target), new ComponentEvent(target, ComponentEvent.COMPONENT_RESIZED));
 330     }
 331 
 332     void focusNext() {
 333         if (isXEmbedActive()) {
 334             xembedLog.fine("Requesting focus for the next component after embedder");
 335             postEvent(new InvocationEvent(target, new Runnable() {
 336                     public void run() {
 337                         KeyboardFocusManager.getCurrentKeyboardFocusManager().focusNextComponent(target);
 338                     }
 339                 }));
 340         } else {
 341             xembedLog.fine("XEmbed is not active - denying focus next");
 342         }
 343     }
 344 
 345     void focusPrev() {
 346         if (isXEmbedActive()) {
 347             xembedLog.fine("Requesting focus for the next component after embedder");
 348             postEvent(new InvocationEvent(target, new Runnable() {
 349                     public void run() {
 350                         KeyboardFocusManager.getCurrentKeyboardFocusManager().focusPreviousComponent(target);
 351                     }
 352                 }));
 353         } else {
 354             xembedLog.fine("XEmbed is not active - denying focus prev");
 355         }
 356     }
 357 
 358     void requestXEmbedFocus() {
 359         if (isXEmbedActive()) {
 360             xembedLog.fine("Requesting focus for client");
 361             postEvent(new InvocationEvent(target, new Runnable() {
 362                     public void run() {
 363                         target.requestFocus();
 364                     }
 365                 }));
 366         } else {
 367             xembedLog.fine("XEmbed is not active - denying request focus");
 368         }
 369     }
 370 
 371     void notifyChildEmbedded() {
 372         xembed.sendMessage(xembed.handle, XEMBED_EMBEDDED_NOTIFY, getWindow(), Math.min(xembed.version, XEMBED_VERSION), 0);
 373         if (isApplicationActive()) {
 374             xembedLog.fine("Sending WINDOW_ACTIVATE during initialization");
 375             xembed.sendMessage(xembed.handle, XEMBED_WINDOW_ACTIVATE);
 376             if (hasFocus()) {
 377                 xembedLog.fine("Sending FOCUS_GAINED during initialization");
 378                 xembed.sendMessage(xembed.handle, XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT, 0, 0);
 379             }
 380         }
 381     }
 382 
 383     void detachChild() {
 384         if (xembedLog.isLoggable(Level.FINE)) xembedLog.fine("Detaching child " + Long.toHexString(xembed.handle));
 385         /**
 386          *  XEmbed specification:
 387          *  "The embedder can unmap the client and reparent the client window to the root window. If the
 388          *  client receives an ReparentNotify event, it should check the parent field of the XReparentEvent
 389          *  structure. If this is the root window of the window's screen, then the protocol is finished and
 390          *  there is no further interaction. If it is a window other than the root window, then the protocol
 391          *  continues with the new parent acting as the embedder window."
 392          */
 393         XToolkit.awtLock();
 394         try {
 395             XlibWrapper.XUnmapWindow(XToolkit.getDisplay(), xembed.handle);
 396             XlibWrapper.XReparentWindow(XToolkit.getDisplay(), xembed.handle, XToolkit.getDefaultRootWindow(), 0, 0);
 397         } finally {
 398             XToolkit.awtUnlock();
 399         }
 400         endDispatching();
 401         xembed.handle = 0;
 402     }
 403 
 404     public void windowGainedFocus(WindowEvent e) {
 405         applicationActive = true;
 406         if (isXEmbedActive()) {
 407             xembedLog.fine("Sending WINDOW_ACTIVATE");
 408             xembed.sendMessage(xembed.handle, XEMBED_WINDOW_ACTIVATE);
 409         }
 410     }
 411 
 412     public void windowLostFocus(WindowEvent e) {
 413         applicationActive = false;
 414         if (isXEmbedActive()) {
 415             xembedLog.fine("Sending WINDOW_DEACTIVATE");
 416             xembed.sendMessage(xembed.handle, XEMBED_WINDOW_DEACTIVATE);
 417         }
 418     }
 419 
 420     void canvasFocusGained(FocusEvent e) {
 421         if (isXEmbedActive()) {
 422             xembedLog.fine("Forwarding FOCUS_GAINED");
 423             int flavor = XEMBED_FOCUS_CURRENT;
 424             if (e instanceof CausedFocusEvent) {
 425                 CausedFocusEvent ce = (CausedFocusEvent)e;
 426                 if (ce.getCause() == CausedFocusEvent.Cause.TRAVERSAL_FORWARD) {
 427                     flavor = XEMBED_FOCUS_FIRST;
 428                 } else if (ce.getCause() == CausedFocusEvent.Cause.TRAVERSAL_BACKWARD) {
 429                     flavor = XEMBED_FOCUS_LAST;
 430                 }
 431             }
 432             xembed.sendMessage(xembed.handle, XEMBED_FOCUS_IN, flavor, 0, 0);
 433         }
 434     }
 435 
 436     void canvasFocusLost(FocusEvent e) {
 437         if (isXEmbedActive() && !e.isTemporary()) {
 438             xembedLog.fine("Forwarding FOCUS_LOST");
 439             int num = 0;
 440             if (AccessController.doPrivileged(new GetBooleanAction("sun.awt.xembed.testing"))) {
 441                 Component opp = e.getOppositeComponent();
 442                 try {
 443                     num = Integer.parseInt(opp.getName());
 444                 } catch (NumberFormatException nfe) {
 445                 }
 446             }
 447             xembed.sendMessage(xembed.handle, XEMBED_FOCUS_OUT, num, 0, 0);
 448         }
 449     }
 450 
 451     static byte[] getBData(KeyEvent e) {
 452         return AWTAccessor.getAWTEventAccessor().getBData(e);
 453     }
 454 
 455     void forwardKeyEvent(KeyEvent e) {
 456         xembedLog.fine("Try to forward key event");
 457         byte[] bdata = getBData(e);
 458         long data = Native.toData(bdata);
 459         if (data == 0) {
 460             return;
 461         }
 462         try {
 463             XKeyEvent ke = new XKeyEvent(data);
 464             ke.set_window(xembed.handle);
 465             if (xembedLog.isLoggable(Level.FINE)) xembedLog.fine("Forwarding native key event: " + ke);
 466             XToolkit.awtLock();
 467             try {
 468                 XlibWrapper.XSendEvent(XToolkit.getDisplay(), xembed.handle, false, XlibWrapper.NoEventMask, data);
 469             } finally {
 470                 XToolkit.awtUnlock();
 471             }
 472         } finally {
 473             XlibWrapper.unsafe.freeMemory(data);
 474         }
 475     }
 476 
 477 
 478     /**
 479      * Grab/ungrab key functionality is an unofficial API supported by
 480      * GTK.  Unfortunately, it doesn't support accelerator API, so,
 481      * since this is the ONLY shortcut-processing API available, we
 482      * must support it.  See XEmbed.NON_STANDARD_XEMBED_GTK_*
 483      * messages.  The format of these messages is as follows:
 484      * - request from client:
 485      * data[1] = NON_STANDARD_XEMBED_GTK_GRAB_KEY or NON_STANDARD_XEMBED_GTK_UNGRAB_KEY
 486      * data[3] = X keysym
 487      * data[4] = X modifiers
 488      *
 489      * - response from server (in case the grabbed key has been pressed):
 490      * forwarded XKeyEvent that matches keysym/modifiers pair
 491      */
 492     void grabKey(final long keysym, final long modifiers) {
 493         postEvent(new InvocationEvent(target, new Runnable() {
 494                 public void run() {
 495                     GrabbedKey grab = new GrabbedKey(keysym, modifiers);
 496                     if (xembedLog.isLoggable(Level.FINE)) xembedLog.fine("Grabbing key: " + grab);
 497                     synchronized(GRAB_LOCK) {
 498                         grabbed_keys.add(grab);
 499                     }
 500                 }
 501             }));
 502     }
 503 
 504     void ungrabKey(final long keysym, final long modifiers) {
 505         postEvent(new InvocationEvent(target, new Runnable() {
 506                 public void run() {
 507                     GrabbedKey grab = new GrabbedKey(keysym, modifiers);
 508                     if (xembedLog.isLoggable(Level.FINE)) xembedLog.fine("UnGrabbing key: " + grab);
 509                     synchronized(GRAB_LOCK) {
 510                         grabbed_keys.remove(grab);
 511                     }
 512                 }
 513             }));
 514     }
 515 
 516     void registerAccelerator(final long accel_id, final long keysym, final long modifiers) {
 517         postEvent(new InvocationEvent(target, new Runnable() {
 518                 public void run() {
 519                     AWTKeyStroke stroke = xembed.getKeyStrokeForKeySym(keysym, modifiers);
 520                     if (stroke != null) {
 521                         if (xembedLog.isLoggable(Level.FINE)) xembedLog.fine("Registering accelerator " + accel_id + " for " + stroke);
 522                         synchronized(ACCEL_LOCK) {
 523                             accelerators.put(accel_id, stroke);
 524                             accel_lookup.put(stroke, accel_id);
 525                         }
 526                     }
 527                     propogateRegisterAccelerator(stroke);
 528                 }
 529             }));
 530     }
 531 
 532     void unregisterAccelerator(final long accel_id) {
 533         postEvent(new InvocationEvent(target, new Runnable() {
 534                 public void run() {
 535                     AWTKeyStroke stroke = null;
 536                     synchronized(ACCEL_LOCK) {
 537                         stroke = accelerators.get(accel_id);
 538                         if (stroke != null) {
 539                             if (xembedLog.isLoggable(Level.FINE)) xembedLog.fine("Unregistering accelerator: " + accel_id);
 540                             accelerators.remove(accel_id);
 541                             accel_lookup.remove(stroke); // FIXME: How about several accelerators with the same stroke?
 542                         }
 543                     }
 544                     propogateUnRegisterAccelerator(stroke);
 545                 }
 546             }));
 547     }
 548 
 549     void propogateRegisterAccelerator(AWTKeyStroke stroke) {
 550         // Find the top-level and see if it is XEmbed client. If so, ask him to
 551         // register the accelerator
 552         XWindowPeer parent = getToplevelXWindow();
 553         if (parent != null && parent instanceof XEmbeddedFramePeer) {
 554             XEmbeddedFramePeer embedded = (XEmbeddedFramePeer)parent;
 555             embedded.registerAccelerator(stroke);
 556         }
 557     }
 558 
 559     void propogateUnRegisterAccelerator(AWTKeyStroke stroke) {
 560         // Find the top-level and see if it is XEmbed client. If so, ask him to
 561         // register the accelerator
 562         XWindowPeer parent = getToplevelXWindow();
 563         if (parent != null && parent instanceof XEmbeddedFramePeer) {
 564             XEmbeddedFramePeer embedded = (XEmbeddedFramePeer)parent;
 565             embedded.unregisterAccelerator(stroke);
 566         }
 567     }
 568 
 569     public boolean postProcessKeyEvent(KeyEvent e) {
 570         // Processing events only if we are in the focused window but
 571         // we are not focus owner since otherwise we will get
 572         // duplicate shortcut events in the client - one is from
 573         // activate_accelerator, another from forwarded event
 574         // FIXME: This is probably an incompatibility, protocol
 575         // doesn't say anything about disable accelerators when client
 576         // is focused.
 577 
 578         XWindowPeer parent = getToplevelXWindow();
 579         if (parent == null || !((Window)parent.getTarget()).isFocused() || target.isFocusOwner()) {
 580             return false;
 581         }
 582 
 583         boolean result = false;
 584 
 585         if (xembedLog.isLoggable(Level.FINER)) xembedLog.finer("Post-processing event " + e);
 586 
 587         // Process ACCELERATORS
 588         AWTKeyStroke stroke = AWTKeyStroke.getAWTKeyStrokeForEvent(e);
 589         long accel_id = 0;
 590         boolean exists = false;
 591         synchronized(ACCEL_LOCK) {
 592             exists = accel_lookup.containsKey(stroke);
 593             if (exists) {
 594                 accel_id = accel_lookup.get(stroke).longValue();
 595             }
 596         }
 597         if (exists) {
 598             if (xembedLog.isLoggable(Level.FINE)) xembedLog.fine("Activating accelerator " + accel_id);
 599             xembed.sendMessage(xembed.handle, XEMBED_ACTIVATE_ACCELERATOR, accel_id, 0, 0); // FIXME: How about overloaded?
 600             result = true;
 601         }
 602 
 603         // Process Grabs, unofficial GTK feature
 604         exists = false;
 605         GrabbedKey key = new GrabbedKey(e);
 606         synchronized(GRAB_LOCK) {
 607             exists = grabbed_keys.contains(key);
 608         }
 609         if (exists) {
 610             if (xembedLog.isLoggable(Level.FINE)) xembedLog.fine("Forwarding grabbed key " + e);
 611             forwardKeyEvent(e);
 612             result = true;
 613         }
 614 
 615         return result;
 616     }
 617 
 618     public void modalityPushed(ModalityEvent ev) {
 619         xembed.sendMessage(xembed.handle, XEMBED_MODALITY_ON);
 620     }
 621 
 622     public void modalityPopped(ModalityEvent ev) {
 623         xembed.sendMessage(xembed.handle, XEMBED_MODALITY_OFF);
 624     }
 625 
 626     public void handleClientMessage(XEvent xev) {
 627         super.handleClientMessage(xev);
 628         XClientMessageEvent msg = xev.get_xclient();
 629         if (xembedLog.isLoggable(Level.FINER)) xembedLog.finer("Client message to embedder: " + msg);
 630         if (msg.get_message_type() == xembed.XEmbed.getAtom()) {
 631             if (xembedLog.isLoggable(Level.FINE)) xembedLog.fine(xembed.XEmbedMessageToString(msg));
 632         }
 633         if (isXEmbedActive()) {
 634             switch ((int)msg.get_data(1)) {
 635               case _SUN_XEMBED_START:
 636                   // Child has finished initialization and waits for notify
 637                   xembed.processXEmbedInfo();
 638 
 639                   notifyChildEmbedded();
 640                   break;
 641               case XEMBED_REQUEST_FOCUS:
 642                   requestXEmbedFocus();
 643                   break;
 644               case XEMBED_FOCUS_NEXT:
 645                   focusNext();
 646                   break;
 647               case XEMBED_FOCUS_PREV:
 648                   focusPrev();
 649                   break;
 650               case XEMBED_REGISTER_ACCELERATOR:
 651                   registerAccelerator(msg.get_data(2), msg.get_data(3), msg.get_data(4));
 652                   break;
 653               case XEMBED_UNREGISTER_ACCELERATOR:
 654                   unregisterAccelerator(msg.get_data(2));
 655                   break;
 656               case NON_STANDARD_XEMBED_GTK_GRAB_KEY:
 657                   grabKey(msg.get_data(3), msg.get_data(4));
 658                   break;
 659               case NON_STANDARD_XEMBED_GTK_UNGRAB_KEY:
 660                   ungrabKey(msg.get_data(3), msg.get_data(4));
 661                   break;
 662             }
 663         } else {
 664             xembedLog.finer("But XEmbed is not Active!");
 665         }
 666     }
 667 
 668     private static class XEmbedDropTarget extends DropTarget {
 669         public void addDropTargetListener(DropTargetListener dtl)
 670           throws TooManyListenersException {
 671             // Drop target listeners registered with this target will never be
 672             // notified, since all drag notifications are routed to the XEmbed
 673             // client. To avoid confusion we prohibit listeners registration
 674             // by throwing TooManyListenersException as if there is a listener
 675             // registered with this target already.
 676             throw new TooManyListenersException();
 677         }
 678     }
 679 
 680     public void setXEmbedDropTarget() {
 681         // Register a drop site on the top level.
 682         Runnable r = new Runnable() {
 683                 public void run() {
 684                     target.setDropTarget(new XEmbedDropTarget());
 685                 }
 686             };
 687         SunToolkit.executeOnEventHandlerThread(target, r);
 688     }
 689 
 690     public void removeXEmbedDropTarget() {
 691         // Unregister a drop site on the top level.
 692         Runnable r = new Runnable() {
 693                 public void run() {
 694                     if (target.getDropTarget() instanceof XEmbedDropTarget) {
 695                         target.setDropTarget(null);
 696                     }
 697                 }
 698             };
 699         SunToolkit.executeOnEventHandlerThread(target, r);
 700     }
 701 
 702     public boolean processXEmbedDnDEvent(long ctxt, int eventID) {
 703         if (xembedLog.isLoggable(Level.FINEST)) {
 704             xembedLog.finest("     Drop target=" + target.getDropTarget());
 705         }
 706         if (target.getDropTarget() instanceof XEmbedDropTarget) {
 707             AppContext appContext = XToolkit.targetToAppContext(getTarget());
 708             XDropTargetContextPeer peer =
 709                 XDropTargetContextPeer.getPeer(appContext);
 710             peer.forwardEventToEmbedded(xembed.handle, ctxt, eventID);
 711             return true;
 712         } else {
 713             return false;
 714         }
 715     }
 716 
 717     class XEmbedServer extends XEmbedHelper implements XEventDispatcher {
 718         long handle; // Handle to XEmbed client
 719         long version;
 720         long flags;
 721 
 722         boolean processXEmbedInfo() {
 723             long xembed_info_data = Native.allocateLongArray(2);
 724             try {
 725                 if (!XEmbedInfo.getAtomData(handle, xembed_info_data, 2)) {
 726                     // No more XEMBED_INFO? This is not XEmbed client!
 727                     // Unfortunately this is the initial state of the most clients
 728                     // FIXME: add 5-state processing
 729                     //childDestroyed();
 730                     xembedLog.finer("Unable to get XEMBED_INFO atom data");
 731                     return false;
 732                 }
 733                 version = Native.getCard32(xembed_info_data, 0);
 734                 flags = Native.getCard32(xembed_info_data, 1);
 735                 boolean new_mapped = (flags & XEMBED_MAPPED) != 0;
 736                 boolean currently_mapped = XlibUtil.getWindowMapState(handle) != XlibWrapper.IsUnmapped;
 737                 if (new_mapped != currently_mapped) {
 738                     if (xembedLog.isLoggable(Level.FINER))
 739                         xembedLog.fine("Mapping state of the client has changed, old state: " + currently_mapped + ", new state: " + new_mapped);
 740                     if (new_mapped) {
 741                         XToolkit.awtLock();
 742                         try {
 743                             XlibWrapper.XMapWindow(XToolkit.getDisplay(), handle);
 744                         } finally {
 745                             XToolkit.awtUnlock();
 746                         }
 747                     } else {
 748                         XToolkit.awtLock();
 749                         try {
 750                             XlibWrapper.XUnmapWindow(XToolkit.getDisplay(), handle);
 751                         } finally {
 752                             XToolkit.awtUnlock();
 753                         }
 754                     }
 755                 } else {
 756                     xembedLog.finer("Mapping state didn't change, mapped: " + currently_mapped);
 757                 }
 758                 return true;
 759             } finally {
 760                 XlibWrapper.unsafe.freeMemory(xembed_info_data);
 761             }
 762         }
 763 
 764         public void handlePropertyNotify(XEvent xev) {
 765             if (isXEmbedActive()) {
 766                 XPropertyEvent ev = xev.get_xproperty();
 767                 if (xembedLog.isLoggable(Level.FINER)) xembedLog.finer("Property change on client: " + ev);
 768                 if (ev.get_atom() == XAtom.XA_WM_NORMAL_HINTS) {
 769                     childResized();
 770                 } else if (ev.get_atom() == XEmbedInfo.getAtom()) {
 771                     processXEmbedInfo();
 772                 } else if (ev.get_atom() ==
 773                            XDnDConstants.XA_XdndAware.getAtom()) {
 774                     XDropTargetRegistry.getRegistry().unregisterXEmbedClient(getWindow(),
 775                                                                              xembed.handle);
 776                     if (ev.get_state() == XConstants.PropertyNewValue) {
 777                         XDropTargetRegistry.getRegistry().registerXEmbedClient(getWindow(),
 778                                                                                 xembed.handle);
 779                     }
 780                 }
 781             } else {
 782                 xembedLog.finer("XEmbed is not active");
 783             }
 784         }
 785         void handleConfigureNotify(XEvent xev) {
 786             if (isXEmbedActive()) {
 787                 XConfigureEvent ev = xev.get_xconfigure();
 788                 if (xembedLog.isLoggable(Level.FINER)) xembedLog.finer("Bounds change on client: " + ev);
 789                 if (xev.get_xany().get_window() == handle) {
 790                     childResized();
 791                 }
 792             }
 793         }
 794         public void dispatchEvent(XEvent xev) {
 795             int type = xev.get_type();
 796             switch (type) {
 797               case PropertyNotify:
 798                   handlePropertyNotify(xev);
 799                   break;
 800               case ConfigureNotify:
 801                   handleConfigureNotify(xev);
 802                   break;
 803               case ClientMessage:
 804                   handleClientMessage(xev);
 805                   break;
 806             }
 807         }
 808     }
 809 
 810     static class GrabbedKey {
 811         long keysym;
 812         long modifiers;
 813         GrabbedKey(long keysym, long modifiers) {
 814             this.keysym = keysym;
 815             this.modifiers = modifiers;
 816         }
 817 
 818         GrabbedKey(KeyEvent ev) {
 819             init(ev);
 820         }
 821 
 822         private void init(KeyEvent e) {
 823             byte[] bdata = getBData(e);
 824             long data = Native.toData(bdata);
 825             if (data == 0) {
 826                 return;
 827             }
 828             try {
 829                 XToolkit.awtLock();
 830                 try {
 831                     keysym = XWindow.getKeySymForAWTKeyCode(e.getKeyCode());
 832                 } finally {
 833                     XToolkit.awtUnlock();
 834                 }
 835                 XKeyEvent ke = new XKeyEvent(data);
 836 
 837                 // We recognize only these masks
 838                 modifiers = ke.get_state() & (ShiftMask | ControlMask | LockMask);
 839                 if (xembedLog.isLoggable(Level.FINEST)) xembedLog.finest("Mapped " + e + " to " + this);
 840             } finally {
 841                 XlibWrapper.unsafe.freeMemory(data);
 842             }
 843         }
 844 
 845         public int hashCode() {
 846             return (int)keysym & 0xFFFFFFFF;
 847         }
 848 
 849         public boolean equals(Object o) {
 850             if (!(o instanceof GrabbedKey)) {
 851                 return false;
 852             }
 853             GrabbedKey key = (GrabbedKey)o;
 854             return (keysym == key.keysym && modifiers == key.modifiers);
 855         }
 856 
 857         public String toString() {
 858             return "Key combination[keysym=" + keysym + ", mods=" + modifiers + "]";
 859         }
 860     }
 861 }