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>
32 #include "frame-pointer.h"
33 #include "rectangle.h"
40 #include "screen-notify.h"
43 WHook *screen_managed_changed_hook=NULL;
49 bool screen_init(WScreen *scr, WRootWin *parent,
50 const WFitParams *fp, int id, Window rootwin)
53 XSetWindowAttributes attr;
58 scr->atom_workspace=None;
66 watch_init(&(scr->notifywin_watch));
67 watch_init(&(scr->infowin_watch));
73 attr.background_pixmap=ParentRelative;
74 attrflags=CWBackPixmap;
76 win=XCreateWindow(ioncore_g.dpy, WROOTWIN_ROOT(parent),
77 fp->g.x, fp->g.y, fp->g.w, fp->g.h, 0,
78 DefaultDepth(ioncore_g.dpy, parent->xscr),
80 DefaultVisual(ioncore_g.dpy, parent->xscr),
87 if(!mplex_do_init((WMPlex*)scr, (WWindow*)parent, fp, win)){
89 XDestroyWindow(ioncore_g.dpy, win);
93 /*scr->mplex.win.region.rootwin=rootwin;
94 region_set_parent((WRegion*)scr, (WRegion*)rootwin);*/
95 scr->mplex.flags|=MPLEX_ADD_TO_END;
96 scr->mplex.win.region.flags|=REGION_BINDINGS_ARE_GRABBED;
99 scr->mplex.win.region.flags|=REGION_MAPPED;
100 window_select_input((WWindow*)scr, IONCORE_EVENTMASK_SCREEN);
104 scr->atom_workspace=XInternAtom(ioncore_g.dpy,
105 "_ION_WORKSPACE", False);
108 libtu_asprintf(&str, "_ION_WORKSPACE%d", id);
110 scr->atom_workspace=XInternAtom(ioncore_g.dpy, str, False);
115 /* Add all the needed bindings here; mplex does nothing so that
116 * frames don't have to remove extra bindings.
118 region_add_bindmap((WRegion*)scr, ioncore_screen_bindmap);
119 region_add_bindmap((WRegion*)scr, ioncore_mplex_bindmap);
120 region_add_bindmap((WRegion*)scr, ioncore_mplex_toplevel_bindmap);
122 LINK_ITEM(ioncore_g.screens, scr, next_scr, prev_scr);
128 WScreen *create_screen(WRootWin *parent, const WFitParams *fp, int id)
130 CREATEOBJ_IMPL(WScreen, screen, (p, parent, fp, id, None));
134 void screen_deinit(WScreen *scr)
136 UNLINK_ITEM(ioncore_g.screens, scr, next_scr, prev_scr);
138 mplex_deinit((WMPlex*)scr);
145 /*{{{ Attach/detach */
148 void screen_managed_geom(WScreen *scr, WRectangle *geom)
150 geom->x=scr->managed_off.x;
151 geom->y=scr->managed_off.y;
152 geom->w=REGION_GEOM(scr).w+scr->managed_off.w;
153 geom->h=REGION_GEOM(scr).h+scr->managed_off.h;
154 geom->w=maxof(geom->w, 0);
155 geom->h=maxof(geom->h, 0);
159 static bool screen_handle_drop(WScreen *scr, int x, int y, WRegion *dropped)
161 WRegion *curr=mplex_mx_current(&(scr->mplex));
163 /* This code should handle dropping tabs on floating workspaces. */
164 if(curr && HAS_DYN(curr, region_handle_drop)){
166 region_rootpos(curr, &rx, &ry);
167 if(rectangle_contains(®ION_GEOM(curr), x-rx, y-ry)){
168 if(region_handle_drop(curr, x, y, dropped))
173 /* Do not attach to ourselves unlike generic WMPlex. */
181 /*{{{ Region dynfun implementations */
184 static bool screen_fitrep(WScreen *scr, WWindow *par, const WFitParams *fp)
194 return mplex_fitrep((WMPlex*)scr, NULL, fp);
200 static void screen_managed_changed(WScreen *scr, int mode, bool sw,
203 if(ioncore_g.opmode==IONCORE_OPMODE_DEINIT)
206 if(sw && scr->atom_workspace!=None){
207 WRegion *reg=mplex_mx_current(&(scr->mplex));
211 n=region_displayname(reg);
213 xwindow_set_string_property(region_root_of((WRegion*)scr),
218 if(region_is_activity_r((WRegion*)scr))
219 screen_update_notifywin(scr);
221 screen_update_infowin(scr);
223 mplex_call_changed_hook((WMPlex*)scr,
224 screen_managed_changed_hook,
229 static void screen_map(WScreen *scr)
233 mplex_map((WMPlex*)scr);
237 static void screen_unmap(WScreen *scr)
241 mplex_unmap((WMPlex*)scr);
244 void screen_inactivated(WScreen *scr)
246 screen_update_infowin(scr);
250 void screen_activated(WScreen *scr)
252 screen_update_infowin(scr);
263 * Find the screen with numerical id \var{id}.
267 WScreen *ioncore_find_screen_id(int id)
269 WScreen *scr, *maxscr=NULL;
271 FOR_ALL_SCREENS(scr){
273 if(maxscr==NULL || scr->id>maxscr->id)
285 * Switch focus to the screen with id \var{id} and return it.
287 * Note that this function is asynchronous; the screen will not
288 * actually have received the focus when this function returns.
291 WScreen *ioncore_goto_nth_screen(int id)
293 WScreen *scr=ioncore_find_screen_id(id);
295 if(!region_goto((WRegion*)scr))
302 static WScreen *current_screen()
304 if(ioncore_g.focus_current==NULL)
305 return ioncore_g.screens;
307 return region_screen_of(ioncore_g.focus_current);
312 * Switch focus to the next screen and return it.
314 * Note that this function is asynchronous; the screen will not
315 * actually have received the focus when this function returns.
318 WScreen *ioncore_goto_next_screen()
320 WScreen *scr=current_screen();
325 scr=ioncore_g.screens;
327 if(!region_goto((WRegion*)scr))
335 * Switch focus to the previous screen and return it.
337 * Note that this function is asynchronous; the screen will not
338 * actually have received the focus when this function returns.
341 WScreen *ioncore_goto_prev_screen()
343 WScreen *scr=current_screen();
348 scr=ioncore_g.screens;
350 if(!region_goto((WRegion*)scr))
358 * Return the numerical id for screen \var{scr}.
362 int screen_id(WScreen *scr)
368 static WRegion *screen_managed_disposeroot(WScreen *scr, WRegion *reg)
370 bool onmxlist=FALSE, others=FALSE;
374 if(OBJ_IS(reg, WGroupWS)){
375 FOR_ALL_NODES_ON_LLIST(lnode, scr->mplex.mx_list, tmp){
376 if(lnode->st->reg==reg){
378 }else if(OBJ_IS(lnode->st->reg, WGroupWS)){
384 if(onmxlist && !others){
385 warn(TR("Only workspace may not be destroyed/detached."));
394 static bool screen_may_dispose(WScreen *scr)
396 warn(TR("Screens may not be destroyed."));
402 void screen_set_managed_offset(WScreen *scr, const WRectangle *off)
404 scr->managed_off=*off;
405 mplex_fit_managed((WMPlex*)scr);
410 * Set offset of objects managed by the screen from actual screen geometry.
411 * The table \var{offset} should contain the entries \code{x}, \code{y},
412 * \code{w} and \code{h} indicating offsets of that component of screen
415 EXTL_EXPORT_AS(WScreen, set_managed_offset)
416 bool screen_set_managed_offset_extl(WScreen *scr, ExtlTab offset)
420 if(!extl_table_to_rectangle(offset, &g))
423 if(-g.w>=REGION_GEOM(scr).w)
425 if(-g.h>=REGION_GEOM(scr).h)
428 screen_set_managed_offset(scr, &g);
432 warn(TR("Invalid offset."));
443 ExtlTab screen_get_configuration(WScreen *scr)
445 return mplex_get_configuration(&scr->mplex);
449 static WRegion *do_create_initial(WWindow *parent, const WFitParams *fp,
450 WRegionLoadCreateFn *fn)
452 return fn(parent, fp, extl_table_none());
456 static bool create_initial_ws(WScreen *scr)
459 WMPlexAttachParams par=MPLEXATTACHPARAMS_INIT;
460 ExtlTab lo=ioncore_get_layout("default");
462 if(lo==extl_table_none()){
463 reg=mplex_do_attach_new(&scr->mplex, &par,
464 (WRegionCreateFn*)create_groupws, NULL);
466 reg=mplex_attach_new_(&scr->mplex, &par, 0, lo);
467 extl_unref_table(lo);
471 warn(TR("Unable to create a workspace on screen %d."), scr->id);
479 bool screen_init_layout(WScreen *scr, ExtlTab tab)
482 ExtlTab substab, subtab;
485 if(tab==extl_table_none())
486 return create_initial_ws(scr);
488 mplex_load_contents(&scr->mplex, tab);
496 /*{{{ Dynamic function table and class implementation */
499 static DynFunTab screen_dynfuntab[]={
512 {(DynFun*)region_managed_disposeroot,
513 (DynFun*)screen_managed_disposeroot},
515 {(DynFun*)region_may_dispose,
516 (DynFun*)screen_may_dispose},
518 {mplex_managed_changed,
519 screen_managed_changed},
521 {region_managed_notify,
522 screen_managed_notify},
525 screen_managed_geom},
527 {(DynFun*)region_get_configuration,
528 (DynFun*)screen_get_configuration},
530 {(DynFun*)region_handle_drop,
531 (DynFun*)screen_handle_drop},
533 {(DynFun*)region_fitrep,
534 (DynFun*)screen_fitrep},
541 IMPLCLASS(WScreen, WMPlex, screen_deinit, screen_dynfuntab);