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
26package sun.awt.X11;
27
28import java.awt.AWTKeyStroke;
29import sun.awt.SunToolkit;
30import java.awt.Component;
31import java.awt.Container;
32import sun.util.logging.PlatformLogger;
33
34import sun.awt.X11GraphicsConfig;
35import 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 */
42public 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
51    XEmbedClientHelper() {
52        super();
53    }
54
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
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
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    }
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
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    }
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    }
198    boolean requestFocus() {
199        if (active && embedded.focusAllowedFor()) {
200            sendMessage(server, XEMBED_REQUEST_FOCUS);
201            return true;
202        }
203        return false;
204    }
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
216    long getEmbedder(XWindowPeer embedded, XClientMessageEvent info) {
217        // Embedder is the parent of embedded.
218        return XlibUtil.getParentWindow(embedded.getWindow());
219    }
220
221    boolean isApplicationActive() {
222        return applicationActive;
223    }
224
225    boolean isActive() {
226        return active;
227    }
228
229    void traverseOutForward() {
230        if (active) {
231            sendMessage(server, XEMBED_FOCUS_NEXT);
232        }
233    }
234
235    void traverseOutBackward() {
236        if (active) {
237            sendMessage(server, XEMBED_FOCUS_PREV);
238        }
239    }
240
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    }
248    void unregisterAccelerator(int id) {
249        if (active) {
250            sendMessage(server, XEMBED_UNREGISTER_ACCELERATOR, id, 0, 0);
251        }
252    }
253
254    long getX11KeySym(AWTKeyStroke stroke) {
255        XToolkit.awtLock();
256        try {
257            return XWindow.getKeySymForAWTKeyCode(stroke.getKeyCode());
258        } finally {
259            XToolkit.awtUnlock();
260        }
261    }
262
263    long getX11Mods(AWTKeyStroke stroke) {
264        return XWindow.getXModifiers(stroke);
265    }
266}
267