Update _NET_VIRTUAL_ROOTS to identify the WScreen(s)
[notion/jeffpc.git] / ioncore / netwm.c
blob4090c462b3e3939bd1dd2c14a9fbad227de30385
1 /*
2 * ion/ioncore/netwm.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
7 */
9 #include <X11/Xatom.h>
10 #include <X11/Xmd.h>
12 #include <libtu/util.h>
13 #include "common.h"
14 #include "global.h"
15 #include "fullscreen.h"
16 #include "clientwin.h"
17 #include "netwm.h"
18 #include "property.h"
19 #include "activity.h"
20 #include "focus.h"
21 #include "xwindow.h"
22 #include "extlconv.h"
23 #include "group.h"
24 #include "event.h"
27 /*{{{ Atoms */
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;
40 #define N_NETWM 9
42 static Atom atom_net_supported=0;
44 #define SOURCE_UNKNOWN 0
45 #define SOURCE_APPLICATION 1
46 #define SOURCE_PAGER 2
48 /*}}}*/
51 /*{{{ Initialisation */
54 void netwm_init()
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)
72 Atom atoms[N_NETWM];
73 const char *p[1];
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();
96 /**
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);
104 /*}}}*/
107 /*{{{ _NET_WM_STATE */
110 bool netwm_check_initial_fullscreen(WClientWin *cwin)
113 int i, n;
114 int ret=0;
115 long *data;
117 n=xwindow_get_property(cwin->win, atom_net_wm_state, XA_ATOM,
118 1, TRUE, (uchar**)&data);
120 if(n<0)
121 return FALSE;
123 for(i=0; i<n; i++){
124 if(data[i]==(long)atom_net_wm_state_fullscreen)
125 return TRUE;
128 XFree((void*)data);
130 return FALSE;
133 /*EXTL_DOC
134 * refresh \_NET\_WM\_STATE markers for this window
136 EXTL_SAFE
137 EXTL_EXPORT
138 void ioncore_update_net_state(WClientWin *cwin)
140 netwm_update_state(cwin);
143 void netwm_update_state(WClientWin *cwin)
145 CARD32 data[2];
146 int n=0;
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)
159 CARD32 data[1];
160 int n=0;
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) &&
189 (ev->data.l[2]==0 ||
190 ev->data.l[2]!=(long)atom_net_wm_state_fullscreen)){
191 return;
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;
203 }else{
204 /* Should not be set.. */
205 cwin->flags&=~CLIENTWIN_FS_RQ;
207 }else{
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);
214 }else{
215 /* Set the flag */
216 cwin->flags|=CLIENTWIN_FS_RQ;
222 /*}}}*/
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);
256 if(!ignore)
257 region_goto((WRegion*)cwin);
258 else
259 region_set_activity((WRegion*)cwin, SETPARAM_SET);
263 /*}}}*/
266 /*{{{ _NET_WM_NAME */
269 char **netwm_get_name(WClientWin *cwin)
271 return xwindow_get_text_property(cwin->win, atom_net_wm_name, NULL);
275 /*}}}*/
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);
286 if(cwin!=NULL)
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);
292 if(cwin!=NULL)
293 netwm_active_window_rq(cwin, ev);
298 /*}}}*/
301 /*{{{ netwm_handle_property */
304 bool netwm_handle_property(WClientWin *cwin, const XPropertyEvent *ev)
306 if(ev->atom!=atom_net_wm_name)
307 return FALSE;
309 clientwin_get_set_name(cwin);
310 return TRUE;
314 /*}}}*/
317 /*{{{ user time */
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);
331 /* the new window */
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 */
335 CARD32 ut=0, cut=0;
336 /* whether the new (got) and current (gotcut) windows had their usertime
337 * set */
338 bool got=FALSE, gotcut=FALSE;
339 bool nofocus=FALSE;
341 if(cur!=NULL){
342 Window curwin;
343 CARD32 cut;
344 if(param->tfor==cur)
345 return;
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 */
354 if (got && ut == 0)
355 nofocus = TRUE;
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
359 * in some cases
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.
364 if(nofocus){
365 param->switchto=FALSE;
366 param->jumpto=FALSE;
371 /*}}}*/
373 /*{{{ _NET_WM_VIRTUAL_ROOTS */
375 int count_screens()
377 int result = 0;
378 WScreen *scr;
380 FOR_ALL_SCREENS(scr){
381 result++;
384 return result;
387 /*EXTL_DOC
388 * refresh \_NET\_WM\_VIRTUAL\_ROOTS
390 EXTL_SAFE
391 EXTL_EXPORT
392 void ioncore_screens_updated(WRootWin *rw)
394 int current_screen = 0;
395 int n_screens;
396 CARD32 *virtualroots;
397 WScreen *scr;
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);
404 current_screen++;
407 XChangeProperty(ioncore_g.dpy, WROOTWIN_ROOT(rw),
408 atom_net_virtual_roots, XA_WINDOW,
409 32, PropModeReplace, (uchar*)virtualroots, n_screens);
412 /*}}}*/