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 screen_unnotify(scr
);
125 screen_nowindowinfo(scr
);
127 mplex_deinit((WMPlex
*)scr
);
134 /*{{{ Attach/detach */
137 void screen_managed_geom(WScreen
*scr
, WRectangle
*geom
)
139 geom
->x
=scr
->managed_off
.x
;
140 geom
->y
=scr
->managed_off
.y
;
141 geom
->w
=REGION_GEOM(scr
).w
+scr
->managed_off
.w
;
142 geom
->h
=REGION_GEOM(scr
).h
+scr
->managed_off
.h
;
143 geom
->w
=maxof(geom
->w
, 0);
144 geom
->h
=maxof(geom
->h
, 0);
148 static bool screen_handle_drop(WScreen
*scr
, int x
, int y
, WRegion
*dropped
)
150 WRegion
*curr
=mplex_mx_current(&(scr
->mplex
));
152 /* This code should handle dropping tabs on floating workspaces. */
153 if(curr
&& HAS_DYN(curr
, region_handle_drop
)){
155 region_rootpos(curr
, &rx
, &ry
);
156 if(rectangle_contains(®ION_GEOM(curr
), x
-rx
, y
-ry
)){
157 if(region_handle_drop(curr
, x
, y
, dropped
))
162 /* Do not attach to ourselves unlike generic WMPlex. */
170 /*{{{ Region dynfun implementations */
173 static void screen_managed_changed(WScreen
*scr
, int mode
, bool sw
,
176 if(ioncore_g
.opmode
==IONCORE_OPMODE_DEINIT
)
179 if(sw
&& scr
->atom_workspace
!=None
){
180 WRegion
*reg
=mplex_mx_current(&(scr
->mplex
));
184 n
=region_displayname(reg
);
186 xwindow_set_string_property(region_root_of((WRegion
*)scr
),
191 if(region_is_activity_r((WRegion
*)scr
))
192 screen_update_notifywin(scr
);
194 screen_update_infowin(scr
);
196 mplex_call_changed_hook((WMPlex
*)scr
,
197 screen_managed_changed_hook
,
202 static void screen_map(WScreen
*scr
)
204 mplex_map((WMPlex
*)scr
);
208 static void screen_unmap(WScreen
*scr
)
210 mplex_unmap((WMPlex
*)scr
);
213 void screen_inactivated(WScreen
*scr
)
215 screen_update_infowin(scr
);
219 void screen_activated(WScreen
*scr
)
221 screen_update_infowin(scr
);
232 * Find the screen with numerical id \var{id}.
236 WScreen
*ioncore_find_screen_id(int id
)
240 FOR_ALL_SCREENS(scr
){
250 * Switch focus to the screen with id \var{id} and return it.
252 * Note that this function is asynchronous; the screen will not
253 * actually have received the focus when this function returns.
256 WScreen
*ioncore_goto_nth_screen(int id
)
258 WScreen
*scr
=ioncore_find_screen_id(id
);
260 if(!region_goto((WRegion
*)scr
))
267 static WScreen
*current_screen()
269 if(ioncore_g
.focus_current
==NULL
)
270 return ioncore_g
.screens
;
272 return region_screen_of(ioncore_g
.focus_current
);
277 * Switch focus to the next screen and return it.
279 * Note that this function is asynchronous; the screen will not
280 * actually have received the focus when this function returns.
283 WScreen
*ioncore_goto_next_screen()
285 WScreen
*scr
=current_screen();
290 scr
=ioncore_g
.screens
;
292 if(!region_goto((WRegion
*)scr
))
300 * Switch focus to the previous screen and return it.
302 * Note that this function is asynchronous; the screen will not
303 * actually have received the focus when this function returns.
306 WScreen
*ioncore_goto_prev_screen()
308 WScreen
*scr
=current_screen();
313 scr
=ioncore_g
.screens
;
315 if(!region_goto((WRegion
*)scr
))
323 * Return the numerical id for screen \var{scr}.
327 int screen_id(WScreen
*scr
)
333 static WRegion
*screen_managed_disposeroot(WScreen
*scr
, WRegion
*reg
)
335 bool onmxlist
=FALSE
, others
=FALSE
;
339 if(scr
==scr
->prev_scr
&& OBJ_IS(reg
, WGroupWS
)){
340 FOR_ALL_NODES_ON_LLIST(lnode
, scr
->mplex
.mx_list
, tmp
){
341 if(lnode
->st
->reg
==reg
){
343 }else if(OBJ_IS(lnode
->st
->reg
, WGroupWS
)){
349 if(onmxlist
&& !others
){
350 warn(TR("Only workspace on only screen may not be destroyed/detached."));
359 static bool screen_may_dispose(WScreen
*scr
)
366 void screen_set_managed_offset(WScreen
*scr
, const WRectangle
*off
)
368 scr
->managed_off
=*off
;
369 mplex_fit_managed((WMPlex
*)scr
);
374 * Set offset of objects managed by the screen from actual screen geometry.
375 * The table \var{offset} should contain the entries \code{x}, \code{y},
376 * \code{w} and \code{h} indicating offsets of that component of screen
379 EXTL_EXPORT_AS(WScreen
, set_managed_offset
)
380 bool screen_set_managed_offset_extl(WScreen
*scr
, ExtlTab offset
)
384 if(!extl_table_to_rectangle(offset
, &g
))
387 if(-g
.w
>=REGION_GEOM(scr
).w
)
389 if(-g
.h
>=REGION_GEOM(scr
).h
)
392 screen_set_managed_offset(scr
, &g
);
396 warn(TR("Invalid offset."));
407 ExtlTab
screen_get_configuration(WScreen
*scr
)
409 return mplex_get_configuration(&scr
->mplex
);
413 static WRegion
*do_create_initial(WWindow
*parent
, const WFitParams
*fp
,
414 WRegionLoadCreateFn
*fn
)
416 return fn(parent
, fp
, extl_table_none());
420 static bool create_initial_ws(WScreen
*scr
)
423 WMPlexAttachParams par
=MPLEXATTACHPARAMS_INIT
;
424 ExtlTab lo
=ioncore_get_layout("default");
426 if(lo
==extl_table_none()){
427 reg
=mplex_do_attach_new(&scr
->mplex
, &par
,
428 (WRegionCreateFn
*)create_groupws
, NULL
);
430 reg
=mplex_attach_new_(&scr
->mplex
, &par
, 0, lo
);
431 extl_unref_table(lo
);
435 warn(TR("Unable to create a workspace on screen %d."), scr
->id
);
443 bool screen_init_layout(WScreen
*scr
, ExtlTab tab
)
446 ExtlTab substab
, subtab
;
449 if(tab
==extl_table_none())
450 return create_initial_ws(scr
);
452 mplex_load_contents(&scr
->mplex
, tab
);
460 /*{{{ Dynamic function table and class implementation */
463 static DynFunTab screen_dynfuntab
[]={
476 {(DynFun
*)region_managed_disposeroot
,
477 (DynFun
*)screen_managed_disposeroot
},
479 {(DynFun
*)region_may_dispose
,
480 (DynFun
*)screen_may_dispose
},
482 {mplex_managed_changed
,
483 screen_managed_changed
},
485 {region_managed_notify
,
486 screen_managed_notify
},
489 screen_managed_geom
},
491 {(DynFun
*)region_get_configuration
,
492 (DynFun
*)screen_get_configuration
},
494 {(DynFun
*)region_handle_drop
,
495 (DynFun
*)screen_handle_drop
},
502 IMPLCLASS(WScreen
, WMPlex
, screen_deinit
, screen_dynfuntab
);