1 /*
2 * pwm/winobj.c
3 *
4 * Copyright (c) Tuomo Valkonen 1999-2001.
5 *
6 * You may distribute and modify this program under the terms of either
7 * the Clarified Artistic License or the GNU GPL, version 2 or later.
8 */
9
10 #include "common.h"
11 #include "thing.h"
12 #include "frame.h"
13 #include "menu.h"
14 #include "focus.h"
15
16
lowest_win(WWinObj * obj)17 static Window lowest_win(WWinObj *obj)
18 {
19 if(WTHING_IS(obj, WTHING_FRAME))
20 return ((WFrame*)obj)->bar_win;
21
22 if(WTHING_IS(obj, WTHING_DOCK))
23 return ((WDock*)obj)->win;
24
25 if(WTHING_IS(obj, WTHING_MENU))
26 return ((WMenu*)obj)->menu_win;
27
28 return None;
29 }
30
31
highest_win(WWinObj * obj)32 static Window highest_win(WWinObj *obj)
33 {
34 if(WTHING_IS(obj, WTHING_FRAME))
35 return ((WFrame*)obj)->frame_win;
36
37 if(WTHING_IS(obj, WTHING_DOCK))
38 return ((WDock*)obj)->win;
39
40 if(WTHING_IS(obj, WTHING_MENU))
41 return ((WMenu*)obj)->menu_win;
42
43 return None;
44 }
45
46
do_restack_window(Window win,Window other,int stack_mode)47 static void do_restack_window(Window win, Window other, int stack_mode)
48 {
49 XWindowChanges wc;
50 int wcmask;
51
52 wcmask=CWStackMode;
53 wc.stack_mode=stack_mode;
54 if((wc.sibling=other)!=None)
55 wcmask|=CWSibling;
56
57 XConfigureWindow(wglobal.dpy, win, wcmask, &wc);
58 }
59
60
restack_windows(WWinObj * obj,Window other,int mode)61 static void restack_windows(WWinObj *obj, Window other, int mode)
62 {
63 WFrame *frame;
64 Window win;
65
66 win=lowest_win(obj);
67
68 do_restack_window(win, other, mode);
69
70 if(!WTHING_IS(obj, WTHING_FRAME))
71 return;
72
73 frame=(WFrame*)obj;
74
75 do_restack_window(frame->frame_win, frame->bar_win, Above);
76 }
77
78
set_winobj_pos(WWinObj * obj,int x,int y)79 void set_winobj_pos(WWinObj *obj, int x, int y)
80 {
81 if(WTHING_IS(obj, WTHING_FRAME))
82 set_frame_pos((WFrame*)obj, x, y);
83 else if(WTHING_IS(obj, WTHING_DOCK))
84 set_dock_pos((WDock*)obj, x, y);
85 else if(WTHING_IS(obj, WTHING_MENU))
86 set_menu_pos((WMenu*)obj, x, y);
87 }
88
89
winobj_is_visible(WWinObj * obj)90 bool winobj_is_visible(WWinObj *obj)
91 {
92 if(obj->x>=SCREEN->width)
93 return FALSE;
94
95 if(obj->y>=SCREEN->height)
96 return FALSE;
97
98 if(obj->x+obj->w<=0)
99 return FALSE;
100
101 if(obj->y+obj->h<=0)
102 return FALSE;
103
104 return TRUE;
105 }
106
107
focus_stack_above(WWinObj * obj)108 static void focus_stack_above(WWinObj *obj)
109 {
110 wglobal.current_winobj=NULL;
111
112 while(1){
113 obj=obj->stack_above;
114
115 if(obj==NULL)
116 break;
117
118 if(!WWINOBJ_IS_MAPPED(obj))
119 continue;
120
121 set_focus_weak((WThing*)obj);
122 return;
123 }
124
125 set_focus_weak((WThing*)SCREEN);
126 }
127
128
129 /* */
130
131
132 /* Get a pointer to the stacking list that should contain the given
133 * winobj and possibly unlink the winobj. The winobj *must* be stacked
134 * or else this will fail.
135 */
get_listptr(WWinObj * obj,bool unlink)136 static WWinObj **get_listptr(WWinObj *obj, bool unlink)
137 {
138 WWinObj **listptr=NULL;
139
140 if(obj->stack_lvl!=LVL_OTHER){
141 listptr=&(SCREEN->winobj_stack_lists[obj->stack_lvl]);
142 }else{
143 assert(obj->stack_above!=NULL);
144 listptr=&(obj->stack_above->stack_above_list);
145 }
146
147 if(unlink){
148 UNLINK_ITEM(*listptr, obj, stack_next, stack_prev);
149 }
150
151 return listptr;
152 }
153
154
155 /* Raise or lower a winobj.
156 */
do_raiselower_winobj(WWinObj * obj,bool unlink,int raise)157 static void do_raiselower_winobj(WWinObj *obj, bool unlink, int raise)
158 {
159 WScreen *screen=SCREEN;
160 Window win, other=None;
161 WWinObj **listptr, *tmpobj, *nextobj=NULL;
162 int i, mode=Above;
163
164 if(!raise && unlink){
165 /* If lowering lowest transient, lower parent. */
166 while(obj->stack_lvl==LVL_OTHER && obj->stack_prev!=NULL &&
167 obj->stack_prev->stack_next!=obj){
168 obj=obj->stack_above;
169 assert(obj!=NULL);
170 }
171 }
172
173 tmpobj=obj;
174
175 win=lowest_win(obj);
176
177 listptr=get_listptr(obj, unlink);
178 assert(listptr!=NULL);
179
180 /* Find the "normally" stacked parent of a transient. */
181
182 while(tmpobj->stack_above!=NULL)
183 tmpobj=tmpobj->stack_above;
184
185 if(!raise && obj->stack_above!=NULL){
186 tmpobj=obj->stack_above;
187 mode=Above;
188 other=highest_win(tmpobj);
189 }else{
190 /* Find the lowest winobj on this (lower only) or higher stacking
191 * levels.
192 */
193 for(i=tmpobj->stack_lvl+raise; i<N_STACK_LVLS; ++i){
194 if(screen->winobj_stack_lists[i]!=NULL)
195 break;
196 }
197
198 /* Found a window to place this one under */
199 if(i<N_STACK_LVLS){
200 other=lowest_win(screen->winobj_stack_lists[i]);
201 mode=Below;
202 }
203 }
204
205 /* Else none were found - this winobj should be raised highest on
206 * the stack (other and mode are already initialized to None and Above).
207 */
208
209 if(raise){
210 LINK_ITEM(*listptr, obj, stack_next, stack_prev);
211 }else{
212 LINK_ITEM_FIRST(*listptr, obj, stack_next, stack_prev);
213 }
214
215 tmpobj=init_traverse_winobjs_b(obj, &nextobj);
216
217 while(tmpobj!=NULL){
218 restack_windows(tmpobj, other, mode);
219 other=lowest_win(tmpobj);
220 mode=Below;
221 tmpobj=traverse_winobjs_b(tmpobj, obj, &nextobj);
222 }
223 }
224
225
raise_winobj(WWinObj * obj)226 void raise_winobj(WWinObj *obj)
227 {
228 do_raiselower_winobj(obj, TRUE, 1);
229 }
230
231
lower_winobj(WWinObj * obj)232 void lower_winobj(WWinObj *obj)
233 {
234 do_raiselower_winobj(obj, TRUE, 0);
235 }
236
237
raiselower_winobj(WWinObj * obj)238 void raiselower_winobj(WWinObj *obj)
239 {
240 do_raiselower_winobj(obj, TRUE, obj->stack_next!=NULL);
241 }
242
243
244 /* */
245
246
check_loop(WWinObj * obj,WWinObj * above)247 static bool check_loop(WWinObj *obj, WWinObj *above)
248 {
249 WWinObj *tmp=obj;
250
251 if(obj==above)
252 return TRUE;
253
254 obj=obj->stack_above_list;
255
256 while(obj!=NULL){
257 if(obj==above)
258 return TRUE;
259 obj=traverse_winobjs(obj, tmp);
260 }
261 return FALSE;
262 }
263
264
do_restack_winobj(WWinObj * obj,int stack_lvl,WWinObj * stack_above,bool raiselower)265 static void do_restack_winobj(WWinObj *obj, int stack_lvl,
266 WWinObj *stack_above, bool raiselower)
267 {
268 WWinObj **listptr;
269
270 /* Unlink the object from stacking list unless it isn't
271 * yet stacked (restack_winobj called from add_winobj)
272 */
273 if(obj->stack_prev!=NULL)
274 get_listptr(obj, TRUE);
275
276 obj->stack_lvl=stack_lvl;
277 obj->stack_above=stack_above;
278 obj->stack_next=NULL;
279 obj->stack_prev=NULL;
280
281 if(raiselower){
282 do_raiselower_winobj(obj, FALSE, 1);
283 }else{
284 listptr=get_listptr(obj, FALSE);
285 LINK_ITEM(*listptr, obj, stack_next, stack_prev);
286 }
287 }
288
289
restack_winobj(WWinObj * obj,int stack_lvl,bool raiselower)290 void restack_winobj(WWinObj *obj, int stack_lvl, bool raiselower)
291 {
292 if(stack_lvl<0 || stack_lvl>=N_STACK_LVLS)
293 stack_lvl=LVL_NORMAL;
294
295 do_restack_winobj(obj, stack_lvl, NULL, raiselower);
296 }
297
298
restack_winobj_above(WWinObj * obj,WWinObj * stack_above,bool raiselower)299 void restack_winobj_above(WWinObj *obj, WWinObj *stack_above, bool raiselower)
300 {
301 if(check_loop(obj, stack_above)){
302 warn("Attempt to create loop in winobj stack. "
303 "Broken WM_TRANSIENT_FOR hints?");
304 do_restack_winobj(obj, LVL_NORMAL, NULL, raiselower);
305 }else{
306 do_restack_winobj(obj, LVL_OTHER, stack_above, raiselower);
307 }
308 }
309
310
add_winobj(WWinObj * obj,int ws,int stack_lvl)311 void add_winobj(WWinObj *obj, int ws, int stack_lvl)
312 {
313 link_thing((WThing*)SCREEN, (WThing*)obj);
314
315 if(ws<0 && ws!=WORKSPACE_STICKY)
316 ws=SCREEN->current_workspace;
317
318 obj->workspace=ws;
319
320 restack_winobj(obj, stack_lvl, TRUE);
321 }
322
323
add_winobj_above(WWinObj * obj,WWinObj * stack_above)324 void add_winobj_above(WWinObj *obj, WWinObj *stack_above)
325 {
326 link_thing((WThing*)SCREEN, (WThing*)obj);
327
328 obj->workspace=stack_above->workspace;
329
330 restack_winobj_above(obj, stack_above, TRUE);
331 }
332
333
334 /* */
335
336
relink_above(WWinObj * obj,WWinObj ** listptr)337 static void relink_above(WWinObj *obj, WWinObj **listptr)
338 {
339 WWinObj *p, *next, **listptr2;
340
341 for(p=obj->stack_above_list; p!=NULL; p=next){
342 next=p->stack_next;
343 UNLINK_ITEM(obj->stack_above_list, p, stack_next, stack_prev);
344 LINK_ITEM(*listptr, p, stack_next, stack_prev);
345 }
346 }
347
348
unlink_winobj_d(WWinObj * obj)349 void unlink_winobj_d(WWinObj *obj)
350 {
351 WWinObj **listptr;
352
353 if(wglobal.current_winobj==obj)
354 focus_stack_above(obj);
355
356 listptr=get_listptr(obj, TRUE);
357
358 destroy_subthings((WThing*)obj);
359 unlink_thing((WThing*)obj);
360
361 if(wglobal.previous_winobj==obj)
362 wglobal.previous_winobj=NULL;
363
364 relink_above(obj, listptr);
365 }
366
367
368 /* */
369
370
do_map_winobj(WWinObj * obj)371 void do_map_winobj(WWinObj *obj)
372 {
373 WFrame *frame;
374
375 if(obj->flags&WWINOBJ_HIDDEN)
376 return;
377
378 if(WTHING_IS(obj, WTHING_MENU)){
379 XMapWindow(wglobal.dpy, ((WMenu*)obj)->menu_win);
380 }else if(WTHING_IS(obj, WTHING_DOCK)){
381 XMapWindow(wglobal.dpy, ((WDock*)obj)->win);
382 }else if(WTHING_IS(obj, WTHING_FRAME)){
383 frame=(WFrame*)obj;
384
385 if(!WFRAME_IS_SHADE(frame))
386 XMapWindow(wglobal.dpy, frame->frame_win);
387
388 if(!WFRAME_IS_NO_BAR(frame))
389 XMapWindow(wglobal.dpy, frame->bar_win);
390 }
391
392 obj->flags|=WWINOBJ_MAPPED;
393 }
394
395
do_unmap_winobj(WWinObj * obj)396 void do_unmap_winobj(WWinObj *obj)
397 {
398 WFrame *frame;
399
400 if(!(obj->flags&WWINOBJ_MAPPED))
401 return;
402
403 if(WTHING_IS(obj, WTHING_MENU)){
404 XUnmapWindow(wglobal.dpy, ((WMenu*)obj)->menu_win);
405 }else if(WTHING_IS(obj, WTHING_DOCK)){
406 XUnmapWindow(wglobal.dpy, ((WDock*)obj)->win);
407 }else if(WTHING_IS(obj, WTHING_FRAME)){
408 frame=(WFrame*)obj;
409 XUnmapWindow(wglobal.dpy, frame->frame_win);
410 XUnmapWindow(wglobal.dpy, frame->bar_win);
411 }
412
413 obj->flags&=~WWINOBJ_MAPPED;
414 }
415
416
map_winobj(WWinObj * obj)417 void map_winobj(WWinObj *obj)
418 {
419 if(!on_current_workspace(obj))
420 return;
421
422 do_map_winobj(obj);
423 }
424
425
unmap_winobj(WWinObj * obj)426 void unmap_winobj(WWinObj *obj)
427 {
428 do_unmap_winobj(obj);
429
430 if(wglobal.current_winobj==obj){
431 wglobal.current_winobj=NULL;
432 focus_stack_above(obj);
433 }
434 }
435
436
437 /* */
438
439
440 /* Will traverse through a tree of winobjs each branch at a time.
441 * Usage:
442 *
443 * WWinObj *root;
444 *
445 * root=obj;
446 * # obj=root->stack_above_list; if root is not to be included
447 *
448 * while(obj!=NULL){
449 * do the stuff on obj
450 * obj=traverse_winobjs(obj, root);
451 * }
452 */
453
traverse_winobjs(WWinObj * current,WWinObj * root)454 WWinObj* traverse_winobjs(WWinObj *current, WWinObj *root)
455 {
456 if(current==NULL)
457 return NULL;
458
459 if(current->stack_above_list!=NULL)
460 return current->stack_above_list;
461
462 if(current==root)
463 return NULL;
464
465 while(current->stack_next==NULL){
466 current=current->stack_above;
467
468 if(current==root)
469 return NULL;
470
471 assert(current!=NULL);
472 }
473
474 return current->stack_next;
475 }
476
477
find_top_node(WWinObj * p)478 static WWinObj *find_top_node(WWinObj *p)
479 {
480 while(p->stack_above_list!=NULL){
481 p=p->stack_above_list;
482 p=p->stack_prev;
483 }
484
485 return p;
486 }
487
488
489 /* Will traverse through a tree backwards (starting from the topmost
490 * item in the last branch). The "root" winobj is included; last of course.
491 * Usage:
492 *
493 * WWinObj *helper, *obj;
494 *
495 * obj=init_traverse_winobjs_b(foo, &helper);
496 *
497 * while(obj!=NULL){
498 * do the stuff on obj
499 * obj=traverse_winobjs_b(obj, foo, &helper);
500 * }
501 */
502
init_traverse_winobjs_b(WWinObj * root,WWinObj ** next)503 WWinObj* init_traverse_winobjs_b(WWinObj *root, WWinObj **next)
504 {
505 WWinObj *p;
506
507 p=root;
508
509 if(p->stack_above_list==NULL){
510 *next=NULL;
511 return p;
512 }
513
514 p=find_top_node(p);
515
516 if(p->stack_prev!=p)
517 *next=p->stack_prev;
518 else
519 *next=p->stack_above;
520
521 return p;
522 }
523
524
traverse_winobjs_b(WWinObj * p,WWinObj * root,WWinObj ** next)525 WWinObj* traverse_winobjs_b(WWinObj *p, WWinObj *root, WWinObj **next)
526 {
527 WWinObj *tmp=NULL;
528
529 if(p==NULL || *next==NULL || p==root)
530 return NULL;
531
532 if(*next==p->stack_above){
533 tmp=(*next)->stack_next;
534 if(tmp!=NULL)
535 tmp=find_top_node(tmp);
536 else
537 tmp=(*next)->stack_above;
538 }else if((*next)->stack_prev->stack_next==NULL){
539 tmp=(*next)->stack_above;
540 }else{
541 tmp=(*next)->stack_prev;
542 }
543
544 p=*next;
545 *next=tmp;
546
547 return p;
548 }
549