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
, const WFitParams
*fp
, int id
)
49 XSetWindowAttributes attr
;
53 scr
->atom_workspace
=None
;
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
),
71 DefaultVisual(ioncore_g
.dpy
, parent
->xscr
),
76 if(!mplex_do_init((WMPlex
*)scr
, (WWindow
*)parent
, fp
, win
, "WScreen")){
77 XDestroyWindow(ioncore_g
.dpy
, win
);
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
);
90 scr
->atom_workspace
=XInternAtom(ioncore_g
.dpy
,
91 "_ION_WORKSPACE", False
);
94 libtu_asprintf(&str
, "_ION_WORKSPACE%d", id
);
96 scr
->atom_workspace
=XInternAtom(ioncore_g
.dpy
, str
, False
);
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
);
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
);
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
)){
152 region_rootpos(curr
, &rx
, &ry
);
153 if(rectangle_contains(®ION_GEOM(curr
), x
-rx
, y
-ry
)){
154 if(region_handle_drop(curr
, x
, y
, dropped
))
159 /* Do not attach to ourselves unlike generic WMPlex. */
167 /*{{{ Region dynfun implementations */
170 static void screen_managed_changed(WScreen
*scr
, int mode
, bool sw
,
173 if(ioncore_g
.opmode
==IONCORE_OPMODE_DEINIT
)
176 if(sw
&& scr
->atom_workspace
!=None
){
177 WRegion
*reg
=mplex_mx_current(&(scr
->mplex
));
181 n
=region_displayname(reg
);
183 xwindow_set_string_property(region_root_of((WRegion
*)scr
),
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
,
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
);
229 * Find the screen with numerical id \var{id}.
233 WScreen
*ioncore_find_screen_id(int id
)
237 FOR_ALL_SCREENS(scr
){
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.
253 WScreen
*ioncore_goto_nth_screen(int id
)
255 WScreen
*scr
=ioncore_find_screen_id(id
);
257 if(!region_goto((WRegion
*)scr
))
264 static WScreen
*current_screen()
266 if(ioncore_g
.focus_current
==NULL
)
267 return ioncore_g
.screens
;
269 return region_screen_of(ioncore_g
.focus_current
);
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.
280 WScreen
*ioncore_goto_next_screen()
282 WScreen
*scr
=current_screen();
287 scr
=ioncore_g
.screens
;
289 if(!region_goto((WRegion
*)scr
))
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.
303 WScreen
*ioncore_goto_prev_screen()
305 WScreen
*scr
=current_screen();
310 scr
=ioncore_g
.screens
;
312 if(!region_goto((WRegion
*)scr
))
320 * Return the numerical id for screen \var{scr}.
324 int screen_id(WScreen
*scr
)
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;
340 if(OBJ_IS(reg, WGroupWS)){
341 FOR_ALL_NODES_ON_LLIST(lnode, scr->mplex.mx_list, tmp){
342 if(lnode->st->reg==reg){
344 }else if(OBJ_IS(lnode->st->reg, WGroupWS)){
350 if(onmxlist && !others){
351 warn(TR("Only workspace may not be destroyed/detached."));
361 static bool screen_may_dispose(WScreen
*scr
)
368 void screen_set_managed_offset(WScreen
*scr
, const WRectangle
*off
)
370 scr
->managed_off
=*off
;
371 mplex_fit_managed((WMPlex
*)scr
);
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
381 EXTL_EXPORT_AS(WScreen
, set_managed_offset
)
382 bool screen_set_managed_offset_extl(WScreen
*scr
, ExtlTab offset
)
386 if(!extl_table_to_rectangle(offset
, &g
))
389 if(-g
.w
>=REGION_GEOM(scr
).w
)
391 if(-g
.h
>=REGION_GEOM(scr
).h
)
394 screen_set_managed_offset(scr
, &g
);
398 warn(TR("Invalid offset."));
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
)
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
);
432 reg
=mplex_attach_new_(&scr
->mplex
, &par
, 0, lo
);
433 extl_unref_table(lo
);
437 warn(TR("Unable to create a workspace on screen %d."), scr
->id
);
445 bool screen_init_layout(WScreen
*scr
, ExtlTab tab
)
448 ExtlTab substab
, subtab
;
451 if(tab
==extl_table_none())
452 return create_initial_ws(scr
);
454 mplex_load_contents(&scr
->mplex
, tab
);
462 /*{{{ Dynamic function table and class implementation */
465 static DynFunTab screen_dynfuntab
[]={
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
},
491 screen_managed_geom
},
493 {(DynFun
*)region_get_configuration
,
494 (DynFun
*)screen_get_configuration
},
496 {(DynFun
*)region_handle_drop
,
497 (DynFun
*)screen_handle_drop
},
504 IMPLCLASS(WScreen
, WMPlex
, screen_deinit
, screen_dynfuntab
);