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 }