]> git.decadent.org.uk Git - ion3.git/blob - ioncore/stacking.c
9d77ac9489ecd981f68acbfe9e9204a060a0eb2a
[ion3.git] / ioncore / stacking.c
1 /*
2  * ion/ioncore/stacking.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 <libtu/rb.h>
13
14 #include "common.h"
15 #include "region.h"
16 #include "stacking.h"
17 #include "window.h"
18 #include "sizepolicy.h"
19
20
21 /*{{{ Alloc */
22
23
24 WStacking *create_stacking()
25 {
26     WStacking *st=ALLOC(WStacking);
27     
28     if(st!=NULL){
29         st->reg=NULL;
30         st->above=NULL;
31         st->level=0;
32         st->szplcy=SIZEPOLICY_DEFAULT;
33         st->hidden=FALSE;
34         st->lnode=NULL;
35     }
36     
37     return st;
38 }
39
40
41 void stacking_free(WStacking *st)
42 {
43     assert(st->mgr_next==NULL && st->mgr_prev==NULL &&
44            st->next==NULL && st->prev==NULL &&
45            /*st->above==NULL &&*/
46            st->lnode==NULL &&
47            st->reg==NULL);
48     
49     free(st);
50 }
51
52
53 /*}}}*/
54
55
56 /*{{{ Lookup */
57
58
59 static Rb_node stacking_of_reg=NULL;
60
61
62 WStacking *ioncore_find_stacking(WRegion *reg)
63 {
64     Rb_node node=NULL;
65     int found=0;
66     
67     if(stacking_of_reg!=NULL)
68         node=rb_find_pkey_n(stacking_of_reg, reg, &found);
69     
70     return (found ? (WStacking*)node->v.val : NULL);
71 }
72
73
74 void stacking_unassoc(WStacking *st)
75 {
76     Rb_node node=NULL;
77     int found=0;
78     
79     if(st->reg==NULL)
80         return;
81     
82     if(stacking_of_reg!=NULL)
83         node=rb_find_pkey_n(stacking_of_reg, st->reg, &found);
84     
85     if(node!=NULL)
86         rb_delete_node(node);
87     
88     st->reg=NULL;
89 }
90
91
92 bool stacking_assoc(WStacking *st, WRegion *reg)
93 {
94     assert(st->reg==NULL);
95     
96     if(stacking_of_reg==NULL){
97         stacking_of_reg=make_rb();
98         if(stacking_of_reg==NULL)
99             return FALSE;
100     }
101
102     if(rb_insertp(stacking_of_reg, reg, st)==NULL)
103         return FALSE;
104     
105     st->reg=reg;
106     return TRUE;
107 }
108
109
110 /*}}}*/
111
112
113
114 /*{{{ List processing */
115
116
117 static WStacking *link_lists(WStacking *l1, WStacking *l2)
118 {
119     /* As everywhere, doubly-linked lists without the forward 
120      * link in last item! 
121      */
122     WStacking *tmp=l2->prev;
123     l1->prev->next=l2;
124     l2->prev=l1->prev;
125     l1->prev=tmp;
126     return l1;
127 }
128
129
130 static WStacking *link_list_before(WStacking *l1, 
131                                    WStacking *i1,
132                                    WStacking *l2)
133 {
134     WStacking *tmp;
135     
136     if(i1==l1)
137         return link_lists(l2, l1);
138     
139     l2->prev->next=i1;
140     i1->prev->next=l2;
141     tmp=i1->prev;
142     i1->prev=l2->prev;
143     l2->prev=tmp;
144     
145     return l1;
146 }
147
148
149 static WStacking *link_list_after(WStacking *l1, 
150                                   WStacking *i1,
151                                   WStacking *l2)
152 {
153     WStacking *tmp;
154     
155     if(i1==l1->prev)
156         return link_lists(l1, l2);
157     
158     i1->next->prev=l2->prev;
159     l2->prev->next=i1->next;
160     i1->next=l2;
161     l2->prev=i1;
162     
163     return l1;
164 }
165
166
167 WStacking *stacking_unstack(WWindow *par, WStacking *regst)
168 {
169     WStacking *nxt=NULL, *st;
170     
171     /*st=regst->next;*/
172     
173     UNLINK_ITEM(par->stacking, regst, next, prev);
174             
175     /*while(st!=NULL){*/
176     for(st=par->stacking; st!=NULL; st=st->next){
177         if(st->above==regst){
178             st->above=NULL;
179             nxt=st;
180         }
181         /*st=st->next;*/
182     }
183     
184     if(nxt==NULL)
185         nxt=regst->above;
186     
187     if(regst->above==NULL)
188         regst->above=NULL;
189     
190     return nxt;
191 }
192
193
194 static bool cf(WStackingFilter *filt, void *filt_data, WStacking *st)
195 {
196     return (filt==NULL || filt(st, filt_data));
197 }
198
199
200 static bool check_unweave(WStacking *st)
201 {
202     /* 2: unknown, 1: yes, 0: no */
203     
204     if(st->to_unweave==2){
205         if(st->above!=NULL)
206             st->to_unweave=check_unweave(st->above);
207         else
208             st->to_unweave=0;
209     }
210     
211     return st->to_unweave;
212 }
213
214
215 WStacking *stacking_unweave(WStacking **stacking, 
216                             WStackingFilter *filt, void *filt_data)
217 {
218     WStacking *np=NULL;
219     WStacking *st, *next;
220
221     for(st=*stacking; st!=NULL; st=st->next){
222         st->to_unweave=2;
223         if(st->above==NULL && cf(filt, filt_data, st))
224             st->to_unweave=1;
225     }
226     
227     for(st=*stacking; st!=NULL; st=st->next)
228         check_unweave(st);
229     
230     for(st=*stacking; st!=NULL; st=next){
231         next=st->next;
232         if(st->to_unweave==1){
233             UNLINK_ITEM(*stacking, st, next, prev);
234             LINK_ITEM(np, st, next, prev);
235         }
236     }
237     
238     return np;
239 }
240
241
242 static int check_above_lvl(WStacking *st)
243 {
244     if(st->above==NULL)
245         return st->level;
246     st->level=check_above_lvl(st->above);
247     return st->level;
248 }
249
250     
251 static void enforce_level_sanity(WStacking **np)
252 {
253     WStacking *st;
254     
255     /* Make sure that the levels of stuff stacked 'above' match
256      * the level of the thing stacked above.
257      */
258     for(st=*np; st!=NULL; st=st->next)
259         check_above_lvl(st);
260
261     /* And now make sure things are ordered by levels. */
262     st=*np; 
263     while(st->next!=NULL){
264         if(st->next->level < st->level){
265             WStacking *st2=st->next;
266             UNLINK_ITEM(*np, st2, next, prev);
267             LINK_ITEM_BEFORE(*np, st2, st, next, prev);
268             if(st2->prev!=NULL)
269                 st=st2->prev;
270         }else{
271             st=st->next;
272         }
273     }
274 }
275
276
277 static void get_bottom(WStacking *st, Window fb_win,
278                        Window *other, int *mode)
279 {
280     Window bottom=None, top=None;
281     
282     while(st!=NULL){
283         if(st->reg!=NULL){
284             region_stacking(st->reg, &bottom, &top);
285             if(bottom!=None){
286                 *other=bottom;
287                 *mode=Below;
288                 return;
289             }
290         }
291         st=st->next;
292     }
293     
294     *other=fb_win;
295     *mode=Above;
296 }
297
298
299 static void stacking_do_weave(WStacking **stacking, WStacking **np, 
300                               bool below, Window fb_win)
301 {
302     WStacking *st, *ab;
303     uint lvl;
304     Window other;
305     int mode;
306
307     if(*np==NULL)
308         return;
309     
310     /* Should do nothing.. */
311     enforce_level_sanity(np);
312     
313     ab=*stacking;
314     
315     while(*np!=NULL){
316         lvl=(*np)->level;
317         
318         while(ab!=NULL){
319             if(ab->level>lvl || (below && ab->level==lvl))
320                 break;
321             ab=ab->next;
322         }
323         get_bottom(ab, fb_win, &other, &mode);
324         
325         st=*np;
326
327         UNLINK_ITEM(*np, st, next, prev);
328         
329         region_restack(st->reg, other, mode);
330
331         if(ab!=NULL){
332             LINK_ITEM_BEFORE(*stacking, ab, st, next, prev);
333         }else{
334             LINK_ITEM_LAST(*stacking, st, next, prev);
335         }
336     }
337 }
338
339
340 void stacking_weave(WStacking **stacking, WStacking **np, bool below)
341 {
342     stacking_do_weave(stacking, np, below, None);
343 }
344
345
346 /*}}}*/
347
348
349 /*{{{ Raise/lower */
350
351
352 static bool is_above(WStacking *st, WStacking *p)
353 {
354     if(st->above==NULL)
355         return FALSE;
356     else if(st->above==p)
357         return TRUE;
358     else
359         return is_above(st->above, p);
360 }
361
362
363 static void collect_first(WStacking **dst, WStacking **src, WStacking *st)
364 {
365     UNLINK_ITEM(*src, st, next, prev);
366     LINK_ITEM_FIRST(*dst, st, next, prev);
367 }
368
369
370 static void collect_last(WStacking **dst, WStacking **src, WStacking *st)
371 {
372     UNLINK_ITEM(*src, st, next, prev);
373     LINK_ITEM_LAST(*dst, st, next, prev);
374 }
375
376
377 static void collect_above(WStacking **dst, WStacking **src, WStacking *regst)
378 {
379     WStacking *stabove, *stnext;
380     
381     for(stabove=*src; stabove!=NULL; stabove=stnext){
382         stnext=stabove->next;
383         
384         if(is_above(stabove, regst))
385             collect_last(dst, src, stabove);
386     }
387 }
388
389
390 static WStacking *unweave_subtree(WStacking **stacking, WStacking *regst,
391                                   bool parents)
392 {
393     WStacking *tmp=NULL;
394     
395     if(parents){
396         WStacking *st=regst;
397         while(st!=NULL){
398             collect_first(&tmp, stacking, st);
399             st=st->above;
400         }
401     }else{
402         collect_first(&tmp, stacking, regst);
403     }
404     
405     collect_above(&tmp, stacking, regst);
406     
407     return tmp;
408 }
409
410
411 void stacking_restack(WStacking **stacking, WStacking *st, Window fb_win,
412                       WStackingFilter *filt, void *filt_data, bool lower)
413 {
414     WStacking *tmp=unweave_subtree(stacking, st, lower);
415
416     stacking_do_weave(stacking, &tmp, lower, fb_win);
417
418     assert(tmp==NULL);
419 }
420
421
422 /*}}}*/
423
424
425 /*{{{ Stacking lists */
426
427
428 WStacking **window_get_stackingp(WWindow *wwin)
429 {
430     return &(wwin->stacking);
431 }
432
433
434 WStacking *window_get_stacking(WWindow *wwin)
435 {
436     return wwin->stacking;
437 }
438
439
440 /*}}}*/
441
442
443 /*{{{ Stacking list iteration */
444
445
446 void stacking_iter_init(WStackingIterTmp *tmp, 
447                         WStacking *st,
448                         WStackingFilter *filt,
449                         void *filt_data)
450 {
451     tmp->st=st;
452     tmp->filt=filt;
453     tmp->filt_data=filt_data;
454 }
455
456
457 WStacking *stacking_iter_nodes(WStackingIterTmp *tmp)
458 {
459     WStacking *next=NULL;
460     
461     while(tmp->st!=NULL){
462         next=tmp->st;
463         tmp->st=tmp->st->next;
464         if(cf(tmp->filt, tmp->filt_data, next))
465             break;
466         next=NULL;
467     }
468     
469     return next;
470 }
471
472
473 WRegion *stacking_iter(WStackingIterTmp *tmp)
474 {
475     WStacking *st=stacking_iter_nodes(tmp);
476     return (st!=NULL ? st->reg : NULL);
477 }
478
479
480 void stacking_iter_mgr_init(WStackingIterTmp *tmp, 
481                             WStacking *st,
482                             WStackingFilter *filt,
483                             void *filt_data)
484 {
485     tmp->st=st;
486     tmp->filt=filt;
487     tmp->filt_data=filt_data;
488 }
489
490
491 WStacking *stacking_iter_mgr_nodes(WStackingIterTmp *tmp)
492 {
493     WStacking *next=NULL;
494     
495     while(tmp->st!=NULL){
496         next=tmp->st;
497         tmp->st=tmp->st->mgr_next;
498         if(cf(tmp->filt, tmp->filt_data, next))
499             break;
500         next=NULL;
501     }
502     
503     return next;
504 }
505
506
507 WRegion *stacking_iter_mgr(WStackingIterTmp *tmp)
508 {
509     WStacking *st=stacking_iter_mgr_nodes(tmp);
510     return (st!=NULL ? st->reg : NULL);
511 }
512
513     
514 /*}}}*/
515
516
517 /*{{{ Focus */
518
519
520 uint stacking_min_level(WStacking *stacking, 
521                         WStackingFilter *include_filt, 
522                         void *filt_data)
523 {
524     WStacking *st=NULL;
525     uint min_level=0;
526     
527     if(stacking==NULL)
528         return STACKING_LEVEL_BOTTOM;
529     
530     st=stacking;
531     do{
532         st=st->prev;
533         
534         if(st->reg!=NULL 
535            && !(st->reg->flags&REGION_SKIP_FOCUS)
536            && cf(include_filt, filt_data, st)){
537             
538             if(st->level>=STACKING_LEVEL_MODAL1)
539                 min_level=st->level;
540             
541             break;
542         }
543     }while(st!=stacking);
544     
545     return min_level;
546 }
547
548
549 WStacking *stacking_find_to_focus(WStacking *stacking, WStacking *to_try,
550                                   WStackingFilter *include_filt, 
551                                   WStackingFilter *approve_filt, 
552                                   void *filt_data)
553 {
554     WStacking *st=NULL;
555     uint min_level=0;
556     
557     if(stacking==NULL)
558         return NULL;
559     
560     min_level=stacking_min_level(stacking, include_filt, filt_data);
561     
562     if(to_try!=NULL && to_try->level>=min_level)
563         return to_try;
564     
565     st=stacking;
566     do{
567         st=st->prev;
568         
569         if(st->level<min_level)
570             break;
571         
572         if(st->reg!=NULL 
573            && !(st->reg->flags&REGION_SKIP_FOCUS)
574            && cf(include_filt, filt_data, st)
575            && cf(approve_filt, filt_data, st)){
576             return st;
577         }
578     }while(st!=stacking);
579     
580     return NULL;
581 }
582
583
584 static bool mapped_filt(WStacking *st, void *unused)
585 {
586     return (st->reg!=NULL && REGION_IS_MAPPED(st->reg));
587 }
588
589
590 static bool mgr_filt(WStacking *st, void *mgr_)
591 {
592     return (st->reg!=NULL && REGION_MANAGER(st->reg)==(WRegion*)mgr_);
593 }
594
595
596 WStacking *stacking_find_to_focus_mapped(WStacking *stacking, 
597                                          WStacking *to_try,
598                                          WRegion *mgr)
599 {
600     if(mgr==NULL){
601         return stacking_find_to_focus(stacking, to_try, mapped_filt, 
602                                       NULL, NULL);
603     }else{
604         return stacking_find_to_focus(stacking, to_try, mapped_filt, 
605                                       mgr_filt, mgr);
606     }
607 }
608
609
610 uint stacking_min_level_mapped(WStacking *stacking)
611 {
612     return stacking_min_level(stacking, mapped_filt, NULL);
613 }
614
615
616 /*}}}*/
617