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 
  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 = (X11GraphicsDevice)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 }