blob: 14cedf1fffa87bcd5119b6c63bf3e14c33ade963 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-2007 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.awt.X11;
27
28import java.awt.*;
29import java.awt.dnd.DropTarget;
30import java.awt.dnd.DropTargetListener;
31import java.awt.event.*;
32import java.awt.image.ColorModel;
33import java.awt.image.ImageObserver;
34import java.awt.image.ImageProducer;
35import java.awt.image.VolatileImage;
36import java.awt.peer.*;
37import sun.awt.*;
38import sun.awt.motif.X11FontMetrics;
39import java.lang.reflect.*;
40import java.util.logging.*;
41import java.util.*;
42import static sun.awt.X11.XEmbedHelper.*;
43
44import java.security.AccessController;
45import sun.security.action.GetBooleanAction;
46
47public class XEmbedCanvasPeer extends XCanvasPeer implements WindowFocusListener, KeyEventPostProcessor, ModalityListener, WindowIDProvider {
48 private static final Logger xembedLog = Logger.getLogger("sun.awt.X11.xembed.XEmbedCanvasPeer");
49
50 boolean applicationActive; // Whether the application is active(has focus)
51 XEmbedServer xembed = new XEmbedServer(); // Helper object, contains XEmbed intrinsics
52 Map<Long, AWTKeyStroke> accelerators = new HashMap<Long, AWTKeyStroke>(); // Maps accelerator ID into AWTKeyStroke
53 Map<AWTKeyStroke, Long> accel_lookup = new HashMap<AWTKeyStroke, Long>(); // Maps AWTKeyStroke into accelerator ID
54 Set<GrabbedKey> grabbed_keys = new HashSet<GrabbedKey>(); // A set of keys grabbed by client
55 Object ACCEL_LOCK = accelerators; // Lock object for working with accelerators;
56 Object GRAB_LOCK = grabbed_keys; // Lock object for working with keys grabbed by client
57
58 XEmbedCanvasPeer() {}
59
60 XEmbedCanvasPeer(XCreateWindowParams params) {
61 super(params);
62 }
63
64 XEmbedCanvasPeer(Component target) {
65 super(target);
66 }
67
68 protected void postInit(XCreateWindowParams params) {
69 super.postInit(params);
70
71 installActivateListener();
72 installAcceleratorListener();
73 installModalityListener();
74
75 // XEmbed canvas should be non-traversable.
76 // FIXME: Probably should be removed and enforced setting of it by the users
77 target.setFocusTraversalKeysEnabled(false);
78 }
79
80 protected void preInit(XCreateWindowParams params) {
81 super.preInit(params);
82
83 params.put(EVENT_MASK,
84 KeyPressMask | KeyReleaseMask
85 | FocusChangeMask | ButtonPressMask | ButtonReleaseMask
86 | EnterWindowMask | LeaveWindowMask | PointerMotionMask
87 | ButtonMotionMask | ExposureMask | StructureNotifyMask | SubstructureNotifyMask);
88
89 }
90
91 void installModalityListener() {
92 ((SunToolkit)Toolkit.getDefaultToolkit()).addModalityListener(this);
93 }
94
95 void deinstallModalityListener() {
96 ((SunToolkit)Toolkit.getDefaultToolkit()).removeModalityListener(this);
97 }
98
99 void installAcceleratorListener() {
100 KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventPostProcessor(this);
101 }
102
103 void deinstallAcceleratorListener() {
104 KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventPostProcessor(this);
105 }
106
107 void installActivateListener() {
108 // FIXME: should watch for hierarchy changes
109 Window toplevel = getTopLevel(target);
110 if (toplevel != null) {
111 toplevel.addWindowFocusListener(this);
112 applicationActive = toplevel.isFocused();
113 }
114 }
115
116 void deinstallActivateListener() {
117 Window toplevel = getTopLevel(target);
118 if (toplevel != null) {
119 toplevel.removeWindowFocusListener(this);
120 }
121 }
122
123 boolean isXEmbedActive() {
124 return xembed.handle != 0;
125 }
126
127 boolean isApplicationActive() {
128 return applicationActive;
129 }
130
131 void initDispatching() {
132 if (xembedLog.isLoggable(Level.FINE)) xembedLog.fine("Init embedding for " + Long.toHexString(xembed.handle));
133 XToolkit.awtLock();
134 try {
135 XToolkit.addEventDispatcher(xembed.handle, xembed);
136 XlibWrapper.XSelectInput(XToolkit.getDisplay(), xembed.handle,
137 XlibWrapper.StructureNotifyMask | XlibWrapper.PropertyChangeMask);
138
139 XDropTargetRegistry.getRegistry().registerXEmbedClient(getWindow(), xembed.handle);
140 } finally {
141 XToolkit.awtUnlock();
142 }
143 xembed.processXEmbedInfo();
144
145 notifyChildEmbedded();
146 }
147
148 void endDispatching() {
149 xembedLog.fine("End dispatching for " + Long.toHexString(xembed.handle));
150 XToolkit.awtLock();
151 try {
152 XDropTargetRegistry.getRegistry().unregisterXEmbedClient(getWindow(), xembed.handle);
153 // We can't deselect input since someone else might be interested in it
154 XToolkit.removeEventDispatcher(xembed.handle, xembed);
155 } finally {
156 XToolkit.awtUnlock();
157 }
158 }
159
160 void embedChild(long child) {
161 if (xembed.handle != 0) {
162 detachChild();
163 }
164 xembed.handle = child;
165 initDispatching();
166 }
167
168 void childDestroyed() {
169 xembedLog.fine("Child " + Long.toHexString(xembed.handle) + " has self-destroyed.");
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 CreateNotify:
198 XCreateWindowEvent cr = ev.get_xcreatewindow();
199 if (xembedLog.isLoggable(Level.FINEST)) {
200 xembedLog.finest("Message on embedder: " + cr);
201 }
202 if (xembedLog.isLoggable(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 DestroyNotify:
209 XDestroyWindowEvent dn = ev.get_xdestroywindow();
210 if (xembedLog.isLoggable(Level.FINEST)) {
211 xembedLog.finest("Message on embedder: " + dn);
212 }
213 if (xembedLog.isLoggable(Level.FINER)) {
214 xembedLog.finer("Destroy notify for parent: " + dn);
215 }
216 childDestroyed();
217 break;
218 case ReparentNotify:
219 XReparentEvent rep = ev.get_xreparent();
220 if (xembedLog.isLoggable(Level.FINEST)) {
221 xembedLog.finest("Message on embedder: " + rep);
222 }
223 if (xembedLog.isLoggable(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 XToolkit.WITH_XERROR_HANDLER(XToolkit.IgnoreBadWindowHandler);
305 int status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
306 xembed.handle, wattr.pData);
307
308 XToolkit.RESTORE_XERROR_HANDLER();
309
310 if (status == 0 ||
311 (XToolkit.saved_error != null &&
312 XToolkit.saved_error.get_error_code() != XlibWrapper.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(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(Level.FINE)) xembedLog.fine("Detaching child " + Long.toHexString(xembed.handle));
392 /**
393 * XEmbed specification:
394 * "The embedder can unmap the client and reparent the client window to the root window. If the
395 * client receives an ReparentNotify event, it should check the parent field of the XReparentEvent
396 * structure. If this is the root window of the window's screen, then the protocol is finished and
397 * there is no further interaction. If it is a window other than the root window, then the protocol
398 * continues with the new parent acting as the embedder window."
399 */
400 XToolkit.awtLock();
401 try {
402 XlibWrapper.XUnmapWindow(XToolkit.getDisplay(), xembed.handle);
403 XlibWrapper.XReparentWindow(XToolkit.getDisplay(), xembed.handle, XToolkit.getDefaultRootWindow(), 0, 0);
404 } finally {
405 XToolkit.awtUnlock();
406 }
407 endDispatching();
408 xembed.handle = 0;
409 }
410
411 public void windowGainedFocus(WindowEvent e) {
412 applicationActive = true;
413 if (isXEmbedActive()) {
414 xembedLog.fine("Sending WINDOW_ACTIVATE");
415 xembed.sendMessage(xembed.handle, XEMBED_WINDOW_ACTIVATE);
416 }
417 }
418
419 public void windowLostFocus(WindowEvent e) {
420 applicationActive = false;
421 if (isXEmbedActive()) {
422 xembedLog.fine("Sending WINDOW_DEACTIVATE");
423 xembed.sendMessage(xembed.handle, XEMBED_WINDOW_DEACTIVATE);
424 }
425 }
426
427 void canvasFocusGained(FocusEvent e) {
428 if (isXEmbedActive()) {
429 xembedLog.fine("Forwarding FOCUS_GAINED");
430 int flavor = XEMBED_FOCUS_CURRENT;
431 if (e instanceof CausedFocusEvent) {
432 CausedFocusEvent ce = (CausedFocusEvent)e;
433 if (ce.getCause() == CausedFocusEvent.Cause.TRAVERSAL_FORWARD) {
434 flavor = XEMBED_FOCUS_FIRST;
435 } else if (ce.getCause() == CausedFocusEvent.Cause.TRAVERSAL_BACKWARD) {
436 flavor = XEMBED_FOCUS_LAST;
437 }
438 }
439 xembed.sendMessage(xembed.handle, XEMBED_FOCUS_IN, flavor, 0, 0);
440 }
441 }
442
443 void canvasFocusLost(FocusEvent e) {
444 if (isXEmbedActive() && !e.isTemporary()) {
445 xembedLog.fine("Forwarding FOCUS_LOST");
446 int num = 0;
447 if (AccessController.doPrivileged(new GetBooleanAction("sun.awt.xembed.testing"))) {
448 Component opp = e.getOppositeComponent();
449 try {
450 num = Integer.parseInt(opp.getName());
451 } catch (NumberFormatException nfe) {
452 }
453 }
454 xembed.sendMessage(xembed.handle, XEMBED_FOCUS_OUT, num, 0, 0);
455 }
456 }
457
458 static Field bdataField;
459 static byte[] getBData(KeyEvent e) {
460 try {
461 if (bdataField == null) {
462 bdataField = SunToolkit.getField(java.awt.AWTEvent.class, "bdata");
463 }
464 return (byte[])bdataField.get(e);
465 } catch (IllegalAccessException ex) {
466 return null;
467 }
468 }
469
470 void forwardKeyEvent(KeyEvent e) {
471 xembedLog.fine("Try to forward key event");
472 byte[] bdata = getBData(e);
473 long data = Native.toData(bdata);
474 if (data == 0) {
475 return;
476 }
477 try {
478 XKeyEvent ke = new XKeyEvent(data);
479 ke.set_window(xembed.handle);
480 if (xembedLog.isLoggable(Level.FINE)) xembedLog.fine("Forwarding native key event: " + ke);
481 XToolkit.awtLock();
482 try {
483 XlibWrapper.XSendEvent(XToolkit.getDisplay(), xembed.handle, false, XlibWrapper.NoEventMask, data);
484 } finally {
485 XToolkit.awtUnlock();
486 }
487 } finally {
488 XlibWrapper.unsafe.freeMemory(data);
489 }
490 }
491
492
493 /**
494 * Grab/ungrab key functionality is an unofficial API supported by
495 * GTK. Unfortunately, it doesn't support accelerator API, so,
496 * since this is the ONLY shortcut-processing API available, we
497 * must support it. See XEmbed.NON_STANDARD_XEMBED_GTK_*
498 * messages. The format of these messages is as follows:
499 * - request from client:
500 * data[1] = NON_STANDARD_XEMBED_GTK_GRAB_KEY or NON_STANDARD_XEMBED_GTK_UNGRAB_KEY
501 * data[3] = X keysym
502 * data[4] = X modifiers
503 *
504 * - response from server (in case the grabbed key has been pressed):
505 * forwarded XKeyEvent that matches keysym/modifiers pair
506 */
507 void grabKey(final long keysym, final long modifiers) {
508 postEvent(new InvocationEvent(target, new Runnable() {
509 public void run() {
510 GrabbedKey grab = new GrabbedKey(keysym, modifiers);
511 if (xembedLog.isLoggable(Level.FINE)) xembedLog.fine("Grabbing key: " + grab);
512 synchronized(GRAB_LOCK) {
513 grabbed_keys.add(grab);
514 }
515 }
516 }));
517 }
518
519 void ungrabKey(final long keysym, final long modifiers) {
520 postEvent(new InvocationEvent(target, new Runnable() {
521 public void run() {
522 GrabbedKey grab = new GrabbedKey(keysym, modifiers);
523 if (xembedLog.isLoggable(Level.FINE)) xembedLog.fine("UnGrabbing key: " + grab);
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(Level.FINE)) xembedLog.fine("Registering accelerator " + accel_id + " for " + stroke);
537 synchronized(ACCEL_LOCK) {
538 accelerators.put(accel_id, stroke);
539 accel_lookup.put(stroke, accel_id);
540 }
541 }
542 propogateRegisterAccelerator(stroke);
543 }
544 }));
545 }
546
547 void unregisterAccelerator(final long accel_id) {
548 postEvent(new InvocationEvent(target, new Runnable() {
549 public void run() {
550 AWTKeyStroke stroke = null;
551 synchronized(ACCEL_LOCK) {
552 stroke = accelerators.get(accel_id);
553 if (stroke != null) {
554 if (xembedLog.isLoggable(Level.FINE)) xembedLog.fine("Unregistering accelerator: " + accel_id);
555 accelerators.remove(accel_id);
556 accel_lookup.remove(stroke); // FIXME: How about several accelerators with the same stroke?
557 }
558 }
559 propogateUnRegisterAccelerator(stroke);
560 }
561 }));
562 }
563
564 void propogateRegisterAccelerator(AWTKeyStroke stroke) {
565 // Find the top-level and see if it is XEmbed client. If so, ask him to
566 // register the accelerator
567 XWindowPeer parent = getToplevelXWindow();
568 if (parent != null && parent instanceof XEmbeddedFramePeer) {
569 XEmbeddedFramePeer embedded = (XEmbeddedFramePeer)parent;
570 embedded.registerAccelerator(stroke);
571 }
572 }
573
574 void propogateUnRegisterAccelerator(AWTKeyStroke stroke) {
575 // Find the top-level and see if it is XEmbed client. If so, ask him to
576 // register the accelerator
577 XWindowPeer parent = getToplevelXWindow();
578 if (parent != null && parent instanceof XEmbeddedFramePeer) {
579 XEmbeddedFramePeer embedded = (XEmbeddedFramePeer)parent;
580 embedded.unregisterAccelerator(stroke);
581 }
582 }
583
584 public boolean postProcessKeyEvent(KeyEvent e) {
585 // Processing events only if we are in the focused window but
586 // we are not focus owner since otherwise we will get
587 // duplicate shortcut events in the client - one is from
588 // activate_accelerator, another from forwarded event
589 // FIXME: This is probably an incompatibility, protocol
590 // doesn't say anything about disable accelerators when client
591 // is focused.
592
593 XWindowPeer parent = getToplevelXWindow();
594 if (parent == null || !((Window)parent.getTarget()).isFocused() || target.isFocusOwner()) {
595 return false;
596 }
597
598 boolean result = false;
599
600 if (xembedLog.isLoggable(Level.FINER)) xembedLog.finer("Post-processing event " + e);
601
602 // Process ACCELERATORS
603 AWTKeyStroke stroke = AWTKeyStroke.getAWTKeyStrokeForEvent(e);
604 long accel_id = 0;
605 boolean exists = false;
606 synchronized(ACCEL_LOCK) {
607 exists = accel_lookup.containsKey(stroke);
608 if (exists) {
609 accel_id = accel_lookup.get(stroke).longValue();
610 }
611 }
612 if (exists) {
613 if (xembedLog.isLoggable(Level.FINE)) xembedLog.fine("Activating accelerator " + accel_id);
614 xembed.sendMessage(xembed.handle, XEMBED_ACTIVATE_ACCELERATOR, accel_id, 0, 0); // FIXME: How about overloaded?
615 result = true;
616 }
617
618 // Process Grabs, unofficial GTK feature
619 exists = false;
620 GrabbedKey key = new GrabbedKey(e);
621 synchronized(GRAB_LOCK) {
622 exists = grabbed_keys.contains(key);
623 }
624 if (exists) {
625 if (xembedLog.isLoggable(Level.FINE)) xembedLog.fine("Forwarding grabbed key " + e);
626 forwardKeyEvent(e);
627 result = true;
628 }
629
630 return result;
631 }
632
633 public void modalityPushed(ModalityEvent ev) {
634 xembed.sendMessage(xembed.handle, XEMBED_MODALITY_ON);
635 }
636
637 public void modalityPopped(ModalityEvent ev) {
638 xembed.sendMessage(xembed.handle, XEMBED_MODALITY_OFF);
639 }
640
641 public void handleClientMessage(XEvent xev) {
642 super.handleClientMessage(xev);
643 XClientMessageEvent msg = xev.get_xclient();
644 if (xembedLog.isLoggable(Level.FINER)) xembedLog.finer("Client message to embedder: " + msg);
645 if (msg.get_message_type() == xembed.XEmbed.getAtom()) {
646 if (xembedLog.isLoggable(Level.FINE)) xembedLog.fine(xembed.XEmbedMessageToString(msg));
647 }
648 if (isXEmbedActive()) {
649 switch ((int)msg.get_data(1)) {
650 case _SUN_XEMBED_START:
651 // Child has finished initialization and waits for notify
652 xembed.processXEmbedInfo();
653
654 notifyChildEmbedded();
655 break;
656 case XEMBED_REQUEST_FOCUS:
657 requestXEmbedFocus();
658 break;
659 case XEMBED_FOCUS_NEXT:
660 focusNext();
661 break;
662 case XEMBED_FOCUS_PREV:
663 focusPrev();
664 break;
665 case XEMBED_REGISTER_ACCELERATOR:
666 registerAccelerator(msg.get_data(2), msg.get_data(3), msg.get_data(4));
667 break;
668 case XEMBED_UNREGISTER_ACCELERATOR:
669 unregisterAccelerator(msg.get_data(2));
670 break;
671 case NON_STANDARD_XEMBED_GTK_GRAB_KEY:
672 grabKey(msg.get_data(3), msg.get_data(4));
673 break;
674 case NON_STANDARD_XEMBED_GTK_UNGRAB_KEY:
675 ungrabKey(msg.get_data(3), msg.get_data(4));
676 break;
677 }
678 } else {
679 xembedLog.finer("But XEmbed is not Active!");
680 }
681 }
682
683 private static class XEmbedDropTarget extends DropTarget {
684 public void addDropTargetListener(DropTargetListener dtl)
685 throws TooManyListenersException {
686 // Drop target listeners registered with this target will never be
687 // notified, since all drag notifications are routed to the XEmbed
688 // client. To avoid confusion we prohibit listeners registration
689 // by throwing TooManyListenersException as if there is a listener
690 // registered with this target already.
691 throw new TooManyListenersException();
692 }
693 }
694
695 public void setXEmbedDropTarget() {
696 // Register a drop site on the top level.
697 Runnable r = new Runnable() {
698 public void run() {
699 target.setDropTarget(new XEmbedDropTarget());
700 }
701 };
702 SunToolkit.executeOnEventHandlerThread(target, r);
703 }
704
705 public void removeXEmbedDropTarget() {
706 // Unregister a drop site on the top level.
707 Runnable r = new Runnable() {
708 public void run() {
709 if (target.getDropTarget() instanceof XEmbedDropTarget) {
710 target.setDropTarget(null);
711 }
712 }
713 };
714 SunToolkit.executeOnEventHandlerThread(target, r);
715 }
716
717 public boolean processXEmbedDnDEvent(long ctxt, int eventID) {
718 if (xembedLog.isLoggable(Level.FINEST)) {
719 xembedLog.finest(" Drop target=" + target.getDropTarget());
720 }
721 if (target.getDropTarget() instanceof XEmbedDropTarget) {
722 AppContext appContext = XToolkit.targetToAppContext(getTarget());
723 XDropTargetContextPeer peer =
724 XDropTargetContextPeer.getPeer(appContext);
725 peer.forwardEventToEmbedded(xembed.handle, ctxt, eventID);
726 return true;
727 } else {
728 return false;
729 }
730 }
731
732 class XEmbedServer extends XEmbedHelper implements XEventDispatcher {
733 long handle; // Handle to XEmbed client
734 long version;
735 long flags;
736
737 boolean processXEmbedInfo() {
738 long xembed_info_data = Native.allocateLongArray(2);
739 try {
740 if (!XEmbedInfo.getAtomData(handle, xembed_info_data, 2)) {
741 // No more XEMBED_INFO? This is not XEmbed client!
742 // Unfortunately this is the initial state of the most clients
743 // FIXME: add 5-state processing
744 //childDestroyed();
745 xembedLog.finer("Unable to get XEMBED_INFO atom data");
746 return false;
747 }
748 version = Native.getCard32(xembed_info_data, 0);
749 flags = Native.getCard32(xembed_info_data, 1);
750 boolean new_mapped = (flags & XEMBED_MAPPED) != 0;
751 boolean currently_mapped = XlibUtil.getWindowMapState(handle) != XlibWrapper.IsUnmapped;
752 if (new_mapped != currently_mapped) {
753 if (xembedLog.isLoggable(Level.FINER))
754 xembedLog.fine("Mapping state of the client has changed, old state: " + currently_mapped + ", new state: " + new_mapped);
755 if (new_mapped) {
756 XToolkit.awtLock();
757 try {
758 XlibWrapper.XMapWindow(XToolkit.getDisplay(), handle);
759 } finally {
760 XToolkit.awtUnlock();
761 }
762 } else {
763 XToolkit.awtLock();
764 try {
765 XlibWrapper.XUnmapWindow(XToolkit.getDisplay(), handle);
766 } finally {
767 XToolkit.awtUnlock();
768 }
769 }
770 } else {
771 xembedLog.finer("Mapping state didn't change, mapped: " + currently_mapped);
772 }
773 return true;
774 } finally {
775 XlibWrapper.unsafe.freeMemory(xembed_info_data);
776 }
777 }
778
779 public void handlePropertyNotify(XEvent xev) {
780 if (isXEmbedActive()) {
781 XPropertyEvent ev = xev.get_xproperty();
782 if (xembedLog.isLoggable(Level.FINER)) xembedLog.finer("Property change on client: " + ev);
783 if (ev.get_atom() == XAtom.XA_WM_NORMAL_HINTS) {
784 childResized();
785 } else if (ev.get_atom() == XEmbedInfo.getAtom()) {
786 processXEmbedInfo();
787 } else if (ev.get_atom() ==
788 XDnDConstants.XA_XdndAware.getAtom()) {
789 XDropTargetRegistry.getRegistry().unregisterXEmbedClient(getWindow(),
790 xembed.handle);
791 if (ev.get_state() == XConstants.PropertyNewValue) {
792 XDropTargetRegistry.getRegistry().registerXEmbedClient(getWindow(),
793 xembed.handle);
794 }
795 }
796 } else {
797 xembedLog.finer("XEmbed is not active");
798 }
799 }
800 void handleConfigureNotify(XEvent xev) {
801 if (isXEmbedActive()) {
802 XConfigureEvent ev = xev.get_xconfigure();
803 if (xembedLog.isLoggable(Level.FINER)) xembedLog.finer("Bounds change on client: " + ev);
804 if (xev.get_xany().get_window() == handle) {
805 childResized();
806 }
807 }
808 }
809 public void dispatchEvent(XEvent xev) {
810 int type = xev.get_type();
811 switch (type) {
812 case PropertyNotify:
813 handlePropertyNotify(xev);
814 break;
815 case ConfigureNotify:
816 handleConfigureNotify(xev);
817 break;
818 case ClientMessage:
819 handleClientMessage(xev);
820 break;
821 }
822 }
823 }
824
825 static class GrabbedKey {
826 long keysym;
827 long modifiers;
828 GrabbedKey(long keysym, long modifiers) {
829 this.keysym = keysym;
830 this.modifiers = modifiers;
831 }
832
833 GrabbedKey(KeyEvent ev) {
834 init(ev);
835 }
836
837 private void init(KeyEvent e) {
838 byte[] bdata = getBData(e);
839 long data = Native.toData(bdata);
840 if (data == 0) {
841 return;
842 }
843 try {
844 XToolkit.awtLock();
845 try {
846 keysym = XWindow.getKeySymForAWTKeyCode(e.getKeyCode());
847 } finally {
848 XToolkit.awtUnlock();
849 }
850 XKeyEvent ke = new XKeyEvent(data);
851
852 // We recognize only these masks
853 modifiers = ke.get_state() & (ShiftMask | ControlMask | LockMask);
854 if (xembedLog.isLoggable(Level.FINEST)) xembedLog.finest("Mapped " + e + " to " + this);
855 } finally {
856 XlibWrapper.unsafe.freeMemory(data);
857 }
858 }
859
860 public int hashCode() {
861 return (int)keysym & 0xFFFFFFFF;
862 }
863
864 public boolean equals(Object o) {
865 if (!(o instanceof GrabbedKey)) {
866 return false;
867 }
868 GrabbedKey key = (GrabbedKey)o;
869 return (keysym == key.keysym && modifiers == key.modifiers);
870 }
871
872 public String toString() {
873 return "Key combination[keysym=" + keysym + ", mods=" + modifiers + "]";
874 }
875 }
876}