001: /*
002: * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.awt.X11;
027:
028: import java.awt.*;
029: import java.util.logging.Level;
030: import java.util.logging.Logger;
031: import java.util.logging.LogManager;
032: import java.awt.*;
033: import java.awt.image.*;
034: import java.util.*;
035:
036: class XNETProtocol extends XProtocol implements XStateProtocol,
037: XLayerProtocol {
038: final static Logger log = Logger
039: .getLogger("sun.awt.X11.XNETProtocol");
040: private final static Logger iconLog = Logger
041: .getLogger("sun.awt.X11.icon.XNETProtocol");
042:
043: /**
044: * XStateProtocol
045: */
046: public boolean supportsState(int state) {
047: return doStateProtocol(); // TODO - check for Frame constants
048: }
049:
050: public void setState(XWindowPeer window, int state) {
051: if (log.isLoggable(Level.FINE))
052: log.fine("Setting state of " + window + " to " + state);
053: if (window.isShowing()) {
054: requestState(window, state);
055: } else {
056: setInitialState(window, state);
057: }
058: }
059:
060: private void setInitialState(XWindowPeer window, int state) {
061: XAtomList old_state = window.getNETWMState();
062: log.log(Level.FINE, "Current state of the window {0} is {1}",
063: new Object[] { window, old_state });
064: if ((state & Frame.MAXIMIZED_VERT) != 0) {
065: old_state.add(XA_NET_WM_STATE_MAXIMIZED_VERT);
066: } else {
067: old_state.remove(XA_NET_WM_STATE_MAXIMIZED_VERT);
068: }
069: if ((state & Frame.MAXIMIZED_HORIZ) != 0) {
070: old_state.add(XA_NET_WM_STATE_MAXIMIZED_HORZ);
071: } else {
072: old_state.remove(XA_NET_WM_STATE_MAXIMIZED_HORZ);
073: }
074: log.log(Level.FINE,
075: "Setting initial state of the window {0} to {1}",
076: new Object[] { window, old_state });
077: window.setNETWMState(old_state);
078: }
079:
080: private void requestState(XWindowPeer window, int state) {
081: /*
082: * We have to use toggle for maximization because of transitions
083: * from maximization in one direction only to maximization in the
084: * other direction only.
085: */
086: int old_net_state = getState(window);
087: int max_changed = (state ^ old_net_state)
088: & (Frame.MAXIMIZED_BOTH);
089:
090: XClientMessageEvent req = new XClientMessageEvent();
091: try {
092: switch (max_changed) {
093: case 0:
094: return;
095: case Frame.MAXIMIZED_HORIZ:
096: req.set_data(1, XA_NET_WM_STATE_MAXIMIZED_HORZ
097: .getAtom());
098: req.set_data(2, 0);
099: break;
100: case Frame.MAXIMIZED_VERT:
101: req.set_data(1, XA_NET_WM_STATE_MAXIMIZED_VERT
102: .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
107: .getAtom());
108: req.set_data(2, XA_NET_WM_STATE_MAXIMIZED_VERT
109: .getAtom());
110: break;
111: default:
112: return;
113: }
114: if (log.isLoggable(Level.FINE))
115: log.fine("Requesting state on " + window + " for "
116: + state);
117: req.set_type((int) XlibWrapper.ClientMessage);
118: req.set_window(window.getWindow());
119: req.set_message_type(XA_NET_WM_STATE.getAtom());
120: req.set_format(32);
121: req.set_data(0, _NET_WM_STATE_TOGGLE);
122: XToolkit.awtLock();
123: try {
124: XlibWrapper.XSendEvent(XToolkit.getDisplay(),
125: XlibWrapper.RootWindow(XToolkit.getDisplay(),
126: window.getScreenNumber()), false,
127: XlibWrapper.SubstructureRedirectMask
128: | XlibWrapper.SubstructureNotifyMask,
129: req.pData);
130: } finally {
131: XToolkit.awtUnlock();
132: }
133: } finally {
134: req.dispose();
135: }
136: }
137:
138: public int getState(XWindowPeer window) {
139: return getStateImpl(window);
140: }
141:
142: /*
143: * New "NET" WM spec: _NET_WM_STATE/Atom[]
144: */
145: int getStateImpl(XWindowPeer window) {
146: XAtomList net_wm_state = window.getNETWMState();
147: if (net_wm_state.size() == 0) {
148: return Frame.NORMAL;
149: }
150: int java_state = Frame.NORMAL;
151: if (net_wm_state.contains(XA_NET_WM_STATE_MAXIMIZED_VERT)) {
152: java_state |= Frame.MAXIMIZED_VERT;
153: }
154: if (net_wm_state.contains(XA_NET_WM_STATE_MAXIMIZED_HORZ)) {
155: java_state |= Frame.MAXIMIZED_HORIZ;
156: }
157: return java_state;
158: }
159:
160: public boolean isStateChange(XPropertyEvent e) {
161: boolean res = doStateProtocol()
162: && (e.get_atom() == XA_NET_WM_STATE.getAtom());
163:
164: if (res) {
165: // Since state change happened, reset our cached state. It will be re-read by getState
166: XWindowPeer wpeer = (XWindowPeer) XToolkit
167: .windowToXWindow(e.get_window());
168: wpeer.setNETWMState(null);
169: }
170: return res;
171: }
172:
173: /*
174: * Work around for 4775545.
175: */
176: public void unshadeKludge(XWindowPeer window) {
177: XAtomList net_wm_state = window.getNETWMState();
178: net_wm_state.remove(XA_NET_WM_STATE_SHADED);
179: window.setNETWMState(net_wm_state);
180: }
181:
182: /**
183: * XLayerProtocol
184: */
185: public boolean supportsLayer(int layer) {
186: return ((layer == LAYER_ALWAYS_ON_TOP) || (layer == LAYER_NORMAL))
187: && doLayerProtocol();
188: }
189:
190: /**
191: * Helper function to set/reset one state in NET_WM_STATE
192: * If window is showing then it uses ClientMessage, otherwise adjusts NET_WM_STATE list
193: * @param window Window which NET_WM_STATE property is being modified
194: * @param state State atom to be set/reset
195: * @param reset Indicates operation, 'set' if false, 'reset' if true
196: */
197: private void setStateHelper(XWindowPeer window, XAtom state,
198: boolean set) {
199: log
200: .log(
201: Level.FINER,
202: "Window visibility is: withdrawn={0}, visible={1}, mapped={2} showing={3}",
203: new Object[] {
204: Boolean.valueOf(window.isWithdrawn()),
205: Boolean.valueOf(window.isVisible()),
206: Boolean.valueOf(window.isMapped()),
207: Boolean.valueOf(window.isShowing()) });
208: if (window.isShowing()) {
209: XClientMessageEvent req = new XClientMessageEvent();
210: try {
211: req.set_type((int) XlibWrapper.ClientMessage);
212: req.set_window(window.getWindow());
213: req.set_message_type(XA_NET_WM_STATE.getAtom());
214: req.set_format(32);
215: req.set_data(0, (!set) ? _NET_WM_STATE_REMOVE
216: : _NET_WM_STATE_ADD);
217: req.set_data(1, state.getAtom());
218: log.log(Level.FINE,
219: "Setting _NET_STATE atom {0} on {1} for {2}",
220: new Object[] { state, window,
221: Boolean.valueOf(set) });
222: XToolkit.awtLock();
223: try {
224: XlibWrapper
225: .XSendEvent(
226: XToolkit.getDisplay(),
227: XlibWrapper.RootWindow(XToolkit
228: .getDisplay(), window
229: .getScreenNumber()),
230: false,
231: XlibWrapper.SubstructureRedirectMask
232: | XlibWrapper.SubstructureNotifyMask,
233: req.pData);
234: } finally {
235: XToolkit.awtUnlock();
236: }
237: } finally {
238: req.dispose();
239: }
240: } else {
241: XAtomList net_wm_state = window.getNETWMState();
242: log.log(Level.FINE, "Current state on {0} is {1}",
243: new Object[] { window, net_wm_state });
244: if (!set) {
245: net_wm_state.remove(state);
246: } else {
247: net_wm_state.add(state);
248: }
249: log.log(Level.FINE, "Setting states on {0} to {1}",
250: new Object[] { window, net_wm_state });
251: window.setNETWMState(net_wm_state);
252: }
253: XToolkit.XSync();
254: }
255:
256: public void setLayer(XWindowPeer window, int layer) {
257: setStateHelper(window, XA_NET_WM_STATE_ABOVE,
258: layer == LAYER_ALWAYS_ON_TOP);
259: }
260:
261: /* New "netwm" spec from www.freedesktop.org */
262: XAtom XA_UTF8_STRING = XAtom.get("UTF8_STRING"); /* like STRING but encoding is UTF-8 */
263: XAtom XA_NET_SUPPORTING_WM_CHECK = XAtom
264: .get("_NET_SUPPORTING_WM_CHECK");
265: XAtom XA_NET_SUPPORTED = XAtom.get("_NET_SUPPORTED"); /* list of protocols (property of root) */
266: XAtom XA_NET_WM_NAME = XAtom.get("_NET_WM_NAME"); /* window property */
267: XAtom XA_NET_WM_STATE = XAtom.get("_NET_WM_STATE");/* both window property and request */
268:
269: /*
270: * _NET_WM_STATE is a list of atoms.
271: * NB: Standard spelling is "HORZ" (yes, without an 'I'), but KDE2
272: * uses misspelled "HORIZ" (see KDE bug #20229). This was fixed in
273: * KDE 2.2. Under earlier versions of KDE2 horizontal and full
274: * maximization doesn't work .
275: */
276: XAtom XA_NET_WM_STATE_MAXIMIZED_HORZ = XAtom
277: .get("_NET_WM_STATE_MAXIMIZED_HORZ");
278: XAtom XA_NET_WM_STATE_MAXIMIZED_VERT = XAtom
279: .get("_NET_WM_STATE_MAXIMIZED_VERT");
280: XAtom XA_NET_WM_STATE_SHADED = XAtom.get("_NET_WM_STATE_SHADED");
281: XAtom XA_NET_WM_STATE_ABOVE = XAtom.get("_NET_WM_STATE_ABOVE");
282: XAtom XA_NET_WM_STATE_MODAL = XAtom.get("_NET_WM_STATE_MODAL");
283: XAtom XA_NET_WM_STATE_FULLSCREEN = XAtom
284: .get("_NET_WM_STATE_FULLSCREEN");
285: XAtom XA_NET_WM_STATE_BELOW = XAtom.get("_NET_WM_STATE_BELOW");
286: XAtom XA_NET_WM_STATE_HIDDEN = XAtom.get("_NET_WM_STATE_HIDDEN");
287: XAtom XA_NET_WM_STATE_SKIP_TASKBAR = XAtom
288: .get("_NET_WM_STATE_SKIP_TASKBAR");
289: XAtom XA_NET_WM_STATE_SKIP_PAGER = XAtom
290: .get("_NET_WM_STATE_SKIP_PAGER");
291:
292: XAtom XA_NET_WM_WINDOW_TYPE = XAtom.get("_NET_WM_WINDOW_TYPE");
293: XAtom XA_NET_WM_WINDOW_TYPE_DIALOG = XAtom
294: .get("_NET_WM_WINDOW_TYPE_DIALOG");
295:
296: /* For _NET_WM_STATE ClientMessage requests */
297: final static int _NET_WM_STATE_REMOVE = 0; /* remove/unset property */
298: final static int _NET_WM_STATE_ADD = 1; /* add/set property */
299: final static int _NET_WM_STATE_TOGGLE = 2; /* toggle property */
300:
301: boolean supportChecked = false;
302: long NetWindow = 0;
303:
304: void detect() {
305: if (supportChecked) {
306: // TODO: How about detecting WM-restart or exit?
307: return;
308: }
309: NetWindow = checkAnchor(XA_NET_SUPPORTING_WM_CHECK,
310: XAtom.XA_WINDOW);
311: supportChecked = true;
312: if (log.isLoggable(Level.FINE))
313: log.fine("### " + this + " is active: " + (NetWindow != 0));
314: }
315:
316: boolean active() {
317: detect();
318: return NetWindow != 0;
319: }
320:
321: boolean doStateProtocol() {
322: boolean res = active()
323: && checkProtocol(XA_NET_SUPPORTED, XA_NET_WM_STATE);
324: return res;
325: }
326:
327: boolean doLayerProtocol() {
328: boolean res = active()
329: && checkProtocol(XA_NET_SUPPORTED,
330: XA_NET_WM_STATE_ABOVE);
331: return res;
332: }
333:
334: boolean doModalityProtocol() {
335: boolean res = active()
336: && checkProtocol(XA_NET_SUPPORTED,
337: XA_NET_WM_STATE_MODAL);
338: return res;
339: }
340:
341: boolean isWMName(String name) {
342: if (!active()) {
343: return false;
344: }
345: String net_wm_name_string = getWMName();
346: if (net_wm_name_string == null) {
347: return false;
348: }
349: if (log.isLoggable(Level.FINE))
350: log.fine("### WM_NAME = " + net_wm_name_string);
351: return net_wm_name_string.startsWith(name);
352: }
353:
354: String net_wm_name_cache;
355:
356: public String getWMName() {
357: if (!active()) {
358: return null;
359: }
360:
361: if (net_wm_name_cache != null) {
362: return net_wm_name_cache;
363: }
364:
365: /*
366: * Check both UTF8_STRING and STRING. We only call this function
367: * with ASCII names and UTF8 preserves ASCII bit-wise. wm-spec
368: * mandates UTF8_STRING for _NET_WM_NAME but at least sawfish-1.0
369: * still uses STRING. (mmm, moving targets...).
370: */
371: String charSet = "UTF8";
372: byte[] net_wm_name = XA_NET_WM_NAME.getByteArrayProperty(
373: NetWindow, XA_UTF8_STRING.getAtom());
374: if (net_wm_name == null) {
375: net_wm_name = XA_NET_WM_NAME.getByteArrayProperty(
376: NetWindow, XAtom.XA_STRING);
377: charSet = "ASCII";
378: }
379:
380: if (net_wm_name == null) {
381: return null;
382: }
383: try {
384: net_wm_name_cache = new String(net_wm_name, charSet);
385: return net_wm_name_cache;
386: } catch (java.io.UnsupportedEncodingException uex) {
387: return null;
388: }
389: }
390:
391: /**
392: * Sets _NET_WM_ICON property on the window using the List of XIconInfo
393: * If icons is null or empty list, removes _NET_WM_ICON property
394: */
395: public void setWMIcons(XWindowPeer window,
396: java.util.List<XIconInfo> icons) {
397: if (window == null)
398: return;
399:
400: XAtom iconsAtom = XAtom.get("_NET_WM_ICON");
401: if (icons == null) {
402: iconsAtom.DeleteProperty(window);
403: return;
404: }
405:
406: int length = 0;
407: for (XIconInfo ii : icons) {
408: length += ii.getRawLength();
409: }
410: int cardinalSize = (XlibWrapper.dataModel == 32) ? 4 : 8;
411: int bufferSize = length * cardinalSize;
412:
413: if (bufferSize != 0) {
414: long buffer = XlibWrapper.unsafe.allocateMemory(bufferSize);
415: try {
416: long ptr = buffer;
417: for (XIconInfo ii : icons) {
418: int size = ii.getRawLength() * cardinalSize;
419: if (XlibWrapper.dataModel == 32) {
420: XlibWrapper.copyIntArray(ptr, ii.getIntData(),
421: size);
422: } else {
423: XlibWrapper.copyLongArray(ptr,
424: ii.getLongData(), size);
425: }
426: ptr += size;
427: }
428: iconsAtom.setAtomData(window.getWindow(),
429: XAtom.XA_CARDINAL, buffer, bufferSize
430: / Native.getCard32Size());
431: } finally {
432: XlibWrapper.unsafe.freeMemory(buffer);
433: }
434: } else {
435: iconsAtom.DeleteProperty(window);
436: }
437: }
438:
439: public boolean isWMStateNetHidden(XWindowPeer window) {
440: if (!doStateProtocol()) {
441: return false;
442: }
443: XAtomList state = window.getNETWMState();
444: return (state != null && state.size() != 0 && state
445: .contains(XA_NET_WM_STATE_HIDDEN));
446: }
447: }
|