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