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