1 /* 2 * Copyright (c) 2003, 2009, 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 27 package sun.awt.X11; 28 29 import java.awt.Frame; 30 import sun.util.logging.PlatformLogger; 31 32 final class XNETProtocol extends XProtocol implements XStateProtocol, XLayerProtocol 33 { 34 private final static PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XNETProtocol"); 35 private final static PlatformLogger iconLog = PlatformLogger.getLogger("sun.awt.X11.icon.XNETProtocol"); 36 private static PlatformLogger stateLog = PlatformLogger.getLogger("sun.awt.X11.states.XNETProtocol"); 37 38 /** 39 * XStateProtocol 40 */ 41 public boolean supportsState(int state) { 42 return doStateProtocol() ; // TODO - check for Frame constants 43 } 44 45 public void setState(XWindowPeer window, int state) { 46 if (log.isLoggable(PlatformLogger.FINE)) log.fine("Setting state of " + window + " to " + state); 47 if (window.isShowing()) { 48 requestState(window, state); 49 } else { 50 setInitialState(window, state); 51 } 52 } 53 54 private void setInitialState(XWindowPeer window, int state) { 55 XAtomList old_state = window.getNETWMState(); 56 log.fine("Current state of the window {0} is {1}", window, old_state); 57 if ((state & Frame.MAXIMIZED_VERT) != 0) { 58 old_state.add(XA_NET_WM_STATE_MAXIMIZED_VERT); 59 } else { 60 old_state.remove(XA_NET_WM_STATE_MAXIMIZED_VERT); 61 } 62 if ((state & Frame.MAXIMIZED_HORIZ) != 0) { 63 old_state.add(XA_NET_WM_STATE_MAXIMIZED_HORZ); 64 } else { 65 old_state.remove(XA_NET_WM_STATE_MAXIMIZED_HORZ); 66 } 67 log.fine("Setting initial state of the window {0} to {1}", window, old_state); 68 window.setNETWMState(old_state); 69 } 70 71 private void requestState(XWindowPeer window, int state) { 72 /* 73 * We have to use toggle for maximization because of transitions 74 * from maximization in one direction only to maximization in the 75 * other direction only. 76 */ 77 int old_net_state = getState(window); 78 int max_changed = (state ^ old_net_state) & (Frame.MAXIMIZED_BOTH); 79 80 XClientMessageEvent req = new XClientMessageEvent(); 81 try { 82 switch(max_changed) { 83 case 0: 84 return; 85 case Frame.MAXIMIZED_HORIZ: 86 req.set_data(1, XA_NET_WM_STATE_MAXIMIZED_HORZ.getAtom()); 87 req.set_data(2, 0); 88 break; 89 case Frame.MAXIMIZED_VERT: 90 req.set_data(1, XA_NET_WM_STATE_MAXIMIZED_VERT.getAtom()); 91 req.set_data(2, 0); 92 break; 93 case Frame.MAXIMIZED_BOTH: 94 req.set_data(1, XA_NET_WM_STATE_MAXIMIZED_HORZ.getAtom()); 95 req.set_data(2, XA_NET_WM_STATE_MAXIMIZED_VERT.getAtom()); 96 break; 97 default: 98 return; 99 } 100 if (log.isLoggable(PlatformLogger.FINE)) log.fine("Requesting state on " + window + " for " + state); 101 req.set_type((int)XConstants.ClientMessage); 102 req.set_window(window.getWindow()); 103 req.set_message_type(XA_NET_WM_STATE.getAtom()); 104 req.set_format(32); 105 req.set_data(0, _NET_WM_STATE_TOGGLE); 106 XToolkit.awtLock(); 107 try { 108 XlibWrapper.XSendEvent(XToolkit.getDisplay(), 109 XlibWrapper.RootWindow(XToolkit.getDisplay(), window.getScreenNumber()), 110 false, 111 XConstants.SubstructureRedirectMask | XConstants.SubstructureNotifyMask, 112 req.pData); 113 } 114 finally { 115 XToolkit.awtUnlock(); 116 } 117 } finally { 118 req.dispose(); 119 } 120 } 121 122 public int getState(XWindowPeer window) { 123 return getStateImpl(window); 124 } 125 126 /* 127 * New "NET" WM spec: _NET_WM_STATE/Atom[] 128 */ 129 int getStateImpl(XWindowPeer window) { 130 XAtomList net_wm_state = window.getNETWMState(); 131 if (net_wm_state.size() == 0) { 132 return Frame.NORMAL; 133 } 134 int java_state = Frame.NORMAL; 135 if (net_wm_state.contains(XA_NET_WM_STATE_MAXIMIZED_VERT)) { 136 java_state |= Frame.MAXIMIZED_VERT; 137 } 138 if (net_wm_state.contains(XA_NET_WM_STATE_MAXIMIZED_HORZ)) { 139 java_state |= Frame.MAXIMIZED_HORIZ; 140 } 141 return java_state; 142 } 143 144 public boolean isStateChange(XPropertyEvent e) { 145 boolean res = doStateProtocol() && (e.get_atom() == XA_NET_WM_STATE.getAtom()) ; 146 147 if (res) { 148 // Since state change happened, reset our cached state. It will be re-read by getState 149 XWindowPeer wpeer = (XWindowPeer)XToolkit.windowToXWindow(e.get_window()); 150 wpeer.setNETWMState(null); 151 } 152 return res; 153 } 154 155 /* 156 * Work around for 4775545. 157 */ 158 public void unshadeKludge(XWindowPeer window) { 159 XAtomList net_wm_state = window.getNETWMState(); 160 net_wm_state.remove(XA_NET_WM_STATE_SHADED); 161 window.setNETWMState(net_wm_state); 162 } 163 164 /** 165 * XLayerProtocol 166 */ 167 public boolean supportsLayer(int layer) { 168 return ((layer == LAYER_ALWAYS_ON_TOP) || (layer == LAYER_NORMAL)) && doLayerProtocol(); 169 } 170 171 public void requestState(XWindow window, XAtom state, boolean isAdd) { 172 XClientMessageEvent req = new XClientMessageEvent(); 173 try { 174 req.set_type((int)XConstants.ClientMessage); 175 req.set_window(window.getWindow()); 176 req.set_message_type(XA_NET_WM_STATE.getAtom()); 177 req.set_format(32); 178 req.set_data(0, isAdd ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE); 179 req.set_data(1, state.getAtom()); 180 // Fix for 6735584: req.data[2] must be set to 0 when only one property is changed 181 req.set_data(2, 0); 182 log.fine("Setting _NET_STATE atom {0} on {1} for {2}", state, window, Boolean.valueOf(isAdd)); 183 XToolkit.awtLock(); 184 try { 185 XlibWrapper.XSendEvent(XToolkit.getDisplay(), 186 XlibWrapper.RootWindow(XToolkit.getDisplay(), window.getScreenNumber()), 187 false, 188 XConstants.SubstructureRedirectMask | XConstants.SubstructureNotifyMask, 189 req.pData); 190 } 191 finally { 192 XToolkit.awtUnlock(); 193 } 194 } finally { 195 req.dispose(); 196 } 197 } 198 199 /** 200 * Helper function to set/reset one state in NET_WM_STATE 201 * If window is showing then it uses ClientMessage, otherwise adjusts NET_WM_STATE list 202 * @param window Window which NET_WM_STATE property is being modified 203 * @param state State atom to be set/reset 204 * @param reset Indicates operation, 'set' if false, 'reset' if true 205 */ 206 private void setStateHelper(XWindowPeer window, XAtom state, boolean set) { 207 log.finer("Window visibility is: withdrawn={0}, visible={1}, mapped={2} showing={3}", 208 Boolean.valueOf(window.isWithdrawn()), Boolean.valueOf(window.isVisible()), 209 Boolean.valueOf(window.isMapped()), Boolean.valueOf(window.isShowing())); 210 if (window.isShowing()) { 211 requestState(window, state, set); 212 } else { 213 XAtomList net_wm_state = window.getNETWMState(); 214 log.finer("Current state on {0} is {1}", window, net_wm_state); 215 if (!set) { 216 net_wm_state.remove(state); 217 } else { 218 net_wm_state.add(state); 219 } 220 log.fine("Setting states on {0} to {1}", window, net_wm_state); 221 window.setNETWMState(net_wm_state); 222 } 223 XToolkit.XSync(); 224 } 225 226 public void setLayer(XWindowPeer window, int layer) { 227 setStateHelper(window, XA_NET_WM_STATE_ABOVE, layer == LAYER_ALWAYS_ON_TOP); 228 } 229 230 /* New "netwm" spec from www.freedesktop.org */ 231 XAtom XA_UTF8_STRING = XAtom.get("UTF8_STRING"); /* like STRING but encoding is UTF-8 */ 232 XAtom XA_NET_SUPPORTING_WM_CHECK = XAtom.get("_NET_SUPPORTING_WM_CHECK"); 233 XAtom XA_NET_SUPPORTED = XAtom.get("_NET_SUPPORTED"); /* list of protocols (property of root) */ 234 XAtom XA_NET_WM_NAME = XAtom.get("_NET_WM_NAME"); /* window property */ 235 XAtom XA_NET_WM_STATE = XAtom.get("_NET_WM_STATE");/* both window property and request */ 236 237 /* 238 * _NET_WM_STATE is a list of atoms. 239 * NB: Standard spelling is "HORZ" (yes, without an 'I'), but KDE2 240 * uses misspelled "HORIZ" (see KDE bug #20229). This was fixed in 241 * KDE 2.2. Under earlier versions of KDE2 horizontal and full 242 * maximization doesn't work . 243 */ 244 XAtom XA_NET_WM_STATE_MAXIMIZED_HORZ = XAtom.get("_NET_WM_STATE_MAXIMIZED_HORZ"); 245 XAtom XA_NET_WM_STATE_MAXIMIZED_VERT = XAtom.get("_NET_WM_STATE_MAXIMIZED_VERT"); 246 XAtom XA_NET_WM_STATE_SHADED = XAtom.get("_NET_WM_STATE_SHADED"); 247 XAtom XA_NET_WM_STATE_ABOVE = XAtom.get("_NET_WM_STATE_ABOVE"); 248 XAtom XA_NET_WM_STATE_MODAL = XAtom.get("_NET_WM_STATE_MODAL"); 249 XAtom XA_NET_WM_STATE_FULLSCREEN = XAtom.get("_NET_WM_STATE_FULLSCREEN"); 250 XAtom XA_NET_WM_STATE_BELOW = XAtom.get("_NET_WM_STATE_BELOW"); 251 XAtom XA_NET_WM_STATE_HIDDEN = XAtom.get("_NET_WM_STATE_HIDDEN"); 252 XAtom XA_NET_WM_STATE_SKIP_TASKBAR = XAtom.get("_NET_WM_STATE_SKIP_TASKBAR"); 253 XAtom XA_NET_WM_STATE_SKIP_PAGER = XAtom.get("_NET_WM_STATE_SKIP_PAGER"); 254 255 public final XAtom XA_NET_WM_WINDOW_TYPE = XAtom.get("_NET_WM_WINDOW_TYPE"); 256 public final XAtom XA_NET_WM_WINDOW_TYPE_NORMAL = XAtom.get("_NET_WM_WINDOW_TYPE_NORMAL"); 257 public final XAtom XA_NET_WM_WINDOW_TYPE_DIALOG = XAtom.get("_NET_WM_WINDOW_TYPE_DIALOG"); 258 public final XAtom XA_NET_WM_WINDOW_TYPE_UTILITY = XAtom.get("_NET_WM_WINDOW_TYPE_UTILITY"); 259 public final XAtom XA_NET_WM_WINDOW_TYPE_POPUP_MENU = XAtom.get("_NET_WM_WINDOW_TYPE_POPUP_MENU"); 260 261 XAtom XA_NET_WM_WINDOW_OPACITY = XAtom.get("_NET_WM_WINDOW_OPACITY"); 262 263 /* For _NET_WM_STATE ClientMessage requests */ 264 final static int _NET_WM_STATE_REMOVE =0; /* remove/unset property */ 265 final static int _NET_WM_STATE_ADD =1; /* add/set property */ 266 final static int _NET_WM_STATE_TOGGLE =2; /* toggle property */ 267 268 boolean supportChecked = false; 269 long NetWindow = 0; 270 void detect() { 271 if (supportChecked) { 272 // TODO: How about detecting WM-restart or exit? 273 return; 274 } 275 NetWindow = checkAnchor(XA_NET_SUPPORTING_WM_CHECK, XAtom.XA_WINDOW); 276 supportChecked = true; 277 if (log.isLoggable(PlatformLogger.FINE)) log.fine("### " + this + " is active: " + (NetWindow != 0)); 278 } 279 280 boolean active() { 281 detect(); 282 return NetWindow != 0; 283 } 284 285 boolean doStateProtocol() { 286 boolean res = active() && checkProtocol(XA_NET_SUPPORTED, XA_NET_WM_STATE); 287 stateLog.finer("doStateProtocol() returns " + res); 288 return res; 289 } 290 291 boolean doLayerProtocol() { 292 boolean res = active() && checkProtocol(XA_NET_SUPPORTED, XA_NET_WM_STATE_ABOVE); 293 return res; 294 } 295 296 boolean doModalityProtocol() { 297 boolean res = active() && checkProtocol(XA_NET_SUPPORTED, XA_NET_WM_STATE_MODAL); 298 return res; 299 } 300 301 boolean doOpacityProtocol() { 302 boolean res = active() && checkProtocol(XA_NET_SUPPORTED, XA_NET_WM_WINDOW_OPACITY); 303 return res; 304 } 305 306 boolean isWMName(String name) { 307 if (!active()) { 308 return false; 309 } 310 String net_wm_name_string = getWMName(); 311 if (net_wm_name_string == null) { 312 return false; 313 } 314 if (log.isLoggable(PlatformLogger.FINE)) log.fine("### WM_NAME = " + net_wm_name_string); 315 return net_wm_name_string.startsWith(name); 316 } 317 318 String net_wm_name_cache; 319 public String getWMName() { 320 if (!active()) { 321 return null; 322 } 323 324 if (net_wm_name_cache != null) { 325 return net_wm_name_cache; 326 } 327 328 /* 329 * Check both UTF8_STRING and STRING. We only call this function 330 * with ASCII names and UTF8 preserves ASCII bit-wise. wm-spec 331 * mandates UTF8_STRING for _NET_WM_NAME but at least sawfish-1.0 332 * still uses STRING. (mmm, moving targets...). 333 */ 334 String charSet = "UTF8"; 335 byte[] net_wm_name = XA_NET_WM_NAME.getByteArrayProperty(NetWindow, XA_UTF8_STRING.getAtom()); 336 if (net_wm_name == null) { 337 net_wm_name = XA_NET_WM_NAME.getByteArrayProperty(NetWindow, XAtom.XA_STRING); 338 charSet = "ASCII"; 339 } 340 341 if (net_wm_name == null) { 342 return null; 343 } 344 try { 345 net_wm_name_cache = new String(net_wm_name, charSet); 346 return net_wm_name_cache; 347 } catch (java.io.UnsupportedEncodingException uex) { 348 return null; 349 } 350 } 351 352 /** 353 * Sets _NET_WM_ICON property on the window using the List of XIconInfo 354 * If icons is null or empty list, removes _NET_WM_ICON property 355 */ 356 public void setWMIcons(XWindowPeer window, java.util.List<XIconInfo> icons) { 357 if (window == null) return; 358 359 XAtom iconsAtom = XAtom.get("_NET_WM_ICON"); 360 if (icons == null) { 361 iconsAtom.DeleteProperty(window); 362 return; 363 } 364 365 int length = 0; 366 for (XIconInfo ii : icons) { 367 length += ii.getRawLength(); 368 } 369 int cardinalSize = (XlibWrapper.dataModel == 32) ? 4 : 8; 370 int bufferSize = length * cardinalSize; 371 372 if (bufferSize != 0) { 373 long buffer = XlibWrapper.unsafe.allocateMemory(bufferSize); 374 try { 375 long ptr = buffer; 376 for (XIconInfo ii : icons) { 377 int size = ii.getRawLength() * cardinalSize; 378 if (XlibWrapper.dataModel == 32) { 379 XlibWrapper.copyIntArray(ptr, ii.getIntData(), size); 380 } else { 381 XlibWrapper.copyLongArray(ptr, ii.getLongData(), size); 382 } 383 ptr += size; 384 } 385 iconsAtom.setAtomData(window.getWindow(), XAtom.XA_CARDINAL, buffer, bufferSize/Native.getCard32Size()); 386 } finally { 387 XlibWrapper.unsafe.freeMemory(buffer); 388 } 389 } else { 390 iconsAtom.DeleteProperty(window); 391 } 392 } 393 394 public boolean isWMStateNetHidden(XWindowPeer window) { 395 if (!doStateProtocol()) { 396 return false; 397 } 398 XAtomList state = window.getNETWMState(); 399 return (state != null && state.size() != 0 && state.contains(XA_NET_WM_STATE_HIDDEN)); 400 } 401 }