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