Revert "Remove dead 'uses_root' flag from WScreen"
[notion.git] / ioncore / screen.c
bloba16e7955aa6d94c76c92ca940bbbc72a0ae45837
1 /*
2 * ion/ioncore/screen.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
7 */
9 #include <string.h>
11 #include <libtu/objp.h>
12 #include <libtu/minmax.h>
14 #include "common.h"
15 #include "global.h"
16 #include "screen.h"
17 #include "region.h"
18 #include "attach.h"
19 #include "manage.h"
20 #include "focus.h"
21 #include "property.h"
22 #include "names.h"
23 #include "reginfo.h"
24 #include "saveload.h"
25 #include "resize.h"
26 #include "event.h"
27 #include "bindmaps.h"
28 #include "regbind.h"
29 #include "frame-pointer.h"
30 #include "rectangle.h"
31 #include "extlconv.h"
32 #include "llist.h"
33 #include "group-ws.h"
34 #include "mplex.h"
35 #include "conf.h"
36 #include "activity.h"
37 #include "screen-notify.h"
40 WHook *screen_managed_changed_hook=NULL;
43 /*{{{ Init/deinit */
46 bool screen_init(WScreen *scr, WRootWin *parent,
47 const WFitParams *fp, int id, Window rootwin)
49 Window win;
50 XSetWindowAttributes attr;
51 ulong attrflags=0;
52 bool is_root=FALSE;
54 scr->id=id;
55 scr->atom_workspace=None;
56 scr->managed_off.x=0;
57 scr->managed_off.y=0;
58 scr->managed_off.w=0;
59 scr->managed_off.h=0;
60 scr->next_scr=NULL;
61 scr->prev_scr=NULL;
63 watch_init(&(scr->notifywin_watch));
64 watch_init(&(scr->infowin_watch));
66 if(parent==NULL){
67 win=rootwin;
68 is_root=TRUE;
69 }else{
70 attr.background_pixmap=ParentRelative;
71 attrflags=CWBackPixmap;
73 win=XCreateWindow(ioncore_g.dpy, WROOTWIN_ROOT(parent),
74 fp->g.x, fp->g.y, fp->g.w, fp->g.h, 0,
75 DefaultDepth(ioncore_g.dpy, parent->xscr),
76 InputOutput,
77 DefaultVisual(ioncore_g.dpy, parent->xscr),
78 attrflags, &attr);
79 if(win==None)
80 return FALSE;
83 if(!mplex_do_init((WMPlex*)scr, (WWindow*)parent, fp, win, "WScreen")){
84 if(!is_root)
85 XDestroyWindow(ioncore_g.dpy, win);
86 return FALSE;
89 /*scr->mplex.win.region.rootwin=rootwin;
90 region_set_parent((WRegion*)scr, (WRegion*)rootwin);*/
91 scr->mplex.flags|=MPLEX_ADD_TO_END;
92 scr->mplex.win.region.flags|=REGION_BINDINGS_ARE_GRABBED;
94 if(!is_root){
95 scr->mplex.win.region.flags|=REGION_MAPPED;
96 window_select_input((WWindow*)scr, IONCORE_EVENTMASK_SCREEN);
99 if(id==0){
100 scr->atom_workspace=XInternAtom(ioncore_g.dpy,
101 "_ION_WORKSPACE", False);
102 }else if(id>=0){
103 char *str;
104 libtu_asprintf(&str, "_ION_WORKSPACE%d", id);
105 if(str!=NULL){
106 scr->atom_workspace=XInternAtom(ioncore_g.dpy, str, False);
107 free(str);
111 /* Add all the needed bindings here; mplex does nothing so that
112 * frames don't have to remove extra bindings.
114 region_add_bindmap((WRegion*)scr, ioncore_screen_bindmap);
115 region_add_bindmap((WRegion*)scr, ioncore_mplex_bindmap);
116 region_add_bindmap((WRegion*)scr, ioncore_mplex_toplevel_bindmap);
118 LINK_ITEM(ioncore_g.screens, scr, next_scr, prev_scr);
120 return TRUE;
124 WScreen *create_screen(WRootWin *parent, const WFitParams *fp, int id)
126 CREATEOBJ_IMPL(WScreen, screen, (p, parent, fp, id, None));
130 void screen_deinit(WScreen *scr)
132 UNLINK_ITEM(ioncore_g.screens, scr, next_scr, prev_scr);
134 mplex_deinit((WMPlex*)scr);
138 /*}}}*/
141 /*{{{ Attach/detach */
144 void screen_managed_geom(WScreen *scr, WRectangle *geom)
146 geom->x=scr->managed_off.x;
147 geom->y=scr->managed_off.y;
148 geom->w=REGION_GEOM(scr).w+scr->managed_off.w;
149 geom->h=REGION_GEOM(scr).h+scr->managed_off.h;
150 geom->w=maxof(geom->w, 0);
151 geom->h=maxof(geom->h, 0);
155 static bool screen_handle_drop(WScreen *scr, int x, int y, WRegion *dropped)
157 WRegion *curr=mplex_mx_current(&(scr->mplex));
159 /* This code should handle dropping tabs on floating workspaces. */
160 if(curr && HAS_DYN(curr, region_handle_drop)){
161 int rx, ry;
162 region_rootpos(curr, &rx, &ry);
163 if(rectangle_contains(&REGION_GEOM(curr), x-rx, y-ry)){
164 if(region_handle_drop(curr, x, y, dropped))
165 return TRUE;
169 /* Do not attach to ourselves unlike generic WMPlex. */
170 return FALSE;
174 /*}}}*/
177 /*{{{ Region dynfun implementations */
180 static void screen_managed_changed(WScreen *scr, int mode, bool sw,
181 WRegion *reg_)
183 if(ioncore_g.opmode==IONCORE_OPMODE_DEINIT)
184 return;
186 if(sw && scr->atom_workspace!=None){
187 WRegion *reg=mplex_mx_current(&(scr->mplex));
188 const char *n=NULL;
190 if(reg!=NULL)
191 n=region_displayname(reg);
193 xwindow_set_string_property(region_root_of((WRegion*)scr),
194 scr->atom_workspace,
195 n==NULL ? "" : n);
198 if(region_is_activity_r((WRegion*)scr))
199 screen_update_notifywin(scr);
201 screen_update_infowin(scr);
203 mplex_call_changed_hook((WMPlex*)scr,
204 screen_managed_changed_hook,
205 mode, sw, reg_);
209 static void screen_map(WScreen *scr)
211 if(scr->uses_root)
212 return;
213 mplex_map((WMPlex*)scr);
217 static void screen_unmap(WScreen *scr)
219 if(scr->uses_root)
220 return;
221 mplex_unmap((WMPlex*)scr);
224 void screen_inactivated(WScreen *scr)
226 screen_update_infowin(scr);
230 void screen_activated(WScreen *scr)
232 screen_update_infowin(scr);
236 /*}}}*/
239 /*{{{ Misc. */
242 /*EXTL_DOC
243 * Find the screen with numerical id \var{id}.
245 EXTL_SAFE
246 EXTL_EXPORT
247 WScreen *ioncore_find_screen_id(int id)
249 WScreen *scr, *maxscr=NULL;
251 FOR_ALL_SCREENS(scr){
252 if(id==-1){
253 if(maxscr==NULL || scr->id>maxscr->id)
254 maxscr=scr;
256 if(scr->id==id)
257 return scr;
260 return maxscr;
264 /*EXTL_DOC
265 * Switch focus to the screen with id \var{id} and return it.
267 * Note that this function is asynchronous; the screen will not
268 * actually have received the focus when this function returns.
270 EXTL_EXPORT
271 WScreen *ioncore_goto_nth_screen(int id)
273 WScreen *scr=ioncore_find_screen_id(id);
274 if(scr!=NULL){
275 if(!region_goto((WRegion*)scr))
276 return NULL;
278 return scr;
282 static WScreen *current_screen()
284 if(ioncore_g.focus_current==NULL)
285 return ioncore_g.screens;
286 else
287 return region_screen_of(ioncore_g.focus_current);
291 /*EXTL_DOC
292 * Switch focus to the next screen and return it.
294 * Note that this function is asynchronous; the screen will not
295 * actually have received the focus when this function returns.
297 EXTL_EXPORT
298 WScreen *ioncore_goto_next_screen()
300 WScreen *scr=current_screen();
302 if(scr!=NULL)
303 scr=scr->next_scr;
304 if(scr==NULL)
305 scr=ioncore_g.screens;
306 if(scr!=NULL){
307 if(!region_goto((WRegion*)scr))
308 return NULL;
310 return scr;
314 /*EXTL_DOC
315 * Switch focus to the previous screen and return it.
317 * Note that this function is asynchronous; the screen will not
318 * actually have received the focus when this function returns.
320 EXTL_EXPORT
321 WScreen *ioncore_goto_prev_screen()
323 WScreen *scr=current_screen();
325 if(scr!=NULL)
326 scr=scr->prev_scr;
327 else
328 scr=ioncore_g.screens;
329 if(scr!=NULL){
330 if(!region_goto((WRegion*)scr))
331 return NULL;
333 return scr;
337 /*EXTL_DOC
338 * Return the numerical id for screen \var{scr}.
340 EXTL_SAFE
341 EXTL_EXPORT_MEMBER
342 int screen_id(WScreen *scr)
344 return scr->id;
348 static WRegion *screen_managed_disposeroot(WScreen *scr, WRegion *reg)
350 bool onmxlist=FALSE, others=FALSE;
351 WLListNode *lnode;
352 WLListIterTmp tmp;
354 if(OBJ_IS(reg, WGroupWS)){
355 FOR_ALL_NODES_ON_LLIST(lnode, scr->mplex.mx_list, tmp){
356 if(lnode->st->reg==reg){
357 onmxlist=TRUE;
358 }else if(OBJ_IS(lnode->st->reg, WGroupWS)){
359 others=TRUE;
360 break;
364 if(onmxlist && !others){
365 warn(TR("Only workspace may not be destroyed/detached."));
366 return NULL;
370 return reg;
374 static bool screen_may_dispose(WScreen *scr)
376 warn(TR("Screens may not be destroyed."));
377 return FALSE;
382 void screen_set_managed_offset(WScreen *scr, const WRectangle *off)
384 scr->managed_off=*off;
385 mplex_fit_managed((WMPlex*)scr);
389 /*EXTL_DOC
390 * Set offset of objects managed by the screen from actual screen geometry.
391 * The table \var{offset} should contain the entries \code{x}, \code{y},
392 * \code{w} and \code{h} indicating offsets of that component of screen
393 * geometry.
395 EXTL_EXPORT_AS(WScreen, set_managed_offset)
396 bool screen_set_managed_offset_extl(WScreen *scr, ExtlTab offset)
398 WRectangle g;
400 if(!extl_table_to_rectangle(offset, &g))
401 goto err;
403 if(-g.w>=REGION_GEOM(scr).w)
404 goto err;
405 if(-g.h>=REGION_GEOM(scr).h)
406 goto err;
408 screen_set_managed_offset(scr, &g);
410 return TRUE;
411 err:
412 warn(TR("Invalid offset."));
413 return FALSE;
417 /*}}}*/
420 /*{{{ Save/load */
423 ExtlTab screen_get_configuration(WScreen *scr)
425 return mplex_get_configuration(&scr->mplex);
429 static WRegion *do_create_initial(WWindow *parent, const WFitParams *fp,
430 WRegionLoadCreateFn *fn)
432 return fn(parent, fp, extl_table_none());
436 static bool create_initial_ws(WScreen *scr)
438 WRegion *reg=NULL;
439 WMPlexAttachParams par=MPLEXATTACHPARAMS_INIT;
440 ExtlTab lo=ioncore_get_layout("default");
442 if(lo==extl_table_none()){
443 reg=mplex_do_attach_new(&scr->mplex, &par,
444 (WRegionCreateFn*)create_groupws, NULL);
445 }else{
446 reg=mplex_attach_new_(&scr->mplex, &par, 0, lo);
447 extl_unref_table(lo);
450 if(reg==NULL){
451 warn(TR("Unable to create a workspace on screen %d."), scr->id);
452 return FALSE;
455 return TRUE;
459 bool screen_init_layout(WScreen *scr, ExtlTab tab)
461 char *name;
462 ExtlTab substab, subtab;
463 int n, i;
465 if(tab==extl_table_none())
466 return create_initial_ws(scr);
468 mplex_load_contents(&scr->mplex, tab);
470 return TRUE;
473 /*}}}*/
476 /*{{{ Dynamic function table and class implementation */
479 static DynFunTab screen_dynfuntab[]={
480 {region_map,
481 screen_map},
483 {region_unmap,
484 screen_unmap},
486 {region_activated,
487 screen_activated},
489 {region_inactivated,
490 screen_inactivated},
492 {(DynFun*)region_managed_disposeroot,
493 (DynFun*)screen_managed_disposeroot},
495 {(DynFun*)region_may_dispose,
496 (DynFun*)screen_may_dispose},
498 {mplex_managed_changed,
499 screen_managed_changed},
501 {region_managed_notify,
502 screen_managed_notify},
504 {mplex_managed_geom,
505 screen_managed_geom},
507 {(DynFun*)region_get_configuration,
508 (DynFun*)screen_get_configuration},
510 {(DynFun*)region_handle_drop,
511 (DynFun*)screen_handle_drop},
513 END_DYNFUNTAB
517 EXTL_EXPORT
518 IMPLCLASS(WScreen, WMPlex, screen_deinit, screen_dynfuntab);
521 /*}}}*/