allow screens to be removed, don't return a screen for an invalid screen id
[notion/jeffpc.git] / ioncore / screen.c
blobd6e98f8fd786a5c36a8eb9508188ce18676db512
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, const WFitParams *fp, int id)
48 Window win;
49 XSetWindowAttributes attr;
50 ulong attrflags=0;
52 scr->id=id;
53 scr->atom_workspace=None;
54 scr->managed_off.x=0;
55 scr->managed_off.y=0;
56 scr->managed_off.w=0;
57 scr->managed_off.h=0;
58 scr->next_scr=NULL;
59 scr->prev_scr=NULL;
61 watch_init(&(scr->notifywin_watch));
62 watch_init(&(scr->infowin_watch));
64 attr.background_pixmap=ParentRelative;
65 attrflags=CWBackPixmap;
67 win=XCreateWindow(ioncore_g.dpy, WROOTWIN_ROOT(parent),
68 fp->g.x, fp->g.y, fp->g.w, fp->g.h, 0,
69 DefaultDepth(ioncore_g.dpy, parent->xscr),
70 InputOutput,
71 DefaultVisual(ioncore_g.dpy, parent->xscr),
72 attrflags, &attr);
73 if(win==None)
74 return FALSE;
76 if(!mplex_do_init((WMPlex*)scr, (WWindow*)parent, fp, win, "WScreen")){
77 XDestroyWindow(ioncore_g.dpy, win);
78 return FALSE;
81 /*scr->mplex.win.region.rootwin=rootwin;
82 region_set_parent((WRegion*)scr, (WRegion*)rootwin);*/
83 scr->mplex.flags|=MPLEX_ADD_TO_END;
84 scr->mplex.win.region.flags|=REGION_BINDINGS_ARE_GRABBED;
86 scr->mplex.win.region.flags|=REGION_MAPPED;
87 window_select_input((WWindow*)scr, IONCORE_EVENTMASK_SCREEN);
89 if(id==0){
90 scr->atom_workspace=XInternAtom(ioncore_g.dpy,
91 "_ION_WORKSPACE", False);
92 }else if(id>=0){
93 char *str;
94 libtu_asprintf(&str, "_ION_WORKSPACE%d", id);
95 if(str!=NULL){
96 scr->atom_workspace=XInternAtom(ioncore_g.dpy, str, False);
97 free(str);
101 /* Add all the needed bindings here; mplex does nothing so that
102 * frames don't have to remove extra bindings.
104 region_add_bindmap((WRegion*)scr, ioncore_screen_bindmap);
105 region_add_bindmap((WRegion*)scr, ioncore_mplex_bindmap);
106 region_add_bindmap((WRegion*)scr, ioncore_mplex_toplevel_bindmap);
108 LINK_ITEM(ioncore_g.screens, scr, next_scr, prev_scr);
110 return TRUE;
114 WScreen *create_screen(WRootWin *parent, const WFitParams *fp, int id)
116 CREATEOBJ_IMPL(WScreen, screen, (p, parent, fp, id));
120 void screen_deinit(WScreen *scr)
122 UNLINK_ITEM(ioncore_g.screens, scr, next_scr, prev_scr);
124 mplex_deinit((WMPlex*)scr);
128 /*}}}*/
131 /*{{{ Attach/detach */
134 void screen_managed_geom(WScreen *scr, WRectangle *geom)
136 geom->x=scr->managed_off.x;
137 geom->y=scr->managed_off.y;
138 geom->w=REGION_GEOM(scr).w+scr->managed_off.w;
139 geom->h=REGION_GEOM(scr).h+scr->managed_off.h;
140 geom->w=maxof(geom->w, 0);
141 geom->h=maxof(geom->h, 0);
145 static bool screen_handle_drop(WScreen *scr, int x, int y, WRegion *dropped)
147 WRegion *curr=mplex_mx_current(&(scr->mplex));
149 /* This code should handle dropping tabs on floating workspaces. */
150 if(curr && HAS_DYN(curr, region_handle_drop)){
151 int rx, ry;
152 region_rootpos(curr, &rx, &ry);
153 if(rectangle_contains(&REGION_GEOM(curr), x-rx, y-ry)){
154 if(region_handle_drop(curr, x, y, dropped))
155 return TRUE;
159 /* Do not attach to ourselves unlike generic WMPlex. */
160 return FALSE;
164 /*}}}*/
167 /*{{{ Region dynfun implementations */
170 static void screen_managed_changed(WScreen *scr, int mode, bool sw,
171 WRegion *reg_)
173 if(ioncore_g.opmode==IONCORE_OPMODE_DEINIT)
174 return;
176 if(sw && scr->atom_workspace!=None){
177 WRegion *reg=mplex_mx_current(&(scr->mplex));
178 const char *n=NULL;
180 if(reg!=NULL)
181 n=region_displayname(reg);
183 xwindow_set_string_property(region_root_of((WRegion*)scr),
184 scr->atom_workspace,
185 n==NULL ? "" : n);
188 if(region_is_activity_r((WRegion*)scr))
189 screen_update_notifywin(scr);
191 screen_update_infowin(scr);
193 mplex_call_changed_hook((WMPlex*)scr,
194 screen_managed_changed_hook,
195 mode, sw, reg_);
199 static void screen_map(WScreen *scr)
201 mplex_map((WMPlex*)scr);
205 static void screen_unmap(WScreen *scr)
207 mplex_unmap((WMPlex*)scr);
210 void screen_inactivated(WScreen *scr)
212 screen_update_infowin(scr);
216 void screen_activated(WScreen *scr)
218 screen_update_infowin(scr);
222 /*}}}*/
225 /*{{{ Misc. */
228 /*EXTL_DOC
229 * Find the screen with numerical id \var{id}.
231 EXTL_SAFE
232 EXTL_EXPORT
233 WScreen *ioncore_find_screen_id(int id)
235 WScreen *scr=NULL;
237 FOR_ALL_SCREENS(scr){
238 if(scr->id==id)
239 return scr;
242 return NULL;
246 /*EXTL_DOC
247 * Switch focus to the screen with id \var{id} and return it.
249 * Note that this function is asynchronous; the screen will not
250 * actually have received the focus when this function returns.
252 EXTL_EXPORT
253 WScreen *ioncore_goto_nth_screen(int id)
255 WScreen *scr=ioncore_find_screen_id(id);
256 if(scr!=NULL){
257 if(!region_goto((WRegion*)scr))
258 return NULL;
260 return scr;
264 static WScreen *current_screen()
266 if(ioncore_g.focus_current==NULL)
267 return ioncore_g.screens;
268 else
269 return region_screen_of(ioncore_g.focus_current);
273 /*EXTL_DOC
274 * Switch focus to the next screen and return it.
276 * Note that this function is asynchronous; the screen will not
277 * actually have received the focus when this function returns.
279 EXTL_EXPORT
280 WScreen *ioncore_goto_next_screen()
282 WScreen *scr=current_screen();
284 if(scr!=NULL)
285 scr=scr->next_scr;
286 if(scr==NULL)
287 scr=ioncore_g.screens;
288 if(scr!=NULL){
289 if(!region_goto((WRegion*)scr))
290 return NULL;
292 return scr;
296 /*EXTL_DOC
297 * Switch focus to the previous screen and return it.
299 * Note that this function is asynchronous; the screen will not
300 * actually have received the focus when this function returns.
302 EXTL_EXPORT
303 WScreen *ioncore_goto_prev_screen()
305 WScreen *scr=current_screen();
307 if(scr!=NULL)
308 scr=scr->prev_scr;
309 else
310 scr=ioncore_g.screens;
311 if(scr!=NULL){
312 if(!region_goto((WRegion*)scr))
313 return NULL;
315 return scr;
319 /*EXTL_DOC
320 * Return the numerical id for screen \var{scr}.
322 EXTL_SAFE
323 EXTL_EXPORT_MEMBER
324 int screen_id(WScreen *scr)
326 return scr->id;
330 static WRegion *screen_managed_disposeroot(WScreen *scr, WRegion *reg)
333 I'm not so sure this is neccessary and it interferes somewhat with
334 multi-monitor workspace rearrangements
336 bool onmxlist=FALSE, others=FALSE;
337 WLListNode *lnode;
338 WLListIterTmp tmp;
340 if(OBJ_IS(reg, WGroupWS)){
341 FOR_ALL_NODES_ON_LLIST(lnode, scr->mplex.mx_list, tmp){
342 if(lnode->st->reg==reg){
343 onmxlist=TRUE;
344 }else if(OBJ_IS(lnode->st->reg, WGroupWS)){
345 others=TRUE;
346 break;
350 if(onmxlist && !others){
351 warn(TR("Only workspace may not be destroyed/detached."));
352 return NULL;
357 return reg;
361 static bool screen_may_dispose(WScreen *scr)
363 return TRUE;
368 void screen_set_managed_offset(WScreen *scr, const WRectangle *off)
370 scr->managed_off=*off;
371 mplex_fit_managed((WMPlex*)scr);
375 /*EXTL_DOC
376 * Set offset of objects managed by the screen from actual screen geometry.
377 * The table \var{offset} should contain the entries \code{x}, \code{y},
378 * \code{w} and \code{h} indicating offsets of that component of screen
379 * geometry.
381 EXTL_EXPORT_AS(WScreen, set_managed_offset)
382 bool screen_set_managed_offset_extl(WScreen *scr, ExtlTab offset)
384 WRectangle g;
386 if(!extl_table_to_rectangle(offset, &g))
387 goto err;
389 if(-g.w>=REGION_GEOM(scr).w)
390 goto err;
391 if(-g.h>=REGION_GEOM(scr).h)
392 goto err;
394 screen_set_managed_offset(scr, &g);
396 return TRUE;
397 err:
398 warn(TR("Invalid offset."));
399 return FALSE;
403 /*}}}*/
406 /*{{{ Save/load */
409 ExtlTab screen_get_configuration(WScreen *scr)
411 return mplex_get_configuration(&scr->mplex);
415 static WRegion *do_create_initial(WWindow *parent, const WFitParams *fp,
416 WRegionLoadCreateFn *fn)
418 return fn(parent, fp, extl_table_none());
422 static bool create_initial_ws(WScreen *scr)
424 WRegion *reg=NULL;
425 WMPlexAttachParams par=MPLEXATTACHPARAMS_INIT;
426 ExtlTab lo=ioncore_get_layout("default");
428 if(lo==extl_table_none()){
429 reg=mplex_do_attach_new(&scr->mplex, &par,
430 (WRegionCreateFn*)create_groupws, NULL);
431 }else{
432 reg=mplex_attach_new_(&scr->mplex, &par, 0, lo);
433 extl_unref_table(lo);
436 if(reg==NULL){
437 warn(TR("Unable to create a workspace on screen %d."), scr->id);
438 return FALSE;
441 return TRUE;
445 bool screen_init_layout(WScreen *scr, ExtlTab tab)
447 char *name;
448 ExtlTab substab, subtab;
449 int n, i;
451 if(tab==extl_table_none())
452 return create_initial_ws(scr);
454 mplex_load_contents(&scr->mplex, tab);
456 return TRUE;
459 /*}}}*/
462 /*{{{ Dynamic function table and class implementation */
465 static DynFunTab screen_dynfuntab[]={
466 {region_map,
467 screen_map},
469 {region_unmap,
470 screen_unmap},
472 {region_activated,
473 screen_activated},
475 {region_inactivated,
476 screen_inactivated},
478 {(DynFun*)region_managed_disposeroot,
479 (DynFun*)screen_managed_disposeroot},
481 {(DynFun*)region_may_dispose,
482 (DynFun*)screen_may_dispose},
484 {mplex_managed_changed,
485 screen_managed_changed},
487 {region_managed_notify,
488 screen_managed_notify},
490 {mplex_managed_geom,
491 screen_managed_geom},
493 {(DynFun*)region_get_configuration,
494 (DynFun*)screen_get_configuration},
496 {(DynFun*)region_handle_drop,
497 (DynFun*)screen_handle_drop},
499 END_DYNFUNTAB
503 EXTL_EXPORT
504 IMPLCLASS(WScreen, WMPlex, screen_deinit, screen_dynfuntab);
507 /*}}}*/