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