4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
12 #include <libtu/util.h>
15 #include "fullscreen.h"
16 #include "clientwin.h"
29 static Atom atom_net_wm_name
=0;
30 static Atom atom_net_wm_state
=0;
31 static Atom atom_net_wm_state_fullscreen
=0;
32 static Atom atom_net_wm_state_demands_attention
=0;
33 static Atom atom_net_supporting_wm_check
=0;
34 static Atom atom_net_virtual_roots
=0;
35 static Atom atom_net_active_window
=0;
36 static Atom atom_net_wm_user_time
=0;
37 static Atom atom_net_wm_allowed_actions
=0;
38 static Atom atom_net_wm_moveresize
=0;
42 static Atom atom_net_supported
=0;
44 #define SOURCE_UNKNOWN 0
45 #define SOURCE_APPLICATION 1
46 #define SOURCE_PAGER 2
51 /*{{{ Initialisation */
56 atom_net_wm_name
=XInternAtom(ioncore_g
.dpy
, "_NET_WM_NAME", False
);
57 atom_net_wm_state
=XInternAtom(ioncore_g
.dpy
, "_NET_WM_STATE", False
);
58 atom_net_wm_state_fullscreen
=XInternAtom(ioncore_g
.dpy
, "_NET_WM_STATE_FULLSCREEN", False
);
59 atom_net_wm_state_demands_attention
=XInternAtom(ioncore_g
.dpy
, "_NET_WM_STATE_DEMANDS_ATTENTION", False
);
60 atom_net_supported
=XInternAtom(ioncore_g
.dpy
, "_NET_SUPPORTED", False
);
61 atom_net_supporting_wm_check
=XInternAtom(ioncore_g
.dpy
, "_NET_SUPPORTING_WM_CHECK", False
);
62 atom_net_virtual_roots
=XInternAtom(ioncore_g
.dpy
, "_NET_VIRTUAL_ROOTS", False
);
63 atom_net_active_window
=XInternAtom(ioncore_g
.dpy
, "_NET_ACTIVE_WINDOW", False
);
64 atom_net_wm_user_time
=XInternAtom(ioncore_g
.dpy
, "_NET_WM_USER_TIME", False
);
65 atom_net_wm_allowed_actions
=XInternAtom(ioncore_g
.dpy
, "_NET_WM_ALLOWED_ACTIONS", False
);
66 atom_net_wm_moveresize
=XInternAtom(ioncore_g
.dpy
, "_NET_WM_MOVERESIZE", False
);
70 void netwm_init_rootwin(WRootWin
*rw
)
75 atoms
[0]=atom_net_wm_name
;
76 atoms
[1]=atom_net_wm_state
;
77 atoms
[2]=atom_net_wm_state_fullscreen
;
78 atoms
[3]=atom_net_wm_state_demands_attention
;
79 atoms
[4]=atom_net_supporting_wm_check
;
80 atoms
[5]=atom_net_virtual_roots
;
81 atoms
[6]=atom_net_active_window
;
82 atoms
[7]=atom_net_wm_allowed_actions
;
83 atoms
[8]=atom_net_wm_moveresize
;
85 XChangeProperty(ioncore_g
.dpy
, WROOTWIN_ROOT(rw
),
86 atom_net_supporting_wm_check
, XA_WINDOW
,
87 32, PropModeReplace
, (uchar
*)&(rw
->dummy_win
), 1);
88 XChangeProperty(ioncore_g
.dpy
, rw
->dummy_win
,
89 atom_net_supporting_wm_check
, XA_WINDOW
,
90 32, PropModeReplace
, (uchar
*)&(rw
->dummy_win
), 1);
91 XChangeProperty(ioncore_g
.dpy
, WROOTWIN_ROOT(rw
),
92 atom_net_supported
, XA_ATOM
,
93 32, PropModeReplace
, (uchar
*)atoms
, N_NETWM
);
95 p
[0]=libtu_progbasename();
97 * Unfortunately we cannot determine the charset of libtu_progbasename()
98 * so we'll just have to guess it makes sense in the current locale charset
100 xwindow_set_utf8_property(rw
->dummy_win
, atom_net_wm_name
, p
, 1);
107 /*{{{ _NET_WM_STATE */
110 bool netwm_check_initial_fullscreen(WClientWin
*cwin
)
117 n
=xwindow_get_property(cwin
->win
, atom_net_wm_state
, XA_ATOM
,
118 1, TRUE
, (uchar
**)&data
);
124 if(data
[i
]==(long)atom_net_wm_state_fullscreen
)
134 * refresh \_NET\_WM\_STATE markers for this window
138 void ioncore_update_net_state(WClientWin
*cwin
)
140 netwm_update_state(cwin
);
143 void netwm_update_state(WClientWin
*cwin
)
148 if(REGION_IS_FULLSCREEN(cwin
))
149 data
[n
++]=atom_net_wm_state_fullscreen
;
150 if(region_is_activity_r(&(cwin
->region
)))
151 data
[n
++]=atom_net_wm_state_demands_attention
;
153 XChangeProperty(ioncore_g
.dpy
, cwin
->win
, atom_net_wm_state
,
154 XA_ATOM
, 32, PropModeReplace
, (uchar
*)data
, n
);
157 void netwm_update_allowed_actions(WClientWin
*cwin
)
162 /* TODO add support for 'resize' and list it here */
163 /* TODO add support for 'minimize' and list it here */
164 /* TODO add support for 'maximize_horz' and list it here */
165 /* TODO add support for 'maximize_vert' and list it here */
166 /* TODO add support for 'fullscreen' and list it here */
167 /* TODO add support for 'change desktop' and list it here */
168 /* TODO add support for 'close' and list it here */
169 /* TODO add support for 'above' and list it here */
170 /* TODO add support for 'below' and list it here */
172 XChangeProperty(ioncore_g
.dpy
, cwin
->win
, atom_net_wm_allowed_actions
,
173 XA_ATOM
, 32, PropModeReplace
, (uchar
*)data
, n
);
177 void netwm_delete_state(WClientWin
*cwin
)
179 XDeleteProperty(ioncore_g
.dpy
, cwin
->win
, atom_net_wm_state
);
184 static void netwm_state_change_rq(WClientWin
*cwin
,
185 const XClientMessageEvent
*ev
)
187 if((ev
->data
.l
[1]==0 ||
188 ev
->data
.l
[1]!=(long)atom_net_wm_state_fullscreen
) &&
190 ev
->data
.l
[2]!=(long)atom_net_wm_state_fullscreen
)){
194 /* Ok, full screen add/remove/toggle */
195 if(!REGION_IS_FULLSCREEN(cwin
)){
196 if(ev
->data
.l
[0]==_NET_WM_STATE_ADD
||
197 ev
->data
.l
[0]==_NET_WM_STATE_TOGGLE
){
198 WRegion
*grp
=region_groupleader_of((WRegion
*)cwin
);
199 bool sw
=clientwin_fullscreen_may_switchto(cwin
);
200 cwin
->flags
|=CLIENTWIN_FS_RQ
;
201 if(!region_enter_fullscreen(grp
, sw
))
202 cwin
->flags
&=~CLIENTWIN_FS_RQ
;
204 /* Should not be set.. */
205 cwin
->flags
&=~CLIENTWIN_FS_RQ
;
208 if(ev
->data
.l
[0]==_NET_WM_STATE_REMOVE
||
209 ev
->data
.l
[0]==_NET_WM_STATE_TOGGLE
){
210 WRegion
*grp
=region_groupleader_of((WRegion
*)cwin
);
211 bool sw
=clientwin_fullscreen_may_switchto(cwin
);
212 cwin
->flags
&=~CLIENTWIN_FS_RQ
;
213 region_leave_fullscreen(grp
, sw
);
216 cwin
->flags
|=CLIENTWIN_FS_RQ
;
225 /*{{{ _NET_ACTIVE_WINDOW */
228 void netwm_set_active(WRegion
*reg
)
230 CARD32 data
[1]={None
};
232 if(OBJ_IS(reg
, WClientWin
))
233 data
[0]=region_xwindow(reg
);
235 /* The spec doesn't say how multihead should be handled, so
236 * we just update the root window the window is on.
238 XChangeProperty(ioncore_g
.dpy
, region_root_of(reg
),
239 atom_net_active_window
, XA_WINDOW
,
240 32, PropModeReplace
, (uchar
*)data
, 1);
244 static void netwm_active_window_rq(WClientWin
*cwin
,
245 const XClientMessageEvent
*ev
)
247 long source
=ev
->data
.l
[0];
249 * By default we ignore non-pager activity requests, as they're known to
250 * steal focus from newly-created windows :(
252 bool ignore
=source
!=SOURCE_PAGER
;
254 extl_table_gets_b(cwin
->proptab
, "ignore_net_active_window", &ignore
);
257 region_goto((WRegion
*)cwin
);
259 region_set_activity((WRegion
*)cwin
, SETPARAM_SET
);
266 /*{{{ _NET_WM_NAME */
269 char **netwm_get_name(WClientWin
*cwin
)
271 return xwindow_get_text_property(cwin
->win
, atom_net_wm_name
, NULL
);
278 /*{{{ netwm_handle_client_message */
281 void netwm_handle_client_message(const XClientMessageEvent
*ev
)
283 /* Check _NET_WM_STATE fullscreen request */
284 if(ev
->message_type
==atom_net_wm_state
&& ev
->format
==32){
285 WClientWin
*cwin
=XWINDOW_REGION_OF_T(ev
->window
, WClientWin
);
287 netwm_state_change_rq(cwin
, ev
);
289 /* Check _NET_ACTIVE_WINDOW request */
290 else if(ev
->message_type
==atom_net_active_window
&& ev
->format
==32){
291 WClientWin
*cwin
=XWINDOW_REGION_OF_T(ev
->window
, WClientWin
);
293 netwm_active_window_rq(cwin
, ev
);
301 /*{{{ netwm_handle_property */
304 bool netwm_handle_property(WClientWin
*cwin
, const XPropertyEvent
*ev
)
306 if(ev
->atom
!=atom_net_wm_name
)
309 clientwin_get_set_name(cwin
);
319 /** When a new window is mapped, look at the netwm user time to find out
320 * whether the new window should be switched to and get the focus.
322 * It is unclear what the desired behavior would be, and how we should takee
323 * into consideration ioncore_g.usertime_diff_new and IONCORE_CLOCK_SKEW_MS,
324 * so for now we deny raising the new window only in the special case where
325 * its user time is set to 0, specifically preventing it from being raised.
327 void netwm_check_manage_user_time(WClientWin
*cwin
, WManageParams
*param
)
329 /* the currently focussed window */
330 WClientWin
*cur
=OBJ_CAST(ioncore_g
.focus_current
, WClientWin
);
332 Window win
=region_xwindow((WRegion
*)cwin
);
333 Time now
=ioncore_get_timestamp(); /* TODO: should really use the event.. */
334 /* user time, current window user time */
336 /* whether the new (got) and current (gotcut) windows had their usertime
338 bool got
=FALSE
, gotcut
=FALSE
;
346 curwin
=region_xwindow((WRegion
*)cur
);
347 gotcut
=xwindow_get_cardinal_property(curwin
, atom_net_wm_user_time
, &cut
);
350 got
=xwindow_get_cardinal_property(win
, atom_net_wm_user_time
, &ut
);
352 /* The special value of zero on a newly mapped window can be used to
353 * request that the window not be initially focused when it is mapped */
357 /* there was some other logic here, but it was not clear how it was meant
358 * to work and prevented newly created windows from receiving the focus
360 * (https://sourceforge.net/tracker/?func=detail&aid=3109576&group_id=314802&atid=1324528)
361 * Stripped until we decide how this is supposed to behave.
365 param
->switchto
=FALSE
;
373 /*{{{ _NET_WM_VIRTUAL_ROOTS */
380 FOR_ALL_SCREENS(scr
){
388 * refresh \_NET\_WM\_VIRTUAL\_ROOTS
392 void ioncore_screens_updated(WRootWin
*rw
)
394 int current_screen
= 0;
396 CARD32
*virtualroots
;
399 n_screens
= count_screens();
400 virtualroots
= (CARD32
*)malloc(n_screens
* sizeof(CARD32
));
402 FOR_ALL_SCREENS(scr
){
403 virtualroots
[current_screen
] = region_xwindow((WRegion
*)scr
);
407 XChangeProperty(ioncore_g
.dpy
, WROOTWIN_ROOT(rw
),
408 atom_net_virtual_roots
, XA_WINDOW
,
409 32, PropModeReplace
, (uchar
*)virtualroots
, n_screens
);