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
));
63 watch_init(&(scr
->workspace_indicatorwin_watch
));
65 attr
.background_pixmap
=ParentRelative
;
66 attrflags
=CWBackPixmap
;
68 win
=XCreateWindow(ioncore_g
.dpy
, WROOTWIN_ROOT(parent
),
69 fp
->g
.x
, fp
->g
.y
, fp
->g
.w
, fp
->g
.h
, 0,
70 DefaultDepth(ioncore_g
.dpy
, parent
->xscr
),
72 DefaultVisual(ioncore_g
.dpy
, parent
->xscr
),
77 if(!mplex_do_init((WMPlex
*)scr
, (WWindow
*)parent
, fp
, win
, "WScreen")){
78 XDestroyWindow(ioncore_g
.dpy
, win
);
82 /*scr->mplex.win.region.rootwin=rootwin;
83 region_set_parent((WRegion*)scr, (WRegion*)rootwin);*/
84 scr
->mplex
.flags
|=MPLEX_ADD_TO_END
;
85 scr
->mplex
.win
.region
.flags
|=REGION_BINDINGS_ARE_GRABBED
;
87 scr
->mplex
.win
.region
.flags
|=REGION_MAPPED
;
88 window_select_input((WWindow
*)scr
, IONCORE_EVENTMASK_SCREEN
);
91 scr
->atom_workspace
=XInternAtom(ioncore_g
.dpy
,
92 "_ION_WORKSPACE", False
);
95 libtu_asprintf(&str
, "_ION_WORKSPACE%d", id
);
97 scr
->atom_workspace
=XInternAtom(ioncore_g
.dpy
, str
, False
);
102 /* Add all the needed bindings here; mplex does nothing so that
103 * frames don't have to remove extra bindings.
105 region_add_bindmap((WRegion
*)scr
, ioncore_screen_bindmap
);
106 region_add_bindmap((WRegion
*)scr
, ioncore_mplex_bindmap
);
107 region_add_bindmap((WRegion
*)scr
, ioncore_mplex_toplevel_bindmap
);
109 LINK_ITEM(ioncore_g
.screens
, scr
, next_scr
, prev_scr
);
115 WScreen
*create_screen(WRootWin
*parent
, const WFitParams
*fp
, int id
)
117 CREATEOBJ_IMPL(WScreen
, screen
, (p
, parent
, fp
, id
));
121 void screen_deinit(WScreen
*scr
)
123 UNLINK_ITEM(ioncore_g
.screens
, scr
, next_scr
, prev_scr
);
125 screen_unnotify_notifywin(scr
);
126 screen_unnotify_infowin(scr
);
127 screen_unnotify_if_screen(scr
);
129 mplex_deinit((WMPlex
*)scr
);
136 /*{{{ Attach/detach */
139 void screen_managed_geom(WScreen
*scr
, WRectangle
*geom
)
141 geom
->x
=scr
->managed_off
.x
;
142 geom
->y
=scr
->managed_off
.y
;
143 geom
->w
=REGION_GEOM(scr
).w
+scr
->managed_off
.w
;
144 geom
->h
=REGION_GEOM(scr
).h
+scr
->managed_off
.h
;
145 geom
->w
=maxof(geom
->w
, 0);
146 geom
->h
=maxof(geom
->h
, 0);
150 static bool screen_handle_drop(WScreen
*scr
, int x
, int y
, WRegion
*dropped
)
152 WRegion
*curr
=mplex_mx_current(&(scr
->mplex
));
154 /* This code should handle dropping tabs on floating workspaces. */
155 if(curr
&& HAS_DYN(curr
, region_handle_drop
)){
157 region_rootpos(curr
, &rx
, &ry
);
158 if(rectangle_contains(®ION_GEOM(curr
), x
-rx
, y
-ry
)){
159 if(region_handle_drop(curr
, x
, y
, dropped
))
164 /* Do not attach to ourselves unlike generic WMPlex. */
172 /*{{{ Region dynfun implementations */
175 static void screen_managed_changed(WScreen
*scr
, int mode
, bool sw
,
178 if(ioncore_g
.opmode
==IONCORE_OPMODE_DEINIT
)
181 if(sw
&& scr
->atom_workspace
!=None
){
182 WRegion
*reg
=mplex_mx_current(&(scr
->mplex
));
186 n
=region_displayname(reg
);
188 xwindow_set_string_property(region_root_of((WRegion
*)scr
),
193 if(region_is_activity_r((WRegion
*)scr
))
194 screen_update_notifywin(scr
);
196 screen_update_infowin(scr
);
198 mplex_call_changed_hook((WMPlex
*)scr
,
199 screen_managed_changed_hook
,
204 static void screen_map(WScreen
*scr
)
206 mplex_map((WMPlex
*)scr
);
210 static void screen_unmap(WScreen
*scr
)
212 mplex_unmap((WMPlex
*)scr
);
215 void screen_inactivated(WScreen
*scr
)
217 screen_update_infowin(scr
);
221 void screen_activated(WScreen
*scr
)
223 screen_update_infowin(scr
);
234 * Find the screen with numerical id \var{id}.
238 WScreen
*ioncore_find_screen_id(int id
)
242 FOR_ALL_SCREENS(scr
){
252 * Switch focus to the screen with id \var{id} and return it.
254 * Note that this function is asynchronous; the screen will not
255 * actually have received the focus when this function returns.
258 WScreen
*ioncore_goto_nth_screen(int id
)
260 WScreen
*scr
=ioncore_find_screen_id(id
);
262 if(!region_goto((WRegion
*)scr
))
269 static WScreen
*current_screen()
271 if(ioncore_g
.focus_current
==NULL
)
272 return ioncore_g
.screens
;
274 return region_screen_of(ioncore_g
.focus_current
);
279 * Switch focus to the next screen and return it.
281 * Note that this function is asynchronous; the screen will not
282 * actually have received the focus when this function returns.
285 WScreen
*ioncore_goto_next_screen()
287 WScreen
*scr
=current_screen();
292 scr
=ioncore_g
.screens
;
294 if(!region_goto((WRegion
*)scr
))
302 * Switch focus to the previous screen and return it.
304 * Note that this function is asynchronous; the screen will not
305 * actually have received the focus when this function returns.
308 WScreen
*ioncore_goto_prev_screen()
310 WScreen
*scr
=current_screen();
315 scr
=ioncore_g
.screens
;
317 if(!region_goto((WRegion
*)scr
))
325 * Return the numerical id for screen \var{scr}.
329 int screen_id(WScreen
*scr
)
335 static WRegion
*screen_managed_disposeroot(WScreen
*scr
, WRegion
*reg
)
337 bool onmxlist
=FALSE
, others
=FALSE
;
341 if(scr
==scr
->prev_scr
&& OBJ_IS(reg
, WGroupWS
)){
342 FOR_ALL_NODES_ON_LLIST(lnode
, scr
->mplex
.mx_list
, tmp
){
343 if(lnode
->st
->reg
==reg
){
345 }else if(OBJ_IS(lnode
->st
->reg
, WGroupWS
)){
351 if(onmxlist
&& !others
){
352 warn(TR("Only workspace on only screen 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 bool create_initial_ws(WScreen
*scr
)
418 WMPlexAttachParams par
=MPLEXATTACHPARAMS_INIT
;
419 ExtlTab lo
=ioncore_get_layout("default");
421 if(lo
==extl_table_none()){
422 reg
=mplex_do_attach_new(&scr
->mplex
, &par
,
423 (WRegionCreateFn
*)create_groupws
, NULL
);
425 reg
=mplex_attach_new_(&scr
->mplex
, &par
, 0, lo
);
426 extl_unref_table(lo
);
430 warn(TR("Unable to create a workspace on screen %d."), scr
->id
);
438 bool screen_init_layout(WScreen
*scr
, ExtlTab tab
)
440 if(tab
==extl_table_none())
441 return create_initial_ws(scr
);
443 mplex_load_contents(&scr
->mplex
, tab
);
451 /*{{{ Dynamic function table and class implementation */
454 static DynFunTab screen_dynfuntab
[]={
467 {(DynFun
*)region_managed_disposeroot
,
468 (DynFun
*)screen_managed_disposeroot
},
470 {(DynFun
*)region_may_dispose
,
471 (DynFun
*)screen_may_dispose
},
473 {mplex_managed_changed
,
474 screen_managed_changed
},
476 {region_managed_notify
,
477 screen_managed_notify
},
480 screen_managed_geom
},
482 {(DynFun
*)region_get_configuration
,
483 (DynFun
*)screen_get_configuration
},
485 {(DynFun
*)region_handle_drop
,
486 (DynFun
*)screen_handle_drop
},
493 IMPLCLASS(WScreen
, WMPlex
, screen_deinit
, screen_dynfuntab
);