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