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 static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XNETProtocol");
37     private static final 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      */
supportsState(int state)43     public boolean supportsState(int state) {
44         return doStateProtocol() ; // TODO - check for Frame constants
45     }
46 
setState(XWindowPeer window, int state)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 
setInitialState(XWindowPeer window, int state)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 
requestState(XWindowPeer window, int state)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 
getState(XWindowPeer window)132     public int getState(XWindowPeer window) {
133         return getStateImpl(window);
134     }
135 
136     /*
137      * New "NET" WM spec: _NET_WM_STATE/Atom[]
138      */
getStateImpl(XWindowPeer window)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 
isStateChange(XPropertyEvent e)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      */
unshadeKludge(XWindowPeer window)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      */
supportsLayer(int layer)177     public boolean supportsLayer(int layer) {
178         return ((layer == LAYER_ALWAYS_ON_TOP) || (layer == LAYER_NORMAL)) && doLayerProtocol();
179     }
180 
requestState(XWindow window, XAtom state, boolean isAdd)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      */
setStateHelper(XWindowPeer window, XAtom state, boolean set)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 
setLayer(XWindowPeer window, int layer)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     static final int _NET_WM_STATE_REMOVE      =0; /* remove/unset property */
284     static final int _NET_WM_STATE_ADD         =1; /* add/set property      */
285     static final int _NET_WM_STATE_TOGGLE      =2; /* toggle property       */
286 
287     boolean supportChecked = false;
288     long NetWindow = 0;
detect()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 
active()301     boolean active() {
302         detect();
303         return NetWindow != 0;
304     }
305 
doStateProtocol()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 
doLayerProtocol()314     boolean doLayerProtocol() {
315         boolean res = active() && checkProtocol(XA_NET_SUPPORTED, XA_NET_WM_STATE_ABOVE);
316         return res;
317     }
318 
doModalityProtocol()319     boolean doModalityProtocol() {
320         boolean res = active() && checkProtocol(XA_NET_SUPPORTED, XA_NET_WM_STATE_MODAL);
321         return res;
322     }
323 
doOpacityProtocol()324     boolean doOpacityProtocol() {
325         boolean res = active() && checkProtocol(XA_NET_SUPPORTED, XA_NET_WM_WINDOW_OPACITY);
326         return res;
327     }
328 
setActiveWindow(long window)329     public void setActiveWindow(long 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);
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 
isWMName(String name)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;
getWMName()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      */
setWMIcons(XWindowPeer window, java.util.List<IconInfo> icons)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 
isWMStateNetHidden(XWindowPeer window)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 }
453