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