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
,
47 const WFitParams
*fp
, int id
, Window rootwin
)
50 XSetWindowAttributes attr
;
55 scr
->atom_workspace
=None
;
63 watch_init(&(scr
->notifywin_watch
));
64 watch_init(&(scr
->infowin_watch
));
70 attr
.background_pixmap
=ParentRelative
;
71 attrflags
=CWBackPixmap
;
73 win
=XCreateWindow(ioncore_g
.dpy
, WROOTWIN_ROOT(parent
),
74 fp
->g
.x
, fp
->g
.y
, fp
->g
.w
, fp
->g
.h
, 0,
75 DefaultDepth(ioncore_g
.dpy
, parent
->xscr
),
77 DefaultVisual(ioncore_g
.dpy
, parent
->xscr
),
83 if(!mplex_do_init((WMPlex
*)scr
, (WWindow
*)parent
, fp
, win
, "WScreen")){
85 XDestroyWindow(ioncore_g
.dpy
, win
);
89 /*scr->mplex.win.region.rootwin=rootwin;
90 region_set_parent((WRegion*)scr, (WRegion*)rootwin);*/
91 scr
->mplex
.flags
|=MPLEX_ADD_TO_END
;
92 scr
->mplex
.win
.region
.flags
|=REGION_BINDINGS_ARE_GRABBED
;
95 scr
->mplex
.win
.region
.flags
|=REGION_MAPPED
;
96 window_select_input((WWindow
*)scr
, IONCORE_EVENTMASK_SCREEN
);
100 scr
->atom_workspace
=XInternAtom(ioncore_g
.dpy
,
101 "_ION_WORKSPACE", False
);
104 libtu_asprintf(&str
, "_ION_WORKSPACE%d", id
);
106 scr
->atom_workspace
=XInternAtom(ioncore_g
.dpy
, str
, False
);
111 /* Add all the needed bindings here; mplex does nothing so that
112 * frames don't have to remove extra bindings.
114 region_add_bindmap((WRegion
*)scr
, ioncore_screen_bindmap
);
115 region_add_bindmap((WRegion
*)scr
, ioncore_mplex_bindmap
);
116 region_add_bindmap((WRegion
*)scr
, ioncore_mplex_toplevel_bindmap
);
118 LINK_ITEM(ioncore_g
.screens
, scr
, next_scr
, prev_scr
);
124 WScreen
*create_screen(WRootWin
*parent
, const WFitParams
*fp
, int id
)
126 CREATEOBJ_IMPL(WScreen
, screen
, (p
, parent
, fp
, id
, None
));
130 void screen_deinit(WScreen
*scr
)
132 UNLINK_ITEM(ioncore_g
.screens
, scr
, next_scr
, prev_scr
);
134 mplex_deinit((WMPlex
*)scr
);
141 /*{{{ Attach/detach */
144 void screen_managed_geom(WScreen
*scr
, WRectangle
*geom
)
146 geom
->x
=scr
->managed_off
.x
;
147 geom
->y
=scr
->managed_off
.y
;
148 geom
->w
=REGION_GEOM(scr
).w
+scr
->managed_off
.w
;
149 geom
->h
=REGION_GEOM(scr
).h
+scr
->managed_off
.h
;
150 geom
->w
=maxof(geom
->w
, 0);
151 geom
->h
=maxof(geom
->h
, 0);
155 static bool screen_handle_drop(WScreen
*scr
, int x
, int y
, WRegion
*dropped
)
157 WRegion
*curr
=mplex_mx_current(&(scr
->mplex
));
159 /* This code should handle dropping tabs on floating workspaces. */
160 if(curr
&& HAS_DYN(curr
, region_handle_drop
)){
162 region_rootpos(curr
, &rx
, &ry
);
163 if(rectangle_contains(®ION_GEOM(curr
), x
-rx
, y
-ry
)){
164 if(region_handle_drop(curr
, x
, y
, dropped
))
169 /* Do not attach to ourselves unlike generic WMPlex. */
177 /*{{{ Region dynfun implementations */
180 static void screen_managed_changed(WScreen
*scr
, int mode
, bool sw
,
183 if(ioncore_g
.opmode
==IONCORE_OPMODE_DEINIT
)
186 if(sw
&& scr
->atom_workspace
!=None
){
187 WRegion
*reg
=mplex_mx_current(&(scr
->mplex
));
191 n
=region_displayname(reg
);
193 xwindow_set_string_property(region_root_of((WRegion
*)scr
),
198 if(region_is_activity_r((WRegion
*)scr
))
199 screen_update_notifywin(scr
);
201 screen_update_infowin(scr
);
203 mplex_call_changed_hook((WMPlex
*)scr
,
204 screen_managed_changed_hook
,
209 static void screen_map(WScreen
*scr
)
213 mplex_map((WMPlex
*)scr
);
217 static void screen_unmap(WScreen
*scr
)
221 mplex_unmap((WMPlex
*)scr
);
224 void screen_inactivated(WScreen
*scr
)
226 screen_update_infowin(scr
);
230 void screen_activated(WScreen
*scr
)
232 screen_update_infowin(scr
);
243 * Find the screen with numerical id \var{id}.
247 WScreen
*ioncore_find_screen_id(int id
)
249 WScreen
*scr
, *maxscr
=NULL
;
251 FOR_ALL_SCREENS(scr
){
253 if(maxscr
==NULL
|| scr
->id
>maxscr
->id
)
265 * Switch focus to the screen with id \var{id} and return it.
267 * Note that this function is asynchronous; the screen will not
268 * actually have received the focus when this function returns.
271 WScreen
*ioncore_goto_nth_screen(int id
)
273 WScreen
*scr
=ioncore_find_screen_id(id
);
275 if(!region_goto((WRegion
*)scr
))
282 static WScreen
*current_screen()
284 if(ioncore_g
.focus_current
==NULL
)
285 return ioncore_g
.screens
;
287 return region_screen_of(ioncore_g
.focus_current
);
292 * Switch focus to the next screen and return it.
294 * Note that this function is asynchronous; the screen will not
295 * actually have received the focus when this function returns.
298 WScreen
*ioncore_goto_next_screen()
300 WScreen
*scr
=current_screen();
305 scr
=ioncore_g
.screens
;
307 if(!region_goto((WRegion
*)scr
))
315 * Switch focus to the previous screen and return it.
317 * Note that this function is asynchronous; the screen will not
318 * actually have received the focus when this function returns.
321 WScreen
*ioncore_goto_prev_screen()
323 WScreen
*scr
=current_screen();
328 scr
=ioncore_g
.screens
;
330 if(!region_goto((WRegion
*)scr
))
338 * Return the numerical id for screen \var{scr}.
342 int screen_id(WScreen
*scr
)
348 static WRegion
*screen_managed_disposeroot(WScreen
*scr
, WRegion
*reg
)
350 bool onmxlist
=FALSE
, others
=FALSE
;
354 if(OBJ_IS(reg
, WGroupWS
)){
355 FOR_ALL_NODES_ON_LLIST(lnode
, scr
->mplex
.mx_list
, tmp
){
356 if(lnode
->st
->reg
==reg
){
358 }else if(OBJ_IS(lnode
->st
->reg
, WGroupWS
)){
364 if(onmxlist
&& !others
){
365 warn(TR("Only workspace may not be destroyed/detached."));
374 static bool screen_may_dispose(WScreen
*scr
)
376 warn(TR("Screens may not be destroyed."));
382 void screen_set_managed_offset(WScreen
*scr
, const WRectangle
*off
)
384 scr
->managed_off
=*off
;
385 mplex_fit_managed((WMPlex
*)scr
);
390 * Set offset of objects managed by the screen from actual screen geometry.
391 * The table \var{offset} should contain the entries \code{x}, \code{y},
392 * \code{w} and \code{h} indicating offsets of that component of screen
395 EXTL_EXPORT_AS(WScreen
, set_managed_offset
)
396 bool screen_set_managed_offset_extl(WScreen
*scr
, ExtlTab offset
)
400 if(!extl_table_to_rectangle(offset
, &g
))
403 if(-g
.w
>=REGION_GEOM(scr
).w
)
405 if(-g
.h
>=REGION_GEOM(scr
).h
)
408 screen_set_managed_offset(scr
, &g
);
412 warn(TR("Invalid offset."));
423 ExtlTab
screen_get_configuration(WScreen
*scr
)
425 return mplex_get_configuration(&scr
->mplex
);
429 static WRegion
*do_create_initial(WWindow
*parent
, const WFitParams
*fp
,
430 WRegionLoadCreateFn
*fn
)
432 return fn(parent
, fp
, extl_table_none());
436 static bool create_initial_ws(WScreen
*scr
)
439 WMPlexAttachParams par
=MPLEXATTACHPARAMS_INIT
;
440 ExtlTab lo
=ioncore_get_layout("default");
442 if(lo
==extl_table_none()){
443 reg
=mplex_do_attach_new(&scr
->mplex
, &par
,
444 (WRegionCreateFn
*)create_groupws
, NULL
);
446 reg
=mplex_attach_new_(&scr
->mplex
, &par
, 0, lo
);
447 extl_unref_table(lo
);
451 warn(TR("Unable to create a workspace on screen %d."), scr
->id
);
459 bool screen_init_layout(WScreen
*scr
, ExtlTab tab
)
462 ExtlTab substab
, subtab
;
465 if(tab
==extl_table_none())
466 return create_initial_ws(scr
);
468 mplex_load_contents(&scr
->mplex
, tab
);
476 /*{{{ Dynamic function table and class implementation */
479 static DynFunTab screen_dynfuntab
[]={
492 {(DynFun
*)region_managed_disposeroot
,
493 (DynFun
*)screen_managed_disposeroot
},
495 {(DynFun
*)region_may_dispose
,
496 (DynFun
*)screen_may_dispose
},
498 {mplex_managed_changed
,
499 screen_managed_changed
},
501 {region_managed_notify
,
502 screen_managed_notify
},
505 screen_managed_geom
},
507 {(DynFun
*)region_get_configuration
,
508 (DynFun
*)screen_get_configuration
},
510 {(DynFun
*)region_handle_drop
,
511 (DynFun
*)screen_handle_drop
},
518 IMPLCLASS(WScreen
, WMPlex
, screen_deinit
, screen_dynfuntab
);