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