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