]> git.decadent.org.uk Git - ion3.git/blob - ioncore/screen.c
00d675f2f576683323f35ff92ccd3f703f9a4c39
[ion3.git] / ioncore / screen.c
1 /*
2  * ion/ioncore/screen.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2007. 
5  *
6  * Ion is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2.1 of the License, or
9  * (at your option) any later version.
10  */
11
12 #include <string.h>
13
14 #include <libtu/objp.h>
15 #include <libtu/minmax.h>
16
17 #include "common.h"
18 #include "global.h"
19 #include "screen.h"
20 #include "region.h"
21 #include "attach.h"
22 #include "manage.h"
23 #include "focus.h"
24 #include "property.h"
25 #include "names.h"
26 #include "reginfo.h"
27 #include "saveload.h"
28 #include "resize.h"
29 #include "event.h"
30 #include "bindmaps.h"
31 #include "regbind.h"
32 #include "frame-pointer.h"
33 #include "rectangle.h"
34 #include "extlconv.h"
35 #include "llist.h"
36 #include "group-ws.h"
37 #include "mplex.h"
38 #include "conf.h"
39 #include "activity.h"
40 #include "screen-notify.h"
41
42
43 WHook *screen_managed_changed_hook=NULL;
44
45
46 /*{{{ Init/deinit */
47
48
49 bool screen_init(WScreen *scr, WRootWin *parent,
50                  const WFitParams *fp, int id, Window rootwin)
51 {
52     Window win;
53     XSetWindowAttributes attr;
54     ulong attrflags=0;
55     bool is_root=FALSE;
56     
57     scr->id=id;
58     scr->atom_workspace=None;
59     scr->managed_off.x=0;
60     scr->managed_off.y=0;
61     scr->managed_off.w=0;
62     scr->managed_off.h=0;
63     scr->next_scr=NULL;
64     scr->prev_scr=NULL;
65     
66     watch_init(&(scr->notifywin_watch));
67     watch_init(&(scr->infowin_watch));
68
69     if(parent==NULL){
70         win=rootwin;
71         is_root=TRUE;
72     }else{
73         attr.background_pixmap=ParentRelative;
74         attrflags=CWBackPixmap;
75         
76         win=XCreateWindow(ioncore_g.dpy, WROOTWIN_ROOT(parent),
77                           fp->g.x, fp->g.y, fp->g.w, fp->g.h, 0, 
78                           DefaultDepth(ioncore_g.dpy, parent->xscr),
79                           InputOutput,
80                           DefaultVisual(ioncore_g.dpy, parent->xscr),
81                           attrflags, &attr);
82         if(win==None)
83             return FALSE;
84             
85     }
86
87     if(!mplex_do_init((WMPlex*)scr, (WWindow*)parent, fp, win)){
88         if(!is_root)
89             XDestroyWindow(ioncore_g.dpy, win);
90         return FALSE;
91     }
92
93     /*scr->mplex.win.region.rootwin=rootwin;
94     region_set_parent((WRegion*)scr, (WRegion*)rootwin);*/
95     scr->mplex.flags|=MPLEX_ADD_TO_END;
96     scr->mplex.win.region.flags|=REGION_BINDINGS_ARE_GRABBED;
97     
98     if(!is_root){
99         scr->mplex.win.region.flags|=REGION_MAPPED;
100         window_select_input((WWindow*)scr, IONCORE_EVENTMASK_SCREEN);
101     }
102     
103     if(id==0){
104         scr->atom_workspace=XInternAtom(ioncore_g.dpy, 
105                                         "_ION_WORKSPACE", False);
106     }else if(id>=0){
107         char *str;
108         libtu_asprintf(&str, "_ION_WORKSPACE%d", id);
109         if(str!=NULL){
110             scr->atom_workspace=XInternAtom(ioncore_g.dpy, str, False);
111             free(str);
112         }
113     }
114
115     /* Add all the needed bindings here; mplex does nothing so that
116      * frames don't have to remove extra bindings.
117      */
118     region_add_bindmap((WRegion*)scr, ioncore_screen_bindmap);
119     region_add_bindmap((WRegion*)scr, ioncore_mplex_bindmap);
120     region_add_bindmap((WRegion*)scr, ioncore_mplex_toplevel_bindmap);
121
122     LINK_ITEM(ioncore_g.screens, scr, next_scr, prev_scr);
123     
124     return TRUE;
125 }
126
127
128 WScreen *create_screen(WRootWin *parent, const WFitParams *fp, int id)
129 {
130     CREATEOBJ_IMPL(WScreen, screen, (p, parent, fp, id, None));
131 }
132
133
134 void screen_deinit(WScreen *scr)
135 {
136     UNLINK_ITEM(ioncore_g.screens, scr, next_scr, prev_scr);
137     
138     mplex_deinit((WMPlex*)scr);
139 }
140
141
142 /*}}}*/
143
144
145 /*{{{ Attach/detach */
146
147
148 void screen_managed_geom(WScreen *scr, WRectangle *geom)
149 {
150     geom->x=scr->managed_off.x;
151     geom->y=scr->managed_off.y;
152     geom->w=REGION_GEOM(scr).w+scr->managed_off.w;
153     geom->h=REGION_GEOM(scr).h+scr->managed_off.h;
154     geom->w=maxof(geom->w, 0);
155     geom->h=maxof(geom->h, 0);
156 }
157
158
159 static bool screen_handle_drop(WScreen *scr, int x, int y, WRegion *dropped)
160 {
161     WRegion *curr=mplex_mx_current(&(scr->mplex));
162
163     /* This code should handle dropping tabs on floating workspaces. */
164     if(curr && HAS_DYN(curr, region_handle_drop)){
165         int rx, ry;
166         region_rootpos(curr, &rx, &ry);
167         if(rectangle_contains(&REGION_GEOM(curr), x-rx, y-ry)){
168             if(region_handle_drop(curr, x, y, dropped))
169                 return TRUE;
170         }
171     }
172     
173     /* Do not attach to ourselves unlike generic WMPlex. */
174     return FALSE;
175 }
176
177
178 /*}}}*/
179
180
181 /*{{{ Region dynfun implementations */
182
183
184 static bool screen_fitrep(WScreen *scr, WWindow *par, const WFitParams *fp)
185 {
186     WRegion *sub;
187     
188     if(par==NULL)
189         return FALSE;
190     
191     if(scr->uses_root)
192         return FALSE;
193
194     return mplex_fitrep((WMPlex*)scr, NULL, fp);
195 }
196
197
198
199
200 static void screen_managed_changed(WScreen *scr, int mode, bool sw, 
201                                    WRegion *reg_)
202 {
203     if(ioncore_g.opmode==IONCORE_OPMODE_DEINIT)
204         return;
205     
206     if(sw && scr->atom_workspace!=None){
207         WRegion *reg=mplex_mx_current(&(scr->mplex));
208         const char *n=NULL;
209         
210         if(reg!=NULL)
211             n=region_displayname(reg);
212         
213         xwindow_set_string_property(region_root_of((WRegion*)scr),
214                                     scr->atom_workspace, 
215                                     n==NULL ? "" : n);
216     }
217     
218     if(region_is_activity_r((WRegion*)scr))
219         screen_update_notifywin(scr);
220     
221     screen_update_infowin(scr);
222     
223     mplex_call_changed_hook((WMPlex*)scr,
224                             screen_managed_changed_hook,
225                             mode, sw, reg_);
226 }
227
228
229 static void screen_map(WScreen *scr)
230 {
231     if(scr->uses_root)
232         return;
233     mplex_map((WMPlex*)scr);
234 }
235
236
237 static void screen_unmap(WScreen *scr)
238 {
239     if(scr->uses_root)
240         return;
241     mplex_unmap((WMPlex*)scr);
242 }
243
244 void screen_inactivated(WScreen *scr)
245 {
246     screen_update_infowin(scr);
247 }
248
249
250 void screen_activated(WScreen *scr)
251 {
252     screen_update_infowin(scr);
253 }
254
255
256 /*}}}*/
257
258
259 /*{{{ Misc. */
260
261
262 /*EXTL_DOC
263  * Find the screen with numerical id \var{id}. 
264  */
265 EXTL_SAFE
266 EXTL_EXPORT
267 WScreen *ioncore_find_screen_id(int id)
268 {
269     WScreen *scr, *maxscr=NULL;
270     
271     FOR_ALL_SCREENS(scr){
272         if(id==-1){
273             if(maxscr==NULL || scr->id>maxscr->id)
274                 maxscr=scr;
275         }
276         if(scr->id==id)
277             return scr;
278     }
279     
280     return maxscr;
281 }
282
283
284 /*EXTL_DOC
285  * Switch focus to the screen with id \var{id} and return it.
286  * 
287  * Note that this function is asynchronous; the screen will not
288  * actually have received the focus when this function returns.
289  */
290 EXTL_EXPORT
291 WScreen *ioncore_goto_nth_screen(int id)
292 {
293     WScreen *scr=ioncore_find_screen_id(id);
294     if(scr!=NULL){
295         if(!region_goto((WRegion*)scr))
296             return NULL;
297     }
298     return scr;
299 }
300
301
302 static WScreen *current_screen()
303 {
304     if(ioncore_g.focus_current==NULL)
305         return ioncore_g.screens;
306     else
307         return region_screen_of(ioncore_g.focus_current);
308 }
309
310        
311 /*EXTL_DOC
312  * Switch focus to the next screen and return it.
313  * 
314  * Note that this function is asynchronous; the screen will not
315  * actually have received the focus when this function returns.
316  */
317 EXTL_EXPORT
318 WScreen *ioncore_goto_next_screen()
319 {
320     WScreen *scr=current_screen();
321     
322     if(scr!=NULL)
323         scr=scr->next_scr;
324     if(scr==NULL)
325         scr=ioncore_g.screens;
326     if(scr!=NULL){
327         if(!region_goto((WRegion*)scr))
328             return NULL;
329     }
330     return scr;
331 }
332
333
334 /*EXTL_DOC
335  * Switch focus to the previous screen and return it.
336  * 
337  * Note that this function is asynchronous; the screen will not
338  * actually have received the focus when this function returns.
339  */
340 EXTL_EXPORT
341 WScreen *ioncore_goto_prev_screen()
342 {
343     WScreen *scr=current_screen();
344
345     if(scr!=NULL)
346         scr=scr->prev_scr;
347     else
348         scr=ioncore_g.screens;
349     if(scr!=NULL){
350         if(!region_goto((WRegion*)scr))
351             return NULL;
352     }
353     return scr;
354 }
355
356
357 /*EXTL_DOC
358  * Return the numerical id for screen \var{scr}.
359  */
360 EXTL_SAFE
361 EXTL_EXPORT_MEMBER
362 int screen_id(WScreen *scr)
363 {
364     return scr->id;
365 }
366
367
368 static WRegion *screen_managed_disposeroot(WScreen *scr, WRegion *reg)
369 {
370     bool onmxlist=FALSE, others=FALSE;
371     WLListNode *lnode;
372     WLListIterTmp tmp;
373     
374     if(OBJ_IS(reg, WGroupWS)){
375         FOR_ALL_NODES_ON_LLIST(lnode, scr->mplex.mx_list, tmp){
376             if(lnode->st->reg==reg){
377                 onmxlist=TRUE;
378             }else if(OBJ_IS(lnode->st->reg, WGroupWS)){
379                 others=TRUE;
380                 break;
381             }
382         }
383
384         if(onmxlist && !others){
385             warn(TR("Only workspace may not be destroyed/detached."));
386             return NULL;
387         }
388     }
389     
390     return reg;
391 }
392
393
394 static bool screen_may_dispose(WScreen *scr)
395 {
396     warn(TR("Screens may not be destroyed."));
397     return FALSE;
398 }
399
400
401
402 void screen_set_managed_offset(WScreen *scr, const WRectangle *off)
403 {
404     scr->managed_off=*off;
405     mplex_fit_managed((WMPlex*)scr);
406 }
407
408
409 /*EXTL_DOC
410  * Set offset of objects managed by the screen from actual screen geometry.
411  * The table \var{offset} should contain the entries \code{x}, \code{y}, 
412  * \code{w} and \code{h} indicating offsets of that component of screen 
413  * geometry.
414  */
415 EXTL_EXPORT_AS(WScreen, set_managed_offset)
416 bool screen_set_managed_offset_extl(WScreen *scr, ExtlTab offset)
417 {
418     WRectangle g;
419     
420     if(!extl_table_to_rectangle(offset, &g))
421         goto err;
422     
423     if(-g.w>=REGION_GEOM(scr).w)
424         goto err;
425     if(-g.h>=REGION_GEOM(scr).h)
426         goto err;
427     
428     screen_set_managed_offset(scr, &g);
429     
430     return TRUE;
431 err:
432     warn(TR("Invalid offset."));
433     return FALSE;
434 }
435
436
437 /*}}}*/
438
439
440 /*{{{ Save/load */
441
442
443 ExtlTab screen_get_configuration(WScreen *scr)
444 {
445     return mplex_get_configuration(&scr->mplex);
446 }
447
448
449 static WRegion *do_create_initial(WWindow *parent, const WFitParams *fp, 
450                                   WRegionLoadCreateFn *fn)
451 {
452     return fn(parent, fp, extl_table_none());
453 }
454
455
456 static bool create_initial_ws(WScreen *scr)
457 {
458     WRegion *reg=NULL;
459     WMPlexAttachParams par=MPLEXATTACHPARAMS_INIT;
460     ExtlTab lo=ioncore_get_layout("default");
461     
462     if(lo==extl_table_none()){
463         reg=mplex_do_attach_new(&scr->mplex, &par,
464                                 (WRegionCreateFn*)create_groupws, NULL);
465     }else{
466         reg=mplex_attach_new_(&scr->mplex, &par, 0, lo);
467         extl_unref_table(lo);
468     }
469     
470     if(reg==NULL){
471         warn(TR("Unable to create a workspace on screen %d."), scr->id);
472         return FALSE;
473     }
474     
475     return TRUE;
476 }
477
478
479 bool screen_init_layout(WScreen *scr, ExtlTab tab)
480 {
481     char *name;
482     ExtlTab substab, subtab;
483     int n, i;
484
485     if(tab==extl_table_none())
486         return create_initial_ws(scr);
487     
488     mplex_load_contents(&scr->mplex, tab);
489     
490     return TRUE;
491 }
492
493 /*}}}*/
494
495
496 /*{{{ Dynamic function table and class implementation */
497
498
499 static DynFunTab screen_dynfuntab[]={
500     {region_map, 
501      screen_map},
502     
503     {region_unmap, 
504      screen_unmap},
505      
506     {region_activated, 
507      screen_activated},
508      
509     {region_inactivated, 
510      screen_inactivated},
511     
512     {(DynFun*)region_managed_disposeroot,
513      (DynFun*)screen_managed_disposeroot},
514
515     {(DynFun*)region_may_dispose,
516      (DynFun*)screen_may_dispose},
517
518     {mplex_managed_changed, 
519      screen_managed_changed},
520     
521     {region_managed_notify, 
522      screen_managed_notify},
523     
524     {mplex_managed_geom, 
525      screen_managed_geom},
526
527     {(DynFun*)region_get_configuration,
528      (DynFun*)screen_get_configuration},
529
530     {(DynFun*)region_handle_drop, 
531      (DynFun*)screen_handle_drop},
532
533     {(DynFun*)region_fitrep,
534      (DynFun*)screen_fitrep},
535
536     END_DYNFUNTAB
537 };
538
539
540 EXTL_EXPORT
541 IMPLCLASS(WScreen, WMPlex, screen_deinit, screen_dynfuntab);
542
543
544 /*}}}*/