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