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