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