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