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