More cleanups of dead code
[notion.git] / ioncore / screen.c
blobdde20e781d4edb3ddec951ff8bdda0b7853747c6
1 /*
2 * ion/ioncore/screen.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
7 */
9 #include <string.h>
11 #include <libtu/objp.h>
12 #include <libtu/minmax.h>
14 #include "common.h"
15 #include "global.h"
16 #include "screen.h"
17 #include "region.h"
18 #include "attach.h"
19 #include "manage.h"
20 #include "focus.h"
21 #include "property.h"
22 #include "names.h"
23 #include "reginfo.h"
24 #include "saveload.h"
25 #include "resize.h"
26 #include "event.h"
27 #include "bindmaps.h"
28 #include "regbind.h"
29 #include "frame-pointer.h"
30 #include "rectangle.h"
31 #include "extlconv.h"
32 #include "llist.h"
33 #include "group-ws.h"
34 #include "mplex.h"
35 #include "conf.h"
36 #include "activity.h"
37 #include "screen-notify.h"
40 WHook *screen_managed_changed_hook=NULL;
43 /*{{{ Init/deinit */
46 bool screen_init(WScreen *scr, WRootWin *parent, const WFitParams *fp, int id)
48 Window win;
49 XSetWindowAttributes attr;
50 ulong attrflags=0;
52 scr->id=id;
53 scr->atom_workspace=None;
54 scr->managed_off.x=0;
55 scr->managed_off.y=0;
56 scr->managed_off.w=0;
57 scr->managed_off.h=0;
58 scr->next_scr=NULL;
59 scr->prev_scr=NULL;
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),
71 InputOutput,
72 DefaultVisual(ioncore_g.dpy, parent->xscr),
73 attrflags, &attr);
74 if(win==None)
75 return FALSE;
77 if(!mplex_do_init((WMPlex*)scr, (WWindow*)parent, fp, win, "WScreen")){
78 XDestroyWindow(ioncore_g.dpy, win);
79 return FALSE;
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);
90 if(id==0){
91 scr->atom_workspace=XInternAtom(ioncore_g.dpy,
92 "_ION_WORKSPACE", False);
93 }else if(id>=0){
94 char *str;
95 libtu_asprintf(&str, "_ION_WORKSPACE%d", id);
96 if(str!=NULL){
97 scr->atom_workspace=XInternAtom(ioncore_g.dpy, str, False);
98 free(str);
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);
111 return TRUE;
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);
133 /*}}}*/
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)){
156 int rx, ry;
157 region_rootpos(curr, &rx, &ry);
158 if(rectangle_contains(&REGION_GEOM(curr), x-rx, y-ry)){
159 if(region_handle_drop(curr, x, y, dropped))
160 return TRUE;
164 /* Do not attach to ourselves unlike generic WMPlex. */
165 return FALSE;
169 /*}}}*/
172 /*{{{ Region dynfun implementations */
175 static void screen_managed_changed(WScreen *scr, int mode, bool sw,
176 WRegion *reg_)
178 if(ioncore_g.opmode==IONCORE_OPMODE_DEINIT)
179 return;
181 if(sw && scr->atom_workspace!=None){
182 WRegion *reg=mplex_mx_current(&(scr->mplex));
183 const char *n=NULL;
185 if(reg!=NULL)
186 n=region_displayname(reg);
188 xwindow_set_string_property(region_root_of((WRegion*)scr),
189 scr->atom_workspace,
190 n==NULL ? "" : n);
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,
200 mode, sw, reg_);
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);
227 /*}}}*/
230 /*{{{ Misc. */
233 /*EXTL_DOC
234 * Find the screen with numerical id \var{id}.
236 EXTL_SAFE
237 EXTL_EXPORT
238 WScreen *ioncore_find_screen_id(int id)
240 WScreen *scr=NULL;
242 FOR_ALL_SCREENS(scr){
243 if(scr->id==id)
244 return scr;
247 return NULL;
251 /*EXTL_DOC
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.
257 EXTL_EXPORT
258 WScreen *ioncore_goto_nth_screen(int id)
260 WScreen *scr=ioncore_find_screen_id(id);
261 if(scr!=NULL){
262 if(!region_goto((WRegion*)scr))
263 return NULL;
265 return scr;
269 static WScreen *current_screen()
271 if(ioncore_g.focus_current==NULL)
272 return ioncore_g.screens;
273 else
274 return region_screen_of(ioncore_g.focus_current);
278 /*EXTL_DOC
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.
284 EXTL_EXPORT
285 WScreen *ioncore_goto_next_screen()
287 WScreen *scr=current_screen();
289 if(scr!=NULL)
290 scr=scr->next_scr;
291 if(scr==NULL)
292 scr=ioncore_g.screens;
293 if(scr!=NULL){
294 if(!region_goto((WRegion*)scr))
295 return NULL;
297 return scr;
301 /*EXTL_DOC
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.
307 EXTL_EXPORT
308 WScreen *ioncore_goto_prev_screen()
310 WScreen *scr=current_screen();
312 if(scr!=NULL)
313 scr=scr->prev_scr;
314 else
315 scr=ioncore_g.screens;
316 if(scr!=NULL){
317 if(!region_goto((WRegion*)scr))
318 return NULL;
320 return scr;
324 /*EXTL_DOC
325 * Return the numerical id for screen \var{scr}.
327 EXTL_SAFE
328 EXTL_EXPORT_MEMBER
329 int screen_id(WScreen *scr)
331 return scr->id;
335 static WRegion *screen_managed_disposeroot(WScreen *scr, WRegion *reg)
337 bool onmxlist=FALSE, others=FALSE;
338 WLListNode *lnode;
339 WLListIterTmp tmp;
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){
344 onmxlist=TRUE;
345 }else if(OBJ_IS(lnode->st->reg, WGroupWS)){
346 others=TRUE;
347 break;
351 if(onmxlist && !others){
352 warn(TR("Only workspace on only screen may not be destroyed/detached."));
353 return NULL;
357 return reg;
361 static bool screen_may_dispose(WScreen *scr)
363 return TRUE;
368 void screen_set_managed_offset(WScreen *scr, const WRectangle *off)
370 scr->managed_off=*off;
371 mplex_fit_managed((WMPlex*)scr);
375 /*EXTL_DOC
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
379 * geometry.
381 EXTL_EXPORT_AS(WScreen, set_managed_offset)
382 bool screen_set_managed_offset_extl(WScreen *scr, ExtlTab offset)
384 WRectangle g;
386 if(!extl_table_to_rectangle(offset, &g))
387 goto err;
389 if(-g.w>=REGION_GEOM(scr).w)
390 goto err;
391 if(-g.h>=REGION_GEOM(scr).h)
392 goto err;
394 screen_set_managed_offset(scr, &g);
396 return TRUE;
397 err:
398 warn(TR("Invalid offset."));
399 return FALSE;
403 /*}}}*/
406 /*{{{ Save/load */
409 ExtlTab screen_get_configuration(WScreen *scr)
411 return mplex_get_configuration(&scr->mplex);
415 static bool create_initial_ws(WScreen *scr)
417 WRegion *reg=NULL;
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);
424 }else{
425 reg=mplex_attach_new_(&scr->mplex, &par, 0, lo);
426 extl_unref_table(lo);
429 if(reg==NULL){
430 warn(TR("Unable to create a workspace on screen %d."), scr->id);
431 return FALSE;
434 return TRUE;
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);
445 return TRUE;
448 /*}}}*/
451 /*{{{ Dynamic function table and class implementation */
454 static DynFunTab screen_dynfuntab[]={
455 {region_map,
456 screen_map},
458 {region_unmap,
459 screen_unmap},
461 {region_activated,
462 screen_activated},
464 {region_inactivated,
465 screen_inactivated},
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},
479 {mplex_managed_geom,
480 screen_managed_geom},
482 {(DynFun*)region_get_configuration,
483 (DynFun*)screen_get_configuration},
485 {(DynFun*)region_handle_drop,
486 (DynFun*)screen_handle_drop},
488 END_DYNFUNTAB
492 EXTL_EXPORT
493 IMPLCLASS(WScreen, WMPlex, screen_deinit, screen_dynfuntab);
496 /*}}}*/