1 /*
2  * Copyright (c) 2002 Sasha Vasko <sasha@aftercode.net>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  *
18  */
19 
20 /**********************************************************************
21  *
22  * Add a new window, put the titlbar and other stuff around
23  * the window
24  *
25  **********************************************************************/
26 #define LOCAL_DEBUG
27 
28 #include "../../configure.h"
29 
30 #include "asinternals.h"
31 #include "../../libAfterStep/moveresize.h"
32 
33 /*************************************************************************/
34 
35 /*
36  * The following function was written for
37  * new hints management code in libafterstep :
38  */
afterstep_parent_hints_func(Window parent,ASParentHints * dst)39 Bool afterstep_parent_hints_func (Window parent, ASParentHints * dst)
40 {
41 	if (dst == NULL || parent == None)
42 		return False;
43 
44 	memset (dst, 0x00, sizeof (ASParentHints));
45 	dst->parent = parent;
46 	if (parent != Scr.Root) {
47 		ASWindow *p = find_group_lead_aswindow (parent);
48 
49 		if (p != NULL) {
50 			dst->desktop = p->status->desktop;
51 			set_flags (dst->flags, AS_StartDesktop);
52 
53 			/* we may need to move our viewport so that the parent becomes visible : */
54 			if (!ASWIN_GET_FLAGS (p, AS_Iconic)) {
55 #if 0
56 				int x, y;
57 				unsigned int width, height;
58 				x = p->status->x - p->decor->west;
59 				y = p->status->y - p->decor->north;
60 				width = p->status->width + p->decor->west + p->decor->east;
61 				height = p->status->height + p->decor->north + p->decor->south;
62 
63 				if ((dst->viewport_x =
64 						 calculate_viewport (&x, width, p->scr->Vx,
65 																 p->scr->MyDisplayWidth,
66 																 p->scr->VxMax)) != p->scr->Vx)
67 					set_flags (dst->flags, AS_StartViewportX);
68 
69 				if ((dst->viewport_y =
70 						 calculate_viewport (&y, height, p->scr->Vy,
71 																 p->scr->MyDisplayHeight,
72 																 p->scr->VyMax)) != p->scr->Vy)
73 					set_flags (dst->flags, AS_StartViewportY);
74 #endif
75 			}
76 		}
77 	}
78 	return True;
79 }
80 
81 
init_aswindow(ASWindow * t,Bool free_resources)82 void init_aswindow (ASWindow * t, Bool free_resources)
83 {
84 	if (!t)
85 		return;
86 	if (free_resources && t->magic == MAGIC_ASWINDOW) {
87 		if (t->transients)
88 			destroy_asvector (&(t->transients));
89 		if (t->status)
90 			free (t->status);
91 		if (t->hints)
92 			destroy_hints (t->hints, False);
93 	}
94 	memset (t, 0x00, sizeof (ASWindow));
95 	t->magic = MAGIC_ASWINDOW;
96 }
97 
check_aswindow_shaped(ASWindow * asw)98 void check_aswindow_shaped (ASWindow * asw)
99 {
100 	int boundingShaped = False;
101 #ifdef SHAPE
102 	int dumm, clipShaped = False;
103 	unsigned udumm;
104 	XShapeQueryExtents (dpy, asw->w,
105 											&boundingShaped, &dumm, &dumm, &udumm, &udumm,
106 											&clipShaped, &dumm, &dumm, &udumm, &udumm);
107 #endif													/* SHAPE */
108 	if (boundingShaped)
109 		ASWIN_SET_FLAGS (asw, AS_Shaped);
110 	else
111 		ASWIN_CLEAR_FLAGS (asw, AS_Shaped);
112 }
113 
quietly_reparent_aswindow(ASWindow * asw,Window dst,Bool user_root_pos)114 void quietly_reparent_aswindow (ASWindow * asw, Window dst,
115 																Bool user_root_pos)
116 {
117 	if (asw) {
118 		Window cover = get_desktop_cover_window ();
119 		LOCAL_DEBUG_OUT ("cover = %lX", cover);
120 		quietly_reparent_canvas (asw->frame_canvas, dst, AS_FRAME_EVENT_MASK,
121 														 user_root_pos, cover);
122 		quietly_reparent_canvas (asw->icon_canvas, dst, AS_ICON_EVENT_MASK,
123 														 user_root_pos, cover);
124 		if (asw->icon_title_canvas != asw->icon_canvas)
125 			quietly_reparent_canvas (asw->icon_title_canvas, dst,
126 															 AS_ICON_TITLE_EVENT_MASK, user_root_pos,
127 															 cover);
128 	}
129 }
130 
131 /****************************************************************************
132  * Interprets the property MOTIF_WM_HINTS, sets decoration and functions
133  * accordingly
134  *****************************************************************************/
SelectDecor(ASWindow * t)135 void SelectDecor (ASWindow * t)
136 {
137 	ASHints *hints = t->hints;
138 	ASFlagType tflags = hints->flags;
139 
140 	if (!get_flags (hints->function_mask, AS_FuncResize)
141 			|| !get_flags (Scr.Look.flags, DecorateFrames)) {
142 		/* a wide border, with corner tiles */
143 		if (get_flags (tflags, AS_Frame))
144 			clear_flags (t->hints->flags, AS_Frame);
145 	}
146 }
147 
148 
collect_aswindow_hints(ASWindow * asw,ASRawHints * raw_hints)149 Bool collect_aswindow_hints (ASWindow * asw, ASRawHints * raw_hints)
150 {
151 	if (!collect_hints (ASDefaultScr, asw->w, HINT_ANY, raw_hints))
152 		return False;
153 	if (is_output_level_under_threshold (OUTPUT_LEVEL_HINTS))
154 		print_hints (NULL, NULL, raw_hints);
155 
156 	if (raw_hints->wm_hints == NULL)
157 		memset (&(asw->saved_wm_hints), 0x00, sizeof (asw->saved_wm_hints));
158 	else
159 		memcpy (&(asw->saved_wm_hints), raw_hints->wm_hints,
160 						sizeof (asw->saved_wm_hints));
161 
162 	if (raw_hints->wm_normal_hints == NULL)
163 		memset (&(asw->saved_wm_normal_hints), 0x00,
164 						sizeof (asw->saved_wm_normal_hints));
165 	else
166 		memcpy (&(asw->saved_wm_normal_hints), raw_hints->wm_normal_hints,
167 						sizeof (asw->saved_wm_normal_hints));
168 
169 	return True;
170 }
171 
172 /***********************************************************************
173  *
174  *  Procedure:
175  *	AddWindow - add a new window to the afterstep list
176  *
177  *  Returned Value:
178  *	(ASWindow *) - pointer to the ASWindow structure
179  *
180  *  Inputs:
181  *	w	- the window id of the window to add
182  *	iconm	- flag to tell if this is an icon manager window
183  *
184  ***********************************************************************/
AddWindow(Window w,Bool from_map_request)185 ASWindow *AddWindow (Window w, Bool from_map_request)
186 {
187 	ASWindow *tmp_win;						/* new afterstep window structure */
188 	ASRawHints raw_hints;
189 	ASHints *hints = NULL;
190 	ASStatusHints status;
191 	Bool pending_placement;
192 
193 
194 	/* allocate space for the afterstep window */
195 	tmp_win = safecalloc (1, sizeof (ASWindow));
196 	init_aswindow (tmp_win, False);
197 
198 	tmp_win->w = w;
199 	if (validate_drawable (w, NULL, NULL) == None) {
200 		free ((char *)tmp_win);
201 		return (NULL);
202 	}
203 
204 	/* we want to set event mask as soon as possible so not to miss eny configure
205 	 * request that happen after we get client's geometry in collect_hints,
206 	 * and the moment when we afctually setup client's decorations.
207 	 */
208 	XSelectInput (dpy, w, AS_CLIENT_EVENT_MASK);
209 
210 	if (collect_aswindow_hints (tmp_win, &raw_hints)) {
211 		hints =
212 				merge_hints (&raw_hints, Database, &status,
213 										 Scr.Look.supported_hints, HINT_ANY, NULL, w);
214 		destroy_raw_hints (&raw_hints, True);
215 		if (hints) {
216 			show_debug (__FILE__, __FUNCTION__, __LINE__,
217 									"Window management hints collected and merged for window %X",
218 									w);
219 			if (is_output_level_under_threshold (OUTPUT_LEVEL_HINTS)) {
220 				print_clean_hints (NULL, NULL, hints);
221 				print_status_hints (NULL, NULL, &status);
222 			}
223 		} else {
224 			show_warning
225 					("Failed to merge window management hints for window %X", w);
226 			free ((char *)tmp_win);
227 			XSelectInput (dpy, w, 0);
228 			return (NULL);
229 		}
230 		tmp_win->hints = hints;
231 	} else {
232 		show_warning ("Unable to obtain window management hints for window %X",
233 									w);
234 		free ((char *)tmp_win);
235 		XSelectInput (dpy, w, 0);
236 		return (NULL);
237 	}
238 	SelectDecor (tmp_win);
239 
240 	tmp_win->wm_state_transition =
241 			get_flags (status.flags,
242 								 AS_Iconic) ? ASWT_Withdrawn2Iconic :
243 			ASWT_Withdrawn2Normal;
244 
245 	pending_placement = init_aswindow_status (tmp_win, &status);
246 #ifdef SHAPE
247 	XShapeSelectInput (dpy, tmp_win->w, ShapeNotifyMask);
248 #endif
249 	check_aswindow_shaped (tmp_win);
250 	/*
251 	 * Make sure the client window still exists.  We don't want to leave an
252 	 * orphan frame window if it doesn't.  Since we now have the server
253 	 * grabbed, the window can't disappear later without having been
254 	 * reparented, so we'll get a DestroyNotify for it.  We won't have
255 	 * gotten one for anything up to here, however.
256 	 */
257 	ASSync (False);
258 	grab_server ();
259 /*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv Start Grab vvvvvvvvvvvvvvvvvvvvv */
260 	if (validate_drawable (tmp_win->w, NULL, NULL) == None) {
261 		destroy_hints (tmp_win->hints, False);
262 		free ((char *)tmp_win);
263 		ungrab_server ();
264 		return (NULL);
265 	}
266 	XSetWindowBorderWidth (dpy, tmp_win->w, 0);
267 
268 	/* add the window into the afterstep list */
269 	enlist_aswindow (tmp_win);
270 	redecorate_window (tmp_win, False);
271 	/* saving window management properties : */
272 	set_client_desktop (tmp_win->w, as_desk2ext_desk_safe(ASWIN_DESK (tmp_win)));
273 
274 	/* we have to set shape on frame window. If window has title -
275 	 * on_window_title_changed will take care of it - otherwise we force it
276 	 * by calling SetShape directly.
277 	 */
278 
279 	if (tmp_win->tbar)
280 		on_window_title_changed (tmp_win, False);
281 	else
282 		SetShape (tmp_win, 0);
283 	/* Must do it now or else Java will freak out !!! */
284 	XMapRaised (dpy, tmp_win->w);
285 	/* this must happen before RaiseWindow call or else stacking order fails with BadMatch */
286 	XMapWindow (dpy, tmp_win->frame);
287 
288 	RaiseWindow (tmp_win);
289 	ASSync (False);
290 
291 /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ End Grab ^^^^^^^^^^^^^^^^^^^^ */
292 	ungrab_server ();
293 
294 	/* doing that for any window seems to resolve java sizing bugs */
295 	/* this will get called from set_window_wm_state() again just few lines below
296 	   which is a bit of a excess, but we don't seem to have much choice */
297 	on_window_status_changed (tmp_win, True);
298 
299 /* We cannot map frame window at that point as it will play havoc with initial placement */
300 /*    XMapRaised (dpy, tmp_win->frame); */
301 	if (pending_placement) {
302 		if (!place_aswindow (tmp_win)) {
303 			LOCAL_DEBUG_OUT
304 					("window status initialization failed for %lX - will put it where it is",
305 					 w);
306 			/*  Destroy (tmp_win, False);
307 			   return NULL;
308 			 */
309 		}
310 	}
311 
312 
313 	set_window_wm_state (tmp_win, get_flags (status.flags, AS_Iconic),
314 											 from_map_request);
315 
316 	if (ASWIN_HFLAGS (tmp_win, AS_AvoidCover))
317 		enforce_avoid_cover (tmp_win);
318 
319 	/*
320 	 * Reparenting generates an UnmapNotify event, followed by a MapNotify.
321 	 * Set the map state to FALSE to prevent a transition back to
322 	 * WithdrawnState in HandleUnmapNotify.  Map state gets set correctly
323 	 * again in HandleMapNotify.
324 	 */
325 	broadcast_config (M_ADD_WINDOW, tmp_win);
326 	broadcast_res_names (tmp_win);
327 	broadcast_icon_name (tmp_win);
328 	broadcast_window_name (tmp_win);
329 
330 	InstallWindowColormaps (tmp_win);
331 	set_flags (tmp_win->internal_flags, ASWF_WindowComplete);
332 
333 	return (tmp_win);
334 }
335 
336 /* hints gets swallowed, but status does not : */
337 /* w must be unmapped !!!! */
AddInternalWindow(Window w,ASInternalWindow ** pinternal,ASHints ** phints,ASStatusHints * status)338 ASWindow *AddInternalWindow (Window w, ASInternalWindow ** pinternal,
339 														 ASHints ** phints, ASStatusHints * status)
340 {
341 	ASWindow *tmp_win;						/* new afterstep window structure */
342 	ASHints *hints = *phints;
343 
344 	/* allocate space for the afterstep window */
345 	tmp_win = safecalloc (1, sizeof (ASWindow));
346 	init_aswindow (tmp_win, False);
347 
348 	tmp_win->w = w;
349 	if (validate_drawable (w, NULL, NULL) == None) {
350 		free ((char *)tmp_win);
351 		return (NULL);
352 	}
353 
354 	tmp_win->internal = *pinternal;
355 	*pinternal = NULL;
356 
357 	if (hints) {
358 		show_debug (__FILE__, __FUNCTION__, __LINE__,
359 								"Window management hints supplied for window %X", w);
360 		if (is_output_level_under_threshold (OUTPUT_LEVEL_HINTS)) {
361 			print_clean_hints (NULL, NULL, hints);
362 			print_status_hints (NULL, NULL, status);
363 		}
364 	}
365 	tmp_win->hints = hints;
366 	*phints = NULL;
367 
368 	SelectDecor (tmp_win);
369 
370 	tmp_win->wm_state_transition =
371 			get_flags (status->flags,
372 								 AS_Iconic) ? ASWT_Withdrawn2Iconic :
373 			ASWT_Withdrawn2Normal;
374 
375 	init_aswindow_status (tmp_win, status);
376 
377 #ifdef SHAPE
378 	XShapeSelectInput (dpy, tmp_win->w, ShapeNotifyMask);
379 #endif
380 
381 	XSetWindowBorderWidth (dpy, tmp_win->w, 0);
382 
383 	/* add the window into the afterstep list */
384 	enlist_aswindow (tmp_win);
385 
386 	redecorate_window (tmp_win, False);
387 	on_window_title_changed (tmp_win, False);
388 	set_window_wm_state (tmp_win, get_flags (status->flags, AS_Iconic),
389 											 False);
390 	RaiseWindow (tmp_win);
391 /*    activate_aswindow( tmp_win, False, False ); */
392 
393 	/*
394 	 * Reparenting generates an UnmapNotify event, followed by a MapNotify.
395 	 * Set the map state to FALSE to prevent a transition back to
396 	 * WithdrawnState in HandleUnmapNotify.  Map state gets set correctly
397 	 * again in HandleMapNotify.
398 	 */
399 	broadcast_config (M_ADD_WINDOW, tmp_win);
400 	broadcast_res_names (tmp_win);
401 	broadcast_icon_name (tmp_win);
402 	broadcast_window_name (tmp_win);
403 
404 #if 0
405 /* TODO : */
406 	if (NeedToResizeToo) {
407 		XWarpPointer (dpy, Scr.Root, Scr.Root, 0, 0, Scr.MyDisplayWidth,
408 									Scr.MyDisplayHeight,
409 									tmp_win->frame_x + (tmp_win->frame_width >> 1),
410 									tmp_win->frame_y + (tmp_win->frame_height >> 1));
411 		resize_window (tmp_win->w, tmp_win, 0, 0, 0, 0);
412 	}
413 #endif
414 
415 	set_flags (tmp_win->internal_flags, ASWF_WindowComplete);
416 	return (tmp_win);
417 }
418 
419 /***************************************************************************
420  * Handles destruction of a window
421  ****************************************************************************/
Destroy(ASWindow * asw,Bool kill_client)422 void Destroy (ASWindow * asw, Bool kill_client)
423 {
424 	static int nested_level = 0;
425 	/*
426 	 * Warning, this is also called by HandleUnmapNotify; if it ever needs to
427 	 * look at the event, HandleUnmapNotify will have to mash the UnmapNotify
428 	 * into a DestroyNotify.
429 	 */
430 	if (AS_ASSERT (asw))
431 		return;
432 	LOCAL_DEBUG_CALLER_OUT ("asw(%p)->internal(%p)->data(%p)", asw,
433 													asw->internal,
434 													asw->internal ? asw->internal->data : NULL);
435 
436 	/* we could be recursively called from delist_aswindow call - trying to prevent that : */
437 	if (nested_level > 0)
438 		return;
439 	++nested_level;
440 
441 	grab_server ();
442 	if (!ASWIN_GET_FLAGS (asw, AS_Dead)) {
443 		if (validate_drawable (asw->w, NULL, NULL) == None)
444 			ASWIN_SET_FLAGS (asw, AS_Dead);
445 	}
446 
447 	if (!ASWIN_GET_FLAGS (asw, AS_Dead)) {
448 		if (asw->internal == NULL)
449 			XRemoveFromSaveSet (dpy, asw->w);
450 		XSelectInput (dpy, asw->w, NoEventMask);
451 	}
452 	XUnmapWindow (dpy, asw->frame);
453 	grab_window_input (asw, True);
454 
455 	if (Scr.moveresize_in_progress != NULL &&
456 			Scr.moveresize_in_progress->mr == asw->frame_canvas) {
457 		complete_interactive_action (Scr.moveresize_in_progress, True);
458 		Scr.moveresize_in_progress = NULL;
459 		if (!get_flags (asw->internal_flags, ASWF_WindowComplete)) {
460 			/* will be deleted from AddWindow  - can't destroy here, since we are in recursive event loop */
461 			ungrab_server ();
462 			--nested_level;
463 			return;
464 		}
465 		/* otherwise we can delete window normally - there are no recursion */
466 	}
467 
468 	while (timer_remove_by_data ((void *)asw)) ;
469 
470 	if (!get_flags (AfterStepState, ASS_Shutdown)) {
471 		XSync (dpy, 0);
472 		SendPacket (-1, M_DESTROY_WINDOW, 3, asw->w, asw->frame,
473 								(unsigned long)asw);
474 	}
475 
476 	UninstallWindowColormaps (asw);
477 	CheckWarpingFocusDestroyed (asw);
478 	CheckGrabbedFocusDestroyed (asw);
479 
480 	if (asw == Scr.Windows->focused)
481 		focus_prev_aswindow (asw);
482 
483 	if (asw == Scr.Windows->ungrabbed)
484 		Scr.Windows->ungrabbed = NULL;
485 
486 	if (asw == Scr.Windows->active) {
487 		LOCAL_DEBUG_CALLER_OUT ("CHANGE Scr.Windows->active from %p to NULL",
488 														Scr.Windows->active);
489 		Scr.Windows->active = NULL;
490 	}
491 	if (asw == Scr.Windows->hilited)
492 		Scr.Windows->hilited = NULL;
493 	if (asw == Scr.Windows->pressed)
494 		Scr.Windows->pressed = NULL;
495 
496 	if (!kill_client && asw->internal == NULL
497 			&& !ASWIN_HFLAGS (asw, AS_Module))
498 		RestoreWithdrawnLocation (asw, True);
499 
500 	if (asw->internal) {
501 		if (asw->internal->destroy)
502 			asw->internal->destroy (asw->internal);
503 		if (asw->internal->data)		/* in case destroy() above did not work properly */
504 			free (asw->internal->data);
505 		free (asw->internal);
506 		asw->internal = NULL;
507 	}
508 
509 	redecorate_window (asw, True);
510 	unregister_aswindow (asw->w);
511 	remove_iconbox_icon (asw);
512 	delist_aswindow (asw);
513 
514 	init_aswindow (asw, True);
515 
516 	XSync (dpy, 0);
517 	ungrab_server ();
518 
519 	memset (asw, 0x00, sizeof (ASWindow));
520 	free (asw);
521 	--nested_level;
522 
523 	return;
524 }
525 
526 /***********************************************************************
527  *  Procedure:
528  *	RestoreWithdrawnLocation
529  *  Puts windows back where they were before afterstep took over
530  ************************************************************************/
RestoreWithdrawnLocation(ASWindow * asw,Bool restart)531 void RestoreWithdrawnLocation (ASWindow * asw, Bool restart)
532 {
533 	int x = 0, y = 0;
534 	unsigned int width = 0, height = 0, bw = 0;
535 	Bool map_too = False;
536 
537 	LOCAL_DEBUG_CALLER_OUT ("%p, %d", asw, restart);
538 
539 	if (AS_ASSERT (asw))
540 		return;
541 
542 	if (asw->status) {
543 		if (asw->status->width > 0)
544 			width = asw->status->width;
545 		if (asw->status->height > 0)
546 			width = asw->status->height;
547 		if (get_flags (asw->status->flags, AS_StartBorderWidth))
548 			bw = asw->status->border_width;
549 		/* We'll get withdrawn
550 		 * location in virtual coordinates, so that when window is mapped again
551 		 * we'll get it in correct position on the virtual screen.
552 		 * Besides when we map window we check its postion so it would not be
553 		 * completely off the screen ( if PPosition only, since User can place
554 		 * window anywhere he wants ).
555 		 *                             ( Sasha )
556 		 */
557 
558 		make_detach_pos (asw->hints, asw->status, &(asw->anchor), &x, &y);
559 
560 		if (get_flags (asw->status->flags, AS_Iconic)) {
561 			/* if we don't do that while exiting AfterStep -
562 			   we will loose the client while starting AS again, as window will be
563 			   unmapped, AS will be waiting for it to be mapped by client, and
564 			   client will not be aware that it should do so, as WM exits and
565 			   startups are transparent for it */
566 			map_too = True;
567 		}
568 		/*
569 		 * Prevent the receipt of an UnmapNotify, since that would
570 		 * cause a transition to the Withdrawn state.
571 		 */
572 		LOCAL_DEBUG_OUT ("map_too = %d", map_too);
573 
574 		if (!ASWIN_GET_FLAGS (asw, AS_Dead)) {
575 			XSelectInput (dpy, asw->w, NoEventMask);
576 			if (get_parent_window (asw->w) == asw->frame) {
577 				ASStatusHints withdrawn_state;
578 				/* !!! Most of it has been done in datach_basic_widget : */
579 				XReparentWindow (dpy, asw->w, Scr.Root, x, y);
580 				withdrawn_state.flags = AS_Withdrawn;
581 				withdrawn_state.icon_window = None;
582 				set_client_state (asw->w, &withdrawn_state);
583 
584 				if (width > 0 && height > 0)
585 					XResizeWindow (dpy, asw->w, width, height);
586 				XSetWindowBorderWidth (dpy, asw->w, bw);
587 				LOCAL_DEBUG_OUT ("map_too = %d", map_too);
588 				if (map_too)
589 					XMapWindow (dpy, asw->w);
590 /*				else
591 					XUnmapWindow (dpy, asw->w); */
592 				XSync (dpy, False);
593 			}
594 			/* signaling client that we no longer handle it : */
595 			set_multi32bit_property (asw->w, _XA_WM_STATE, _XA_WM_STATE, 2,
596 															 WithdrawnState, None);
597 		}
598 	}
599 }
600 
601 /**********************************************************************/
602 /* The End                                                            */
603 /**********************************************************************/
604