4 * Copyright (c) Tuomo Valkonen 1999-2006.
6 * Ion is free software; you can redistribute it and/or modify it under
7 * the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
14 #include <libtu/objp.h>
15 #include <libtu/minmax.h>
16 #include <libmainloop/defer.h>
33 #include "frame-pointer.h"
34 #include "rectangle.h"
44 WHook *screen_managed_changed_hook=NULL;
47 static void screen_update_infowin(WScreen *scr);
54 static bool screen_init(WScreen *scr, WRootWin *rootwin,
55 int id, const WFitParams *fp, bool useroot)
58 XSetWindowAttributes attr;
62 scr->atom_workspace=None;
63 scr->uses_root=useroot;
70 scr->rotation=SCREEN_ROTATION_0;
72 watch_init(&(scr->notifywin_watch));
73 watch_init(&(scr->infowin_watch));
76 win=WROOTWIN_ROOT(rootwin);
78 attr.background_pixmap=ParentRelative;
79 attrflags=CWBackPixmap;
81 win=XCreateWindow(ioncore_g.dpy, WROOTWIN_ROOT(rootwin),
82 fp->g.x, fp->g.y, fp->g.w, fp->g.h, 0,
83 DefaultDepth(ioncore_g.dpy, rootwin->xscr),
85 DefaultVisual(ioncore_g.dpy, rootwin->xscr),
91 if(!mplex_do_init((WMPlex*)scr, (WWindow*)rootwin, win, fp, FALSE))
94 /*scr->mplex.win.region.rootwin=rootwin;
95 region_set_parent((WRegion*)scr, (WRegion*)rootwin);*/
96 scr->mplex.flags|=MPLEX_ADD_TO_END;
97 scr->mplex.win.region.flags|=REGION_BINDINGS_ARE_GRABBED;
99 scr->mplex.win.region.flags|=REGION_MAPPED;
101 window_select_input(&(scr->mplex.win),
102 FocusChangeMask|EnterWindowMask|
103 KeyPressMask|KeyReleaseMask|
104 ButtonPressMask|ButtonReleaseMask|
105 (useroot ? IONCORE_EVENTMASK_ROOT : 0));
108 scr->atom_workspace=XInternAtom(ioncore_g.dpy,
109 "_ION_WORKSPACE", False);
112 libtu_asprintf(&str, "_ION_WORKSPACE%d", id);
114 scr->atom_workspace=XInternAtom(ioncore_g.dpy, str, False);
119 /* Add rootwin's bindings to screens (ungrabbed) so that bindings
120 * are called with the proper region.
122 region_add_bindmap((WRegion*)scr, ioncore_rootwin_bindmap);
124 LINK_ITEM(ioncore_g.screens, scr, next_scr, prev_scr);
130 WScreen *create_screen(WRootWin *rootwin, int id, const WFitParams *fp,
133 CREATEOBJ_IMPL(WScreen, screen, (p, rootwin, id, fp, useroot));
137 void screen_deinit(WScreen *scr)
139 UNLINK_ITEM(ioncore_g.screens, scr, next_scr, prev_scr);
142 scr->mplex.win.win=None;
144 mplex_deinit((WMPlex*)scr);
151 /*{{{ Attach/detach */
154 void screen_managed_geom(WScreen *scr, WRectangle *geom)
156 geom->x=scr->managed_off.x;
157 geom->y=scr->managed_off.y;
158 geom->w=REGION_GEOM(scr).w+scr->managed_off.w;
159 geom->h=REGION_GEOM(scr).h+scr->managed_off.h;
160 geom->w=maxof(geom->w, 0);
161 geom->h=maxof(geom->h, 0);
165 static bool screen_handle_drop(WScreen *scr, int x, int y, WRegion *dropped)
167 WRegion *curr=mplex_mx_current(&(scr->mplex));
169 /* This code should handle dropping tabs on floating workspaces. */
170 if(curr && HAS_DYN(curr, region_handle_drop)){
172 region_rootpos(curr, &rx, &ry);
173 if(rectangle_contains(®ION_GEOM(curr), x-rx, y-ry)){
174 if(region_handle_drop(curr, x, y, dropped))
179 /* Do not attach to ourselves unlike generic WMPlex. */
187 /*{{{ Region dynfun implementations */
190 static bool screen_fitrep(WScreen *scr, WWindow *par, const WFitParams *fp)
200 return mplex_fitrep((WMPlex*)scr, NULL, fp);
206 static void screen_managed_changed(WScreen *scr, int mode, bool sw,
209 if(ioncore_g.opmode==IONCORE_OPMODE_DEINIT)
212 if(sw && scr->atom_workspace!=None){
213 WRegion *reg=mplex_mx_current(&(scr->mplex));
217 n=region_displayname(reg);
219 xwindow_set_string_property(region_root_of((WRegion*)scr),
224 screen_update_infowin(scr);
226 mplex_call_changed_hook((WMPlex*)scr,
227 screen_managed_changed_hook,
232 static void screen_map(WScreen *scr)
236 mplex_map((WMPlex*)scr);
240 static void screen_unmap(WScreen *scr)
244 mplex_unmap((WMPlex*)scr);
247 void screen_inactivated(WScreen *scr)
249 screen_update_infowin(scr);
253 void screen_activated(WScreen *scr)
255 screen_update_infowin(scr);
265 /*{{{ Notifications */
268 static void do_notify(WScreen *scr, Watch *watch, bool right,
270 char *style, const char *attr)
273 WInfoWin *iw=(WInfoWin*)(watch->obj);
277 WMPlexAttachParams param=MPLEXATTACHPARAMS_INIT;
279 param.flags=(MPLEX_ATTACH_UNNUMBERED|
280 MPLEX_ATTACH_SIZEPOLICY|
283 param.level=STACKING_LEVEL_ON_TOP;
286 param.szplcy=SIZEPOLICY_GRAVITY_NORTHWEST;
289 param.szplcy=SIZEPOLICY_GRAVITY_NORTHEAST;
290 param.geom.x=REGION_GEOM(scr).w-1;
297 iw=(WInfoWin*)mplex_do_attach_new(&scr->mplex, ¶m,
298 (WRegionCreateFn*)create_infowin,
304 watch_setup(watch, (Obj*)iw, NULL);
307 infowin_set_attr2(iw, attr, NULL);
308 infowin_set_text(iw, str);
312 void screen_notify(WScreen *scr, const char *str)
314 do_notify(scr, &scr->notifywin_watch, FALSE, str, "actnotify", NULL);
318 void screen_windowinfo(WScreen *scr, const char *str, const char *attr)
320 do_notify(scr, &scr->infowin_watch, TRUE, str, "tab-info", attr);
324 void screen_unnotify(WScreen *scr)
326 Obj *iw=scr->notifywin_watch.obj;
328 watch_reset(&(scr->notifywin_watch));
329 mainloop_defer_destroy(iw);
334 void screen_nowindowinfo(WScreen *scr)
336 Obj *iw=scr->infowin_watch.obj;
338 watch_reset(&(scr->infowin_watch));
339 mainloop_defer_destroy(iw);
344 static char *addnot(char *str, WRegion *reg)
346 const char *nm=region_name(reg);
353 return scat(TR("act: "), nm);
355 nstr=scat3(str, ", ", nm);
362 static char *screen_managed_activity(WScreen *scr)
368 FOR_ALL_MANAGED_BY_MPLEX(&scr->mplex, reg, tmp){
369 if(region_is_activity_r(reg) && !REGION_IS_MAPPED(reg))
370 notstr=addnot(notstr, reg);
377 static void screen_notify_activity(WScreen *scr)
379 if(ioncore_g.screen_notify){
380 char *notstr=screen_managed_activity(scr);
382 screen_notify(scr, notstr);
388 screen_unnotify(scr);
390 screen_update_infowin(scr);
394 static void screen_notify_tag(WScreen *scr)
396 screen_update_infowin(scr);
400 static void screen_update_infowin(WScreen *scr)
402 WRegion *reg=mplex_mx_current(&(scr->mplex));
403 bool tag=(reg!=NULL && region_is_tagged(reg));
404 bool act=(reg!=NULL && region_is_activity_r(reg));
407 const char *n=region_displayname(reg);
410 libtu_asprintf(&attr, "%s-selected-%s-not_dragged-%s",
411 (REGION_IS_ACTIVE(scr) ? "active" : "inactive"),
412 (tag ? "tagged" : "not_tagged"),
413 (act ? "activity" : "no_activity"));
415 screen_windowinfo(scr, n, attr); /* NULL attr ok */
417 screen_nowindowinfo(scr);
422 static void screen_managed_notify(WScreen *scr, WRegion *reg, const char *how)
424 if(strcmp(how, "sub-activity")==0){
425 /* TODO: multiple calls */
426 mainloop_defer_action((Obj*)scr,
427 (WDeferredAction*)screen_notify_activity);
428 }else if(strcmp(how, "tag")==0){
429 mainloop_defer_action((Obj*)scr,
430 (WDeferredAction*)screen_notify_tag);
442 * Find the screen with numerical id \var{id}. If Xinerama is
443 * not present, \var{id} corresponds to X screen numbers. Otherwise
444 * the ids are some arbitrary ordering of Xinerama rootwins.
445 * If \var{id} is $-1$, the screen with the highest id is returned.
449 WScreen *ioncore_find_screen_id(int id)
451 WScreen *scr, *maxscr=NULL;
453 FOR_ALL_SCREENS(scr){
455 if(maxscr==NULL || scr->id>maxscr->id)
467 * Switch focus to the screen with id \var{id} and return it.
469 * Note that this function is asynchronous; the screen will not
470 * actually have received the focus when this function returns.
473 WScreen *ioncore_goto_nth_screen(int id)
475 WScreen *scr=ioncore_find_screen_id(id);
477 if(!region_goto((WRegion*)scr))
484 static WScreen *current_screen()
486 if(ioncore_g.focus_current==NULL)
487 return ioncore_g.screens;
489 return region_screen_of(ioncore_g.focus_current);
494 * Switch focus to the next screen and return it.
496 * Note that this function is asynchronous; the screen will not
497 * actually have received the focus when this function returns.
500 WScreen *ioncore_goto_next_screen()
502 WScreen *scr=current_screen();
507 scr=ioncore_g.screens;
509 if(!region_goto((WRegion*)scr))
517 * Switch focus to the previous screen and return it.
519 * Note that this function is asynchronous; the screen will not
520 * actually have received the focus when this function returns.
523 WScreen *ioncore_goto_prev_screen()
525 WScreen *scr=current_screen();
530 scr=ioncore_g.screens;
532 if(!region_goto((WRegion*)scr))
540 * Return the numerical id for screen \var{scr}.
544 int screen_id(WScreen *scr)
550 static bool screen_managed_may_destroy(WScreen *scr, WRegion *reg)
556 if(OBJ_IS(reg, WClientWin))
559 FOR_ALL_NODES_ON_LLIST(lnode, scr->mplex.mx_list, tmp){
560 if(lnode->st->reg==reg)
562 else /*if(OBJ_IS(node->reg, WGenWS))*/
569 warn(TR("Only workspace may not be destroyed."));
575 static bool screen_may_destroy(WScreen *scr)
577 warn(TR("Screens may not be destroyed."));
583 void screen_set_managed_offset(WScreen *scr, const WRectangle *off)
585 scr->managed_off=*off;
586 mplex_fit_managed((WMPlex*)scr);
591 * Set offset of objects managed by the screen from actual screen geometry.
592 * The table \var{offset} should contain the entries \code{x}, \code{y},
593 * \code{w} and \code{h} indicating offsets of that component of screen
596 EXTL_EXPORT_AS(WScreen, set_managed_offset)
597 bool screen_set_managed_offset_extl(WScreen *scr, ExtlTab offset)
601 if(!extl_table_to_rectangle(offset, &g))
604 if(-g.w>=REGION_GEOM(scr).w)
606 if(-g.h>=REGION_GEOM(scr).h)
609 screen_set_managed_offset(scr, &g);
613 warn(TR("Invalid offset."));
618 WPHolder *screen_get_rescue_pholder_for(WScreen *scr, WRegion *mgd)
620 #warning "TODO: better special case handling for groups"
622 return (WPHolder*)mplex_get_rescue_pholder_for(&(scr->mplex), mgd);
631 ExtlTab screen_get_configuration(WScreen *scr)
633 return mplex_get_configuration(&scr->mplex);
637 static WRegion *do_create_initial(WWindow *parent, const WFitParams *fp,
638 WRegionLoadCreateFn *fn)
640 return fn(parent, fp, extl_table_none());
644 static bool create_initial_ws(WScreen *scr)
647 WMPlexAttachParams par;
651 reg=mplex_do_attach_new(&scr->mplex, &par,
652 (WRegionCreateFn*)groupws_load_default,
656 warn(TR("Unable to create a workspace on screen %d."), scr->id);
664 bool screen_init_layout(WScreen *scr, ExtlTab tab)
667 ExtlTab substab, subtab;
670 if(tab==extl_table_none())
671 return create_initial_ws(scr);
673 mplex_load_contents(&scr->mplex, tab);
681 /*{{{ Dynamic function table and class implementation */
684 static DynFunTab screen_dynfuntab[]={
697 {(DynFun*)region_managed_may_destroy,
698 (DynFun*)screen_managed_may_destroy},
700 {(DynFun*)region_may_destroy,
701 (DynFun*)screen_may_destroy},
703 {mplex_managed_changed,
704 screen_managed_changed},
706 {region_managed_notify,
707 screen_managed_notify},
710 screen_managed_geom},
712 {(DynFun*)region_get_configuration,
713 (DynFun*)screen_get_configuration},
715 {(DynFun*)region_handle_drop,
716 (DynFun*)screen_handle_drop},
718 {(DynFun*)region_fitrep,
719 (DynFun*)screen_fitrep},
721 {(DynFun*)region_get_rescue_pholder_for,
722 (DynFun*)screen_get_rescue_pholder_for},
729 IMPLCLASS(WScreen, WMPlex, screen_deinit, screen_dynfuntab);