4 * Copyright (c) Tuomo Valkonen 1999-2007.
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"
47 WHook *screen_managed_changed_hook=NULL;
50 static void screen_update_infowin(WScreen *scr);
57 bool screen_init(WScreen *scr, WRootWin *parent,
58 const WFitParams *fp, int id, Window rootwin)
61 XSetWindowAttributes attr;
66 scr->atom_workspace=None;
73 scr->rotation=SCREEN_ROTATION_0;
75 watch_init(&(scr->notifywin_watch));
76 watch_init(&(scr->infowin_watch));
82 attr.background_pixmap=ParentRelative;
83 attrflags=CWBackPixmap;
85 win=XCreateWindow(ioncore_g.dpy, WROOTWIN_ROOT(parent),
86 fp->g.x, fp->g.y, fp->g.w, fp->g.h, 0,
87 DefaultDepth(ioncore_g.dpy, parent->xscr),
89 DefaultVisual(ioncore_g.dpy, parent->xscr),
96 if(!mplex_do_init((WMPlex*)scr, (WWindow*)parent, fp, win)){
98 XDestroyWindow(ioncore_g.dpy, win);
102 /*scr->mplex.win.region.rootwin=rootwin;
103 region_set_parent((WRegion*)scr, (WRegion*)rootwin);*/
104 scr->mplex.flags|=MPLEX_ADD_TO_END;
105 scr->mplex.win.region.flags|=REGION_BINDINGS_ARE_GRABBED;
108 scr->mplex.win.region.flags|=REGION_MAPPED;
109 window_select_input((WWindow*)scr, IONCORE_EVENTMASK_SCREEN);
113 scr->atom_workspace=XInternAtom(ioncore_g.dpy,
114 "_ION_WORKSPACE", False);
117 libtu_asprintf(&str, "_ION_WORKSPACE%d", id);
119 scr->atom_workspace=XInternAtom(ioncore_g.dpy, str, False);
124 /* Add all the needed bindings here; mplex does nothing so that
125 * frames don't have to remove extra bindings.
127 region_add_bindmap((WRegion*)scr, ioncore_screen_bindmap);
128 region_add_bindmap((WRegion*)scr, ioncore_mplex_bindmap);
129 region_add_bindmap((WRegion*)scr, ioncore_mplex_toplevel_bindmap);
131 LINK_ITEM(ioncore_g.screens, scr, next_scr, prev_scr);
137 WScreen *create_screen(WRootWin *parent, const WFitParams *fp, int id)
139 CREATEOBJ_IMPL(WScreen, screen, (p, parent, fp, id, None));
143 void screen_deinit(WScreen *scr)
145 UNLINK_ITEM(ioncore_g.screens, scr, next_scr, prev_scr);
147 mplex_deinit((WMPlex*)scr);
154 /*{{{ Attach/detach */
157 void screen_managed_geom(WScreen *scr, WRectangle *geom)
159 geom->x=scr->managed_off.x;
160 geom->y=scr->managed_off.y;
161 geom->w=REGION_GEOM(scr).w+scr->managed_off.w;
162 geom->h=REGION_GEOM(scr).h+scr->managed_off.h;
163 geom->w=maxof(geom->w, 0);
164 geom->h=maxof(geom->h, 0);
168 static bool screen_handle_drop(WScreen *scr, int x, int y, WRegion *dropped)
170 WRegion *curr=mplex_mx_current(&(scr->mplex));
172 /* This code should handle dropping tabs on floating workspaces. */
173 if(curr && HAS_DYN(curr, region_handle_drop)){
175 region_rootpos(curr, &rx, &ry);
176 if(rectangle_contains(®ION_GEOM(curr), x-rx, y-ry)){
177 if(region_handle_drop(curr, x, y, dropped))
182 /* Do not attach to ourselves unlike generic WMPlex. */
190 /*{{{ Region dynfun implementations */
193 static bool screen_fitrep(WScreen *scr, WWindow *par, const WFitParams *fp)
203 return mplex_fitrep((WMPlex*)scr, NULL, fp);
209 static void screen_managed_changed(WScreen *scr, int mode, bool sw,
212 if(ioncore_g.opmode==IONCORE_OPMODE_DEINIT)
215 if(sw && scr->atom_workspace!=None){
216 WRegion *reg=mplex_mx_current(&(scr->mplex));
220 n=region_displayname(reg);
222 xwindow_set_string_property(region_root_of((WRegion*)scr),
227 screen_update_infowin(scr);
229 mplex_call_changed_hook((WMPlex*)scr,
230 screen_managed_changed_hook,
235 static void screen_map(WScreen *scr)
239 mplex_map((WMPlex*)scr);
243 static void screen_unmap(WScreen *scr)
247 mplex_unmap((WMPlex*)scr);
250 void screen_inactivated(WScreen *scr)
252 screen_update_infowin(scr);
256 void screen_activated(WScreen *scr)
258 screen_update_infowin(scr);
268 /*{{{ Notifications */
271 static void do_notify(WScreen *scr, Watch *watch, bool right,
272 const char *str, char *style)
275 WInfoWin *iw=(WInfoWin*)(watch->obj);
279 WMPlexAttachParams param=MPLEXATTACHPARAMS_INIT;
281 param.flags=(MPLEX_ATTACH_UNNUMBERED|
282 MPLEX_ATTACH_SIZEPOLICY|
285 param.level=STACKING_LEVEL_ON_TOP;
288 param.szplcy=SIZEPOLICY_GRAVITY_NORTHWEST;
291 param.szplcy=SIZEPOLICY_GRAVITY_NORTHEAST;
292 param.geom.x=REGION_GEOM(scr).w-1;
299 iw=(WInfoWin*)mplex_do_attach_new(&scr->mplex, ¶m,
300 (WRegionCreateFn*)create_infowin,
306 watch_setup(watch, (Obj*)iw, NULL);
309 infowin_set_text(iw, str);
313 void screen_notify(WScreen *scr, const char *str)
315 do_notify(scr, &scr->notifywin_watch, FALSE, str, "actnotify");
319 void screen_windowinfo(WScreen *scr, const char *str)
321 do_notify(scr, &scr->infowin_watch, TRUE, str, "tab-info");
325 void screen_unnotify(WScreen *scr)
327 Obj *iw=scr->notifywin_watch.obj;
329 watch_reset(&(scr->notifywin_watch));
330 region_dispose((WRegion*)iw, FALSE);
335 void screen_nowindowinfo(WScreen *scr)
337 Obj *iw=scr->infowin_watch.obj;
339 watch_reset(&(scr->infowin_watch));
340 region_dispose((WRegion*)iw, FALSE);
345 static char *addnot(char *str, WRegion *reg)
347 const char *nm=region_name(reg);
354 return scat(TR("act: "), nm);
356 nstr=scat3(str, ", ", nm);
363 static char *screen_managed_activity(WScreen *scr)
369 FOR_ALL_MANAGED_BY_MPLEX(&scr->mplex, reg, tmp){
370 if(region_is_activity_r(reg) && !REGION_IS_MAPPED(reg))
371 notstr=addnot(notstr, reg);
378 static void screen_notify_activity(WScreen *scr)
380 if(ioncore_g.screen_notify){
381 char *notstr=screen_managed_activity(scr);
383 screen_notify(scr, notstr);
389 screen_unnotify(scr);
391 screen_update_infowin(scr);
395 static void screen_notify_tag(WScreen *scr)
397 screen_update_infowin(scr);
402 GR_DEFATTR(inactive);
403 GR_DEFATTR(selected);
405 GR_DEFATTR(not_tagged);
406 GR_DEFATTR(not_dragged);
407 GR_DEFATTR(activity);
408 GR_DEFATTR(no_activity);
411 static void init_attr()
414 GR_ALLOCATTR(active);
415 GR_ALLOCATTR(inactive);
416 GR_ALLOCATTR(selected);
417 GR_ALLOCATTR(tagged);
418 GR_ALLOCATTR(not_tagged);
419 GR_ALLOCATTR(not_dragged);
420 GR_ALLOCATTR(no_activity);
421 GR_ALLOCATTR(activity);
426 static void screen_update_infowin(WScreen *scr)
428 WRegion *reg=mplex_mx_current(&(scr->mplex));
429 bool tag=(reg!=NULL && region_is_tagged(reg));
430 bool act=(reg!=NULL && region_is_activity_r(reg) && !REGION_IS_ACTIVE(scr));
431 bool sac=REGION_IS_ACTIVE(scr);
434 const char *n=region_displayname(reg);
437 screen_windowinfo(scr, n);
439 iw=(WInfoWin*)scr->infowin_watch.obj;
442 GrStyleSpec *spec=infowin_stylespec(iw);
446 gr_stylespec_unalloc(spec);
448 gr_stylespec_set(spec, GR_ATTR(selected));
449 gr_stylespec_set(spec, GR_ATTR(not_dragged));
450 gr_stylespec_set(spec, sac ? GR_ATTR(active) : GR_ATTR(inactive));
451 gr_stylespec_set(spec, tag ? GR_ATTR(tagged) : GR_ATTR(not_tagged));
452 gr_stylespec_set(spec, act ? GR_ATTR(activity) : GR_ATTR(no_activity));
456 screen_nowindowinfo(scr);
461 static void screen_managed_notify(WScreen *scr, WRegion *reg, WRegionNotify how)
463 if(how==ioncore_g.notifies.sub_activity){
464 /* TODO: multiple calls */
465 mainloop_defer_action((Obj*)scr,
466 (WDeferredAction*)screen_notify_activity);
467 }else if(how==ioncore_g.notifies.tag){
468 mainloop_defer_action((Obj*)scr,
469 (WDeferredAction*)screen_notify_tag);
481 * Find the screen with numerical id \var{id}.
485 WScreen *ioncore_find_screen_id(int id)
487 WScreen *scr, *maxscr=NULL;
489 FOR_ALL_SCREENS(scr){
491 if(maxscr==NULL || scr->id>maxscr->id)
503 * Switch focus to the screen with id \var{id} and return it.
505 * Note that this function is asynchronous; the screen will not
506 * actually have received the focus when this function returns.
509 WScreen *ioncore_goto_nth_screen(int id)
511 WScreen *scr=ioncore_find_screen_id(id);
513 if(!region_goto((WRegion*)scr))
520 static WScreen *current_screen()
522 if(ioncore_g.focus_current==NULL)
523 return ioncore_g.screens;
525 return region_screen_of(ioncore_g.focus_current);
530 * Switch focus to the next screen and return it.
532 * Note that this function is asynchronous; the screen will not
533 * actually have received the focus when this function returns.
536 WScreen *ioncore_goto_next_screen()
538 WScreen *scr=current_screen();
543 scr=ioncore_g.screens;
545 if(!region_goto((WRegion*)scr))
553 * Switch focus to the previous screen and return it.
555 * Note that this function is asynchronous; the screen will not
556 * actually have received the focus when this function returns.
559 WScreen *ioncore_goto_prev_screen()
561 WScreen *scr=current_screen();
566 scr=ioncore_g.screens;
568 if(!region_goto((WRegion*)scr))
576 * Return the numerical id for screen \var{scr}.
580 int screen_id(WScreen *scr)
586 static bool screen_managed_may_destroy(WScreen *scr, WRegion *reg)
592 if(OBJ_IS(reg, WClientWin))
595 FOR_ALL_NODES_ON_LLIST(lnode, scr->mplex.mx_list, tmp){
596 if(lnode->st->reg==reg)
598 else /*if(OBJ_IS(node->reg, WGenWS))*/
605 warn(TR("Only workspace may not be destroyed."));
611 static bool screen_may_destroy(WScreen *scr)
613 warn(TR("Screens may not be destroyed."));
619 void screen_set_managed_offset(WScreen *scr, const WRectangle *off)
621 scr->managed_off=*off;
622 mplex_fit_managed((WMPlex*)scr);
627 * Set offset of objects managed by the screen from actual screen geometry.
628 * The table \var{offset} should contain the entries \code{x}, \code{y},
629 * \code{w} and \code{h} indicating offsets of that component of screen
632 EXTL_EXPORT_AS(WScreen, set_managed_offset)
633 bool screen_set_managed_offset_extl(WScreen *scr, ExtlTab offset)
637 if(!extl_table_to_rectangle(offset, &g))
640 if(-g.w>=REGION_GEOM(scr).w)
642 if(-g.h>=REGION_GEOM(scr).h)
645 screen_set_managed_offset(scr, &g);
649 warn(TR("Invalid offset."));
654 WPHolder *screen_get_rescue_pholder_for(WScreen *scr, WRegion *mgd)
656 #warning "TODO: better special case handling for groups"
658 return (WPHolder*)mplex_get_rescue_pholder_for(&(scr->mplex), mgd);
667 ExtlTab screen_get_configuration(WScreen *scr)
669 return mplex_get_configuration(&scr->mplex);
673 static WRegion *do_create_initial(WWindow *parent, const WFitParams *fp,
674 WRegionLoadCreateFn *fn)
676 return fn(parent, fp, extl_table_none());
680 static bool create_initial_ws(WScreen *scr)
683 WMPlexAttachParams par=MPLEXATTACHPARAMS_INIT;
684 ExtlTab lo=ioncore_get_layout("default");
686 if(lo==extl_table_none()){
687 reg=mplex_do_attach_new(&scr->mplex, &par,
688 (WRegionCreateFn*)create_groupws, NULL);
690 reg=mplex_attach_new_(&scr->mplex, &par, 0, lo);
691 extl_unref_table(lo);
695 warn(TR("Unable to create a workspace on screen %d."), scr->id);
703 bool screen_init_layout(WScreen *scr, ExtlTab tab)
706 ExtlTab substab, subtab;
709 if(tab==extl_table_none())
710 return create_initial_ws(scr);
712 mplex_load_contents(&scr->mplex, tab);
720 /*{{{ Dynamic function table and class implementation */
723 static DynFunTab screen_dynfuntab[]={
736 {(DynFun*)region_managed_may_destroy,
737 (DynFun*)screen_managed_may_destroy},
739 {(DynFun*)region_may_destroy,
740 (DynFun*)screen_may_destroy},
742 {mplex_managed_changed,
743 screen_managed_changed},
745 {region_managed_notify,
746 screen_managed_notify},
749 screen_managed_geom},
751 {(DynFun*)region_get_configuration,
752 (DynFun*)screen_get_configuration},
754 {(DynFun*)region_handle_drop,
755 (DynFun*)screen_handle_drop},
757 {(DynFun*)region_fitrep,
758 (DynFun*)screen_fitrep},
760 {(DynFun*)region_get_rescue_pholder_for,
761 (DynFun*)screen_get_rescue_pholder_for},
768 IMPLCLASS(WScreen, WMPlex, screen_deinit, screen_dynfuntab);