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