1 /*
2  * Copyright (c) 2003, 2013, 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.AWTKeyStroke;
29 import sun.awt.SunToolkit;
30 import java.awt.Component;
31 import java.awt.Container;
32 import sun.util.logging.PlatformLogger;
33 
34 import sun.awt.X11GraphicsConfig;
35 import sun.awt.X11GraphicsDevice;
36 
37 /**
38  * Helper class implementing XEmbed protocol handling routines(client side)
39  * Window which wants to participate in a protocol should create an instance,
40  * call install and forward all XClientMessageEvents to it.
41  */
42 public class XEmbedClientHelper extends XEmbedHelper implements XEventDispatcher {
43     private static final PlatformLogger xembedLog = PlatformLogger.getLogger("sun.awt.X11.xembed.XEmbedClientHelper");
44 
45     private XEmbeddedFramePeer embedded; // XEmbed client
46     private long server; // XEmbed server
47 
48     private boolean active;
49     private boolean applicationActive;
50 
XEmbedClientHelper()51     XEmbedClientHelper() {
52         super();
53     }
54 
setClient(XEmbeddedFramePeer client)55     void setClient(XEmbeddedFramePeer client) {
56         if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
57             xembedLog.fine("XEmbed client: " + client);
58         }
59         if (embedded != null) {
60             XToolkit.removeEventDispatcher(embedded.getWindow(), this);
61             active = false;
62         }
63         embedded = client;
64         if (embedded != null) {
65             XToolkit.addEventDispatcher(embedded.getWindow(), this);
66         }
67     }
68 
install()69     void install() {
70         if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
71             xembedLog.fine("Installing xembedder on " + embedded);
72         }
73         long[] info = new long[] { XEMBED_VERSION, XEMBED_MAPPED };
74         long data = Native.card32ToData(info);
75         try {
76             XEmbedInfo.setAtomData(embedded.getWindow(), data, 2);
77         } finally {
78             unsafe.freeMemory(data);
79         }
80         // XEmbeddedFrame is initially created with a null parent..
81         // Here it is reparented to the proper parent window.
82         long parentWindow = embedded.getParentWindowHandle();
83         if (parentWindow != 0) {
84             XToolkit.awtLock();
85             try {
86                 XlibWrapper.XReparentWindow(XToolkit.getDisplay(),
87                                             embedded.getWindow(),
88                                             parentWindow,
89                                             0, 0);
90             } finally {
91                 XToolkit.awtUnlock();
92             }
93         }
94     }
95 
handleClientMessage(XEvent xev)96     void handleClientMessage(XEvent xev) {
97         XClientMessageEvent msg = xev.get_xclient();
98         if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
99             xembedLog.fine(msg.toString());
100         }
101         if (msg.get_message_type() == XEmbed.getAtom()) {
102             if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
103                 xembedLog.fine("Embedded message: " + msgidToString((int)msg.get_data(1)));
104             }
105             switch ((int)msg.get_data(1)) {
106               case XEMBED_EMBEDDED_NOTIFY: // Notification about embedding protocol start
107                   active = true;
108                   server = getEmbedder(embedded, msg);
109                   // Check if window is reparented. If not - it was created with
110                   // parent and so we should update it here.
111                   if (!embedded.isReparented()) {
112                       embedded.setReparented(true);
113                       embedded.updateSizeHints();
114                   }
115                   embedded.notifyStarted();
116                   break;
117               case XEMBED_WINDOW_ACTIVATE:
118                   applicationActive = true;
119                   break;
120               case XEMBED_WINDOW_DEACTIVATE:
121                   if (applicationActive) {
122                       applicationActive = false;
123                       handleWindowFocusOut();
124                   }
125                   break;
126               case XEMBED_FOCUS_IN: // We got focus!
127                   // Check for direction
128                   handleFocusIn((int)msg.get_data(2));
129                   break;
130               case XEMBED_FOCUS_OUT:
131                   if (applicationActive) {
132                       handleWindowFocusOut();
133                   }
134                   break;
135             }
136         }
137     }
handleFocusIn(int detail)138     void handleFocusIn(int detail) {
139         if (embedded.focusAllowedFor()) {
140             embedded.handleWindowFocusIn(0);
141         }
142         switch(detail) {
143           case XEMBED_FOCUS_CURRENT:
144               // Do nothing - just restore to the current value
145               break;
146           case XEMBED_FOCUS_FIRST:
147               SunToolkit.executeOnEventHandlerThread(embedded.target, new Runnable() {
148                       public void run() {
149                           Component comp = ((Container)embedded.target).getFocusTraversalPolicy().getFirstComponent((Container)embedded.target);
150                           if (comp != null) {
151                               comp.requestFocusInWindow();
152                           }
153                       }});
154               break;
155           case XEMBED_FOCUS_LAST:
156               SunToolkit.executeOnEventHandlerThread(embedded.target, new Runnable() {
157                       public void run() {
158                           Component comp = ((Container)embedded.target).getFocusTraversalPolicy().getLastComponent((Container)embedded.target);
159                           if (comp != null) {
160                               comp.requestFocusInWindow();
161                           }
162                       }});
163               break;
164         }
165     }
166 
dispatchEvent(XEvent xev)167     public void dispatchEvent(XEvent xev) {
168         switch(xev.get_type()) {
169           case XConstants.ClientMessage:
170               handleClientMessage(xev);
171               break;
172           case XConstants.ReparentNotify:
173               handleReparentNotify(xev);
174               break;
175         }
176     }
handleReparentNotify(XEvent xev)177     public void handleReparentNotify(XEvent xev) {
178         XReparentEvent re = xev.get_xreparent();
179         long newParent = re.get_parent();
180         if (active) {
181             // unregister accelerators, etc. for old parent
182             embedded.notifyStopped();
183             // check if newParent is a root window
184             X11GraphicsConfig gc = (X11GraphicsConfig)embedded.getGraphicsConfiguration();
185             X11GraphicsDevice gd = gc.getDevice();
186             if ((newParent == XlibUtil.getRootWindow(gd.getScreen())) ||
187                 (newParent == XToolkit.getDefaultRootWindow()))
188             {
189                 // reparenting to root means XEmbed termination
190                 active = false;
191             } else {
192                 // continue XEmbed with a new parent
193                 server = newParent;
194                 embedded.notifyStarted();
195             }
196         }
197     }
requestFocus()198     boolean requestFocus() {
199         if (active && embedded.focusAllowedFor()) {
200             sendMessage(server, XEMBED_REQUEST_FOCUS);
201             return true;
202         }
203         return false;
204     }
handleWindowFocusOut()205     void handleWindowFocusOut() {
206         // fix for 6269309: it is possible that we call this method twice
207         // (for example, when receiving XEMBED_WINDOW_DEACTIVATE and then
208         // XEMBED_FOCUS_OUT client messages), so we first need to check if
209         // embedded is an active window before sending WINDOW_LOST_FOCUS
210         // to shared code
211         if (XKeyboardFocusManagerPeer.getInstance().getCurrentFocusedWindow() == embedded.target) {
212             embedded.handleWindowFocusOut(null, 0);
213         }
214     }
215 
getEmbedder(XWindowPeer embedded, XClientMessageEvent info)216     long getEmbedder(XWindowPeer embedded, XClientMessageEvent info) {
217         // Embedder is the parent of embedded.
218         return XlibUtil.getParentWindow(embedded.getWindow());
219     }
220 
isApplicationActive()221     boolean isApplicationActive() {
222         return applicationActive;
223     }
224 
isActive()225     boolean isActive() {
226         return active;
227     }
228 
traverseOutForward()229     void traverseOutForward() {
230         if (active) {
231             sendMessage(server, XEMBED_FOCUS_NEXT);
232         }
233     }
234 
traverseOutBackward()235     void traverseOutBackward() {
236         if (active) {
237             sendMessage(server, XEMBED_FOCUS_PREV);
238         }
239     }
240 
registerAccelerator(AWTKeyStroke stroke, int id)241     void registerAccelerator(AWTKeyStroke stroke, int id) {
242         if (active) {
243             long sym = getX11KeySym(stroke);
244             long mods = getX11Mods(stroke);
245             sendMessage(server, XEMBED_REGISTER_ACCELERATOR, id, sym, mods);
246         }
247     }
unregisterAccelerator(int id)248     void unregisterAccelerator(int id) {
249         if (active) {
250             sendMessage(server, XEMBED_UNREGISTER_ACCELERATOR, id, 0, 0);
251         }
252     }
253 
getX11KeySym(AWTKeyStroke stroke)254     long getX11KeySym(AWTKeyStroke stroke) {
255         XToolkit.awtLock();
256         try {
257             return XWindow.getKeySymForAWTKeyCode(stroke.getKeyCode());
258         } finally {
259             XToolkit.awtUnlock();
260         }
261     }
262 
getX11Mods(AWTKeyStroke stroke)263     long getX11Mods(AWTKeyStroke stroke) {
264         return XWindow.getXModifiers(stroke);
265     }
266 }
267