4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
11 #include <libtu/objp.h>
12 #include <libtu/minmax.h>
29 #include "frame-pointer.h"
30 #include "rectangle.h"
37 #include "screen-notify.h"
40 WHook *screen_managed_changed_hook=NULL;
46 bool screen_init(WScreen *scr, WRootWin *parent,
47 const WFitParams *fp, int id, Window rootwin)
50 XSetWindowAttributes attr;
55 scr->atom_workspace=None;
63 watch_init(&(scr->notifywin_watch));
64 watch_init(&(scr->infowin_watch));
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),
77 DefaultVisual(ioncore_g.dpy, parent->xscr),
84 if(!mplex_do_init((WMPlex*)scr, (WWindow*)parent, fp, win)){
86 XDestroyWindow(ioncore_g.dpy, win);
90 /*scr->mplex.win.region.rootwin=rootwin;
91 region_set_parent((WRegion*)scr, (WRegion*)rootwin);*/
92 scr->mplex.flags|=MPLEX_ADD_TO_END;
93 scr->mplex.win.region.flags|=REGION_BINDINGS_ARE_GRABBED;
96 scr->mplex.win.region.flags|=REGION_MAPPED;
97 window_select_input((WWindow*)scr, IONCORE_EVENTMASK_SCREEN);
101 scr->atom_workspace=XInternAtom(ioncore_g.dpy,
102 "_ION_WORKSPACE", False);
105 libtu_asprintf(&str, "_ION_WORKSPACE%d", id);
107 scr->atom_workspace=XInternAtom(ioncore_g.dpy, str, False);
112 /* Add all the needed bindings here; mplex does nothing so that
113 * frames don't have to remove extra bindings.
115 region_add_bindmap((WRegion*)scr, ioncore_screen_bindmap);
116 region_add_bindmap((WRegion*)scr, ioncore_mplex_bindmap);
117 region_add_bindmap((WRegion*)scr, ioncore_mplex_toplevel_bindmap);
119 LINK_ITEM(ioncore_g.screens, scr, next_scr, prev_scr);
125 WScreen *create_screen(WRootWin *parent, const WFitParams *fp, int id)
127 CREATEOBJ_IMPL(WScreen, screen, (p, parent, fp, id, None));
131 void screen_deinit(WScreen *scr)
133 UNLINK_ITEM(ioncore_g.screens, scr, next_scr, prev_scr);
135 mplex_deinit((WMPlex*)scr);
142 /*{{{ Attach/detach */
145 void screen_managed_geom(WScreen *scr, WRectangle *geom)
147 geom->x=scr->managed_off.x;
148 geom->y=scr->managed_off.y;
149 geom->w=REGION_GEOM(scr).w+scr->managed_off.w;
150 geom->h=REGION_GEOM(scr).h+scr->managed_off.h;
151 geom->w=maxof(geom->w, 0);
152 geom->h=maxof(geom->h, 0);
156 static bool screen_handle_drop(WScreen *scr, int x, int y, WRegion *dropped)
158 WRegion *curr=mplex_mx_current(&(scr->mplex));
160 /* This code should handle dropping tabs on floating workspaces. */
161 if(curr && HAS_DYN(curr, region_handle_drop)){
163 region_rootpos(curr, &rx, &ry);
164 if(rectangle_contains(®ION_GEOM(curr), x-rx, y-ry)){
165 if(region_handle_drop(curr, x, y, dropped))
170 /* Do not attach to ourselves unlike generic WMPlex. */
178 /*{{{ Region dynfun implementations */
181 static bool screen_fitrep(WScreen *scr, WWindow *par, const WFitParams *fp)
191 return mplex_fitrep((WMPlex*)scr, NULL, fp);
197 static void screen_managed_changed(WScreen *scr, int mode, bool sw,
200 if(ioncore_g.opmode==IONCORE_OPMODE_DEINIT)
203 if(sw && scr->atom_workspace!=None){
204 WRegion *reg=mplex_mx_current(&(scr->mplex));
208 n=region_displayname(reg);
210 xwindow_set_string_property(region_root_of((WRegion*)scr),
215 if(region_is_activity_r((WRegion*)scr))
216 screen_update_notifywin(scr);
218 screen_update_infowin(scr);
220 mplex_call_changed_hook((WMPlex*)scr,
221 screen_managed_changed_hook,
226 static void screen_map(WScreen *scr)
230 mplex_map((WMPlex*)scr);
234 static void screen_unmap(WScreen *scr)
238 mplex_unmap((WMPlex*)scr);
241 void screen_inactivated(WScreen *scr)
243 screen_update_infowin(scr);
247 void screen_activated(WScreen *scr)
249 screen_update_infowin(scr);
260 * Find the screen with numerical id \var{id}.
264 WScreen *ioncore_find_screen_id(int id)
266 WScreen *scr, *maxscr=NULL;
268 FOR_ALL_SCREENS(scr){
270 if(maxscr==NULL || scr->id>maxscr->id)
282 * Switch focus to the screen with id \var{id} and return it.
284 * Note that this function is asynchronous; the screen will not
285 * actually have received the focus when this function returns.
288 WScreen *ioncore_goto_nth_screen(int id)
290 WScreen *scr=ioncore_find_screen_id(id);
292 if(!region_goto((WRegion*)scr))
299 static WScreen *current_screen()
301 if(ioncore_g.focus_current==NULL)
302 return ioncore_g.screens;
304 return region_screen_of(ioncore_g.focus_current);
309 * Switch focus to the next screen and return it.
311 * Note that this function is asynchronous; the screen will not
312 * actually have received the focus when this function returns.
315 WScreen *ioncore_goto_next_screen()
317 WScreen *scr=current_screen();
322 scr=ioncore_g.screens;
324 if(!region_goto((WRegion*)scr))
332 * Switch focus to the previous screen and return it.
334 * Note that this function is asynchronous; the screen will not
335 * actually have received the focus when this function returns.
338 WScreen *ioncore_goto_prev_screen()
340 WScreen *scr=current_screen();
345 scr=ioncore_g.screens;
347 if(!region_goto((WRegion*)scr))
355 * Return the numerical id for screen \var{scr}.
359 int screen_id(WScreen *scr)
365 static WRegion *screen_managed_disposeroot(WScreen *scr, WRegion *reg)
367 bool onmxlist=FALSE, others=FALSE;
371 if(OBJ_IS(reg, WGroupWS)){
372 FOR_ALL_NODES_ON_LLIST(lnode, scr->mplex.mx_list, tmp){
373 if(lnode->st->reg==reg){
375 }else if(OBJ_IS(lnode->st->reg, WGroupWS)){
381 if(onmxlist && !others){
382 warn(TR("Only workspace may not be destroyed/detached."));
391 static bool screen_may_dispose(WScreen *scr)
393 warn(TR("Screens may not be destroyed."));
399 void screen_set_managed_offset(WScreen *scr, const WRectangle *off)
401 scr->managed_off=*off;
402 mplex_fit_managed((WMPlex*)scr);
407 * Set offset of objects managed by the screen from actual screen geometry.
408 * The table \var{offset} should contain the entries \code{x}, \code{y},
409 * \code{w} and \code{h} indicating offsets of that component of screen
412 EXTL_EXPORT_AS(WScreen, set_managed_offset)
413 bool screen_set_managed_offset_extl(WScreen *scr, ExtlTab offset)
417 if(!extl_table_to_rectangle(offset, &g))
420 if(-g.w>=REGION_GEOM(scr).w)
422 if(-g.h>=REGION_GEOM(scr).h)
425 screen_set_managed_offset(scr, &g);
429 warn(TR("Invalid offset."));
440 ExtlTab screen_get_configuration(WScreen *scr)
442 return mplex_get_configuration(&scr->mplex);
446 static WRegion *do_create_initial(WWindow *parent, const WFitParams *fp,
447 WRegionLoadCreateFn *fn)
449 return fn(parent, fp, extl_table_none());
453 static bool create_initial_ws(WScreen *scr)
456 WMPlexAttachParams par=MPLEXATTACHPARAMS_INIT;
457 ExtlTab lo=ioncore_get_layout("default");
459 if(lo==extl_table_none()){
460 reg=mplex_do_attach_new(&scr->mplex, &par,
461 (WRegionCreateFn*)create_groupws, NULL);
463 reg=mplex_attach_new_(&scr->mplex, &par, 0, lo);
464 extl_unref_table(lo);
468 warn(TR("Unable to create a workspace on screen %d."), scr->id);
476 bool screen_init_layout(WScreen *scr, ExtlTab tab)
479 ExtlTab substab, subtab;
482 if(tab==extl_table_none())
483 return create_initial_ws(scr);
485 mplex_load_contents(&scr->mplex, tab);
493 /*{{{ Dynamic function table and class implementation */
496 static DynFunTab screen_dynfuntab[]={
509 {(DynFun*)region_managed_disposeroot,
510 (DynFun*)screen_managed_disposeroot},
512 {(DynFun*)region_may_dispose,
513 (DynFun*)screen_may_dispose},
515 {mplex_managed_changed,
516 screen_managed_changed},
518 {region_managed_notify,
519 screen_managed_notify},
522 screen_managed_geom},
524 {(DynFun*)region_get_configuration,
525 (DynFun*)screen_get_configuration},
527 {(DynFun*)region_handle_drop,
528 (DynFun*)screen_handle_drop},
530 {(DynFun*)region_fitrep,
531 (DynFun*)screen_fitrep},
538 IMPLCLASS(WScreen, WMPlex, screen_deinit, screen_dynfuntab);