1 /* windows.c -- window manipulation
2    $Id$
3 
4    Copyright (C) 1999 John Harper <john@dcs.warwick.ac.uk>
5 
6    This file is part of sawfish.
7 
8    sawfish is free software; you can redistribute it and/or modify it
9    under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2, or (at your option)
11    any later version.
12 
13    sawfish is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with sawfish; see the file COPYING.   If not, write to
20    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
21 
22 #include "sawfish.h"
23 #include <assert.h>
24 #include <string.h>
25 #include <X11/extensions/shape.h>
26 #include <X11/extensions/XTest.h>
27 #include <X11/keysym.h>
28 #include <X11/Xatom.h>
29 #include <glib.h>
30 
31 Lisp_Window *window_list;
32 int window_type;
33 
34 Lisp_Window *focus_window;
35 
36 int pending_destroys;
37 
38 static bool initialising;
39 
40 DEFSYM(add_window_hook, "add-window-hook");
41 DEFSYM(before_add_window_hook, "before-add-window-hook");
42 DEFSYM(after_add_window_hook, "after-add-window-hook");
43 DEFSYM(place_window_hook, "place-window-hook");
44 DEFSYM(placed, "placed");
45 DEFSYM(after_framing_hook, "after-framing-hook");
46 DEFSYM(after_initialization_hook, "after-initialization-hook");
47 DEFSYM(remove_window_hook, "remove-window-hook");
48 
49 /* for visibility-notify-hook */
50 DEFSYM(fully_obscured, "fully-obscured");
51 DEFSYM(partially_obscured, "partially-obscured");
52 DEFSYM(unobscured, "unobscured");
53 
54 /* for window-size-hints */
55 DEFSYM(min_width, "min-width");
56 DEFSYM(min_height, "min-height");
57 DEFSYM(max_width, "max-width");
58 DEFSYM(max_height, "max-height");
59 DEFSYM(width_inc, "width-inc");
60 DEFSYM(height_inc, "height-inc");
61 DEFSYM(base_width, "base-width");
62 DEFSYM(base_height, "base-height");
63 DEFSYM(min_aspect, "min-aspect");
64 DEFSYM(max_aspect, "max-aspect");
65 DEFSYM(user_size, "user-size");
66 DEFSYM(program_size, "program-size");
67 DEFSYM(user_position, "user-position");
68 DEFSYM(program_position, "program-position");
69 DEFSYM(window_gravity, "window-gravity");
70 
71 /* for window-gravity */
72 DEFSYM(forget, "forget");
73 DEFSYM(static, "static");
74 DEFSYM(north_west, "north-west");
75 DEFSYM(north, "north");
76 DEFSYM(north_east, "north-east");
77 DEFSYM(west, "west");
78 DEFSYM(east, "east");
79 DEFSYM(south_west, "south-west");
80 DEFSYM(south, "south");
81 DEFSYM(south_east, "south-east");
82 
83 static repv gravity_map[StaticGravity+1];
84 
85 /* In sawfish-1.3.3, the only callback used is keymap_prop_change. */
86 
87 static struct prop_handler *prop_handlers;
88 
89 struct prop_handler {
90     struct prop_handler *next;
91     repv prop;
92     void (*callback) (Lisp_Window *w, repv prop, repv old, repv new);
93 };
94 
95 /* utilities */
96 
97 /* Returns true if we should manage window ID */
98 static bool
mapped_not_override_p(Window id)99 mapped_not_override_p (Window id)
100 {
101     XWindowAttributes wa;
102 
103     XGetWindowAttributes(dpy, id, &wa);
104     return ((wa.map_state != IsUnmapped) && (wa.override_redirect != True));
105 }
106 
107 /* Returns true if the window's Input hint is set (or defaults to being set) */
108 static bool
window_input_hint_p(Lisp_Window * w)109 window_input_hint_p (Lisp_Window *w)
110 {
111     if (w->wmhints != 0 && (w->wmhints->flags & InputHint))
112 	return w->wmhints->input;
113     else
114 	return TRUE;
115 }
116 
117 static Window queued_focus_id;
118 static bool queued_take_focus;
119 static bool queued_set_focus;
120 static int queued_focus_revert;
121 static Time queued_focus_time;
122 
123 /* We can lose the focus sometimes, notably after a was-focused
124    window is closed while a keyboard grab exists.. (netscape) */
125 static void
check_for_lost_focus(void)126 check_for_lost_focus (void)
127 {
128     Window focus;
129     int revert_to;
130     XGetInputFocus (dpy, &focus, &revert_to);
131     if (focus == None || focus == PointerRoot)
132     {
133 	DB (("lost focus (%ld)\n", focus));
134 	focus_on_window (focus_window);
135     }
136 }
137 
138 void
commit_queued_focus_change(void)139 commit_queued_focus_change (void)
140 {
141     if (0 && queued_focus_id == 0)
142 	check_for_lost_focus ();
143 
144     if (queued_focus_id != 0)
145     {
146 	if (queued_take_focus)
147 	{
148 	    DB(("  sending WM_TAKE_FOCUS %x %ld\n",
149 		(unsigned) queued_focus_id, queued_focus_time));
150 	    send_client_message (queued_focus_id,
151 				 xa_wm_take_focus,
152 				 queued_focus_time);
153 	}
154 	if (queued_set_focus)
155 	{
156 	    DB(("  focusing %x %ld\n",
157 		(unsigned) queued_focus_id, queued_focus_time));
158 	    XSetInputFocus (dpy, queued_focus_id,
159 			    queued_focus_revert, queued_focus_time);
160 	}
161 	queued_focus_id = 0;
162     }
163 }
164 
165 /* Give the input focus to window W, or to no window if W is null */
166 void
focus_on_window(Lisp_Window * w)167 focus_on_window (Lisp_Window *w)
168 {
169     /* something's going to change, so */
170     queued_focus_id = 0;
171     queued_focus_time = last_event_time;
172     queued_set_focus = FALSE;
173     queued_take_focus = FALSE;
174 
175     if (w != 0 && !WINDOW_IS_GONE_P (w) && w->visible)
176     {
177 	DB(("focus_on_window (%s)\n", rep_STR(w->name)));
178 	if (!w->client_unmapped)
179 	{
180 	    queued_focus_id = w->id;
181 
182 	    if (w->does_wm_take_focus)
183 	    {
184 		queued_take_focus = TRUE;
185 
186 		if (window_input_hint_p (w))
187 		    queued_set_focus = TRUE;
188 	    }
189 	    else
190 		queued_set_focus = TRUE;
191 	}
192 	else
193 	{
194 	    queued_focus_id = w->frame;
195 	    queued_set_focus = TRUE;
196 	}
197 
198 	queued_focus_revert = RevertToParent;
199     }
200 
201     if (queued_focus_id == 0 || (!queued_set_focus && !queued_take_focus))
202     {
203 	DB(("focus_on_window (nil)\n"));
204 	queued_focus_id = no_focus_window;
205 	queued_set_focus = TRUE;
206 	queued_focus_revert = RevertToNone;
207 	queued_focus_time = last_event_time;
208     }
209 }
210 
211 /* Should be called when W is no longer focusable. */
212 void
focus_off_window(Lisp_Window * w)213 focus_off_window (Lisp_Window *w)
214 {
215     if (w == focus_window)
216     {
217 	focus_window = 0;
218 
219 	/* Do this immediately. Any real focus-change will be queued,
220 	   so will happen after this. Doing this here just prevents us
221 	   getting stuck with focus on nothing in some cases.. */
222 
223 	XSetInputFocus (dpy, no_focus_window, RevertToNone, last_event_time);
224     }
225 }
226 
227 /* Set flags in W relating to which window manager protocols are recognised
228    by window W. */
229 void
get_window_protocols(Lisp_Window * w)230 get_window_protocols (Lisp_Window *w)
231 {
232     Atom *prot;
233     int n;
234     w->does_wm_take_focus = 0;
235     w->does_wm_delete_window = 0;
236     if (XGetWMProtocols (dpy, w->id, &prot, &n) != 0)
237     {
238 	int i;
239 	for (i = 0; i < n; i++)
240 	{
241 	    if (prot[i] == xa_wm_take_focus)
242 	    {
243 		w->does_wm_take_focus = 1;
244 		DB(("  WM_TAKE_FOCUS is set\n"));
245 	    }
246 	    if (prot[i] == xa_wm_delete_window)
247 	    {
248 		w->does_wm_delete_window = 1;
249 		DB(("  WM_DELETE_WINDOW is set\n"));
250 	    }
251 	}
252 	XFree (prot);
253     }
254 }
255 
256 /* These two functions are used to bracket Xlib requests that would map,
257    unmap, or reparent the client window. They ensure that any
258    StructureNotifymask events generated between calling before_local_map ()
259    and after_local_map () are discarded, but no others (so that we don't
260    lose client-generated events) */
261 
262 void
before_local_map(Lisp_Window * w)263 before_local_map (Lisp_Window *w)
264 {
265     Fgrab_server ();
266     XSelectInput (dpy, w->id, CLIENT_EVENTS & ~StructureNotifyMask);
267 }
268 
269 void
after_local_map(Lisp_Window * w)270 after_local_map (Lisp_Window *w)
271 {
272     XSelectInput (dpy, w->id, CLIENT_EVENTS);
273     Fungrab_server ();
274 }
275 
276 /* manipulating the Lisp window structures */
277 
278 /* Return the window object containing ID, or a null pointer */
279 Lisp_Window *
find_window_by_id(Window id)280 find_window_by_id (Window id)
281 {
282     Lisp_Window *w;
283     w = window_list;
284     while (w != 0 && w->id != id && w->frame != id)
285 	w = w->next;
286     if (w != 0 && WINDOW_IS_GONE_P (w))
287 	w = 0;
288     return w;
289 }
290 
291 /* This is different to the above in that it could return a window
292    that doesn't have a client window. */
293 Lisp_Window *
x_find_window_by_id(Window id)294 x_find_window_by_id (Window id)
295 {
296     Lisp_Window *w;
297     w = window_list;
298     while (w != 0 && w->saved_id != id && w->frame != id)
299 	w = w->next;
300     return w;
301 }
302 
303 void
install_window_frame(Lisp_Window * w)304 install_window_frame (Lisp_Window *w)
305 {
306     DB(("install_window_frame (%s)\n", rep_STR(w->name)));
307     if (!w->reparented && w->frame != 0 && !WINDOW_IS_GONE_P (w))
308     {
309 	XSetWindowAttributes wa;
310 
311 	XSelectInput (dpy, w->frame, FRAME_EVENTS);
312 
313         XAddToSaveSet (dpy, w->id);
314 	before_local_map (w);
315 	XReparentWindow (dpy, w->id, w->frame, -w->frame_x, -w->frame_y);
316 	w->reparented = TRUE;
317 	after_local_map (w);
318 	restack_window (w);
319 
320 	if (queued_focus_id == w->id)
321 	    queued_focus_id = w->frame;
322 
323 	restack_frame_parts (w);
324 	reset_frame_parts (w);
325 
326 	wa.win_gravity = StaticGravity;
327 	XChangeWindowAttributes (dpy, w->id, CWWinGravity, &wa);
328 
329 	DB(("  reparented to %lx [%dx%d%+d%+d]\n",
330 	    w->frame, w->frame_width, w->frame_height,
331 	    w->frame_x, w->frame_y));
332     }
333 }
334 
335 void
remove_window_frame(Lisp_Window * w)336 remove_window_frame (Lisp_Window *w)
337 {
338     DB(("remove_window_frame (%s)\n", rep_STR(w->name)));
339     if (w->reparented && !WINDOW_IS_GONE_P (w))
340     {
341 	XSetWindowAttributes wa;
342 
343 	/* reparent the subwindow back to the root window */
344 
345 	wa.win_gravity = w->attr.win_gravity;
346 	XChangeWindowAttributes (dpy, w->id, CWWinGravity, &wa);
347 
348 	before_local_map (w);
349 	XReparentWindow (dpy, w->id, root_window, w->attr.x, w->attr.y);
350 	w->reparented = FALSE;
351 	after_local_map (w);
352 	restack_window (w);
353 
354 	if (queued_focus_id == w->frame)
355 	    queued_focus_id = w->id;
356 
357 	if (!w->mapped)
358 	    XRemoveFromSaveSet (dpy, w->id);
359     }
360 }
361 
362 
363 static repv
text_prop_to_utf8(XTextProperty * prop)364 text_prop_to_utf8 (XTextProperty *prop)
365 {
366     repv rval = Qnil;
367     if (prop->value && prop->nitems > 0)
368     {
369         char **list;
370         int count;
371         prop->nitems = strlen((char *) prop->value);
372 #ifdef X_HAVE_UTF8_STRING
373         if (Xutf8TextPropertyToTextList (dpy, prop, &list, &count) >= Success)
374         {
375             if (count > 0)
376                 rval = rep_string_dup (list[0]);
377             XFreeStringList (list);
378         }
379 #else
380         if (XmbTextPropertyToTextList (dpy, prop, &list, &count) >= Success)
381         {
382             if (count > 0) {
383                 gchar *ustr = g_locale_to_utf8(list[0], -1, NULL, NULL, NULL);
384                 if (ustr)
385                 {
386                     rval = rep_string_dup (ustr);
387                     g_free (ustr);
388                 }
389             }
390             XFreeStringList (list);
391         }
392 #endif
393     }
394     return rval;
395 }
396 
397 
398 /* Queries X properties to get the window {icon,}name */
399 static void
get_window_name(Lisp_Window * w)400 get_window_name(Lisp_Window *w)
401 {
402     XTextProperty prop;
403 
404     /* We only try to use the utf8 properties if our xlib supports them.
405        Otherwise conversion would have to go via the current locale, which
406        might lose some characters. */
407 #ifdef X_HAVE_UTF8_STRING
408     if (XGetTextProperty (dpy, w->id, &prop, xa_wm_net_name))
409         w->net_name = text_prop_to_utf8 (&prop);
410     if (XGetTextProperty (dpy, w->id, &prop, xa_wm_net_icon_name))
411         w->net_icon_name = text_prop_to_utf8 (&prop);
412 #endif
413 
414     if (w->net_name == Qnil && XGetWMName (dpy, w->id, &prop))
415     {
416         repv name = text_prop_to_utf8 (&prop);
417         if (name != Qnil)
418             w->name = name;
419     }
420     w->full_name = w->name;
421 
422     if (w->net_icon_name == Qnil && XGetWMIconName (dpy, w->id, &prop))
423         w->icon_name = text_prop_to_utf8 (&prop);
424     if (w->icon_name == Qnil)
425         w->icon_name = w->name;
426 }
427 
428 
429 /* Add the top-level window ID to the manager's data structures.
430  * This is called only from events.c -> map_request */
431 Lisp_Window *
add_window(Window id)432 add_window (Window id)
433 {
434     Lisp_Window *w = rep_ALLOC_CELL(sizeof (Lisp_Window));
435     if (w != 0)
436     {
437 	rep_GC_root gc_win;
438 	repv win = rep_VAL(w);
439 	XWindowChanges xwc;
440 	unsigned int xwcm;
441 	long supplied;
442 
443 	DB(("add_window (%lx)\n", id));
444 
445 	if (id == root_window)
446 	    DB(("  ** adding root window!?\n"));
447 
448 	rep_data_after_gc += sizeof (Lisp_Window);
449 	memset (w, 0, sizeof (Lisp_Window));
450 
451 	/* First initialise the Lisp stuff.. */
452 	w->next = window_list;
453 	window_list = w;
454 	w->car = window_type;
455 	w->id = id;
456 	w->saved_id = id;
457 	w->plist = Qnil;
458 	w->frame_style = Qnil;;
459 	w->icon_image = rep_NULL;
460 	w->name = rep_null_string ();
461 	w->net_name = Qnil;
462 	w->net_icon_name = Qnil;
463 	w->border_pixel = BlackPixel (dpy, screen_num);
464 
465         /* Don't garbage collect the window before we are done. */
466         /* Note: must not return without rep_POPGC. */
467 	rep_PUSHGC(gc_win, win);
468 
469 	/* have to put it somewhere until it finds the right place */
470 	insert_in_stacking_list_above_all (w);
471 	restack_window (w);
472 
473 	/* ..now do the X11 stuff */
474 
475 	XSelectInput (dpy, id, CLIENT_EVENTS);
476 	XGetWindowAttributes (dpy, id, &w->attr);
477 	DB(("  orig: width=%d height=%d x=%d y=%d\n",
478 	    w->attr.width, w->attr.height, w->attr.x, w->attr.y));
479 	w->old_border_width = w->attr.border_width;
480 
481 	get_window_name(w);
482 
483 	w->wmhints = XGetWMHints (dpy, id);
484 	if (!XGetWMNormalHints (dpy, w->id, &w->hints, &supplied))
485 	    w->hints.flags = 0;
486 	get_window_protocols (w);
487 	if (!XGetWMColormapWindows (dpy, w->id,
488 				    &w->cmap_windows, &w->n_cmap_windows))
489 	{
490 	    w->n_cmap_windows = 0;
491 	}
492 
493 	{
494 	    /* Is the window shaped? */
495 	    int xws, yws, xbs, ybs;
496 	    unsigned int wws, hws, wbs, hbs;
497 	    int bounding, clip;
498 	    XShapeSelectInput (dpy, w->id, ShapeNotifyMask);
499 	    XShapeQueryExtents (dpy, w->id, &bounding, &xws, &yws, &wws, &hws,
500 				&clip, &xbs, &ybs, &wbs, &hbs);
501 	    w->shaped = bounding ? 1 : 0;
502 	}
503 
504 	DB(("  name=`%s' x=%d y=%d width=%d height=%d\n",
505 	    rep_STR(w->name), w->attr.x, w->attr.y,
506 	    w->attr.width, w->attr.height));
507 
508 	xwcm = CWX | CWX | CWWidth | CWHeight | CWBorderWidth;
509 	xwc.x = w->attr.x;
510 	xwc.y = w->attr.y;
511 	xwc.width = w->attr.width;
512 	xwc.height = w->attr.height;
513 	xwc.border_width = 0;
514 	XConfigureWindow (dpy, id, xwcm, &xwc);
515 
516         w->visible = TRUE;
517 	w->mapped = TRUE;		/* only called from map request */
518 
519 	if (initialising)
520 	    Fwindow_put (rep_VAL (w), Qplaced, Qt);
521 
522 	/* If the window requires to start as icon, then iconify it.
523 	 * It is better to be done before 'before_add_window_hook', where
524 	 * matching takes place, because in future, matcher can have an
525 	 * option to un-iconify, overrinding the application's request
526 	 * to iconify.
527 	 */
528 	if (w->wmhints && w->wmhints->flags & StateHint
529 	    && w->wmhints->initial_state == IconicState)
530 	  {
531 	    DEFSTRING (iconify_mod, "sawfish.wm.state.iconify");
532 	    rep_call_lisp1 (module_symbol_value
533 			    (rep_VAL (&iconify_mod), Qiconify_window),
534 			    rep_VAL(w));
535 	  }
536 
537 	/* Prevent hook call on non existing window */
538 	if (WINDOW_IS_GONE_P (w))
539 	{
540 		rep_POPGC;
541 		return 0;
542 	}
543 
544 	/* ..then call the add-window-hook's.. */
545 	Fcall_window_hook (Qbefore_add_window_hook, rep_VAL(w), Qnil, Qnil);
546 	Fcall_window_hook (Qadd_window_hook, rep_VAL(w), Qnil, Qnil);
547 
548 	/* In case the window disappeared during the hook call */
549 	if (!WINDOW_IS_GONE_P (w))
550 	{
551 	    Fgrab_server ();
552 
553 	    /* this is where we create and reparent the window frame */
554 	    create_window_frame (w);
555 	    install_window_frame (w);
556 
557 	    /* this grabs bound events in the subwindow */
558 	    grab_window_events (w, TRUE);
559 
560 	    Fungrab_server ();
561 	}
562 	else
563 	    emit_pending_destroys ();
564 
565 	if (!WINDOW_IS_GONE_P (w))
566 	{
567             repv tem = Fwindow_get (rep_VAL(w), Qplaced, Qnil);
568 	    if (initialising || (tem && tem == Qnil))
569 	    {
570 		/* ..then the place-window-hook.. */
571 		Fcall_window_hook (Qplace_window_hook, rep_VAL(w), Qnil, Qor);
572 	    }
573 	}
574 	Fwindow_put (rep_VAL(w), Qplaced, Qt);
575 
576 	if (!WINDOW_IS_GONE_P (w))
577 	    Fcall_window_hook (Qafter_add_window_hook, rep_VAL(w), Qnil, Qnil);
578 
579 	if (!WINDOW_IS_GONE_P (w))
580 	{
581 	    /* Tell the window where it ended up.. */
582 	    send_synthetic_configure (w);
583 	}
584         rep_POPGC;
585     }
586     return w;
587 }
588 
589 /* Remove W from the managed windows. If DESTROYED is nil and
590    the window is currently reparented by us, it will be reparented back to
591    the root window */
592 void
remove_window(Lisp_Window * w,bool destroyed,bool from_error)593 remove_window (Lisp_Window *w, bool destroyed, bool from_error)
594 {
595     DB(("remove_window (%s, %s)\n",
596 	rep_STR(w->name), destroyed ? "destroyed" : "not-destroyed"));
597 
598     if (w->id != 0)
599     {
600 	if (!destroyed && !from_error)
601 	{
602 	    grab_window_events (w, FALSE);
603 	    remove_window_frame (w);
604 
605 	    /* Restore original border width of the client */
606 	    XSetWindowBorderWidth (dpy, w->id, w->old_border_width);
607 	}
608 
609 	if (!from_error)
610 	    destroy_window_frame (w, FALSE);
611 
612 	if (!WINDOW_IS_GONE_P (w))
613 	    remove_from_stacking_list (w);
614 
615 	if (!from_error)
616 	    focus_off_window (w);
617 
618 	w->id = 0;
619 	pending_destroys++;
620 
621 	/* gc will do the rest... */
622     }
623     else if (w->frame != 0 && !from_error)
624 	destroy_window_frame (w, FALSE);
625 }
626 
627 void
fix_window_size(Lisp_Window * w)628 fix_window_size (Lisp_Window *w)
629 {
630     Fgrab_server ();
631     if (w->frame != 0 && w->rebuild_frame != 0)
632 	w->rebuild_frame (w);
633     else
634 	XResizeWindow (dpy, w->id, w->attr.width, w->attr.height);
635     Fungrab_server ();
636 }
637 
638 /* Call destroy-notify-hook on any newly-dead windows */
639 void
emit_pending_destroys(void)640 emit_pending_destroys (void)
641 {
642     if (pending_destroys > 0)
643     {
644 	Lisp_Window *w;
645     again:
646 	for (w = window_list; w != 0 && !rep_INTERRUPTP; w = w->next)
647 	{
648 	    if (WINDOW_IS_GONE_P (w) && !w->destroyed)
649 	    {
650 		w->destroyed = 1;
651 		Fcall_window_hook (Qdestroy_notify_hook,
652 				   rep_VAL(w), Qnil, Qnil);
653 
654 		focus_off_window (w);
655 
656 		/* gc may have reordered the list, so we have to start
657 		   at the beginning again.. */
658 		goto again;
659 	    }
660 	}
661     }
662     pending_destroys = 0;
663 }
664 
665 /* Lisp functions */
666 
667 DEFUN("window-get", Fwindow_get, Swindow_get,
668       (repv win, repv prop, repv checker), rep_Subr3) /*
669 ::doc:sawfish.wm.windows.subrs#window-get::
670 window-get WINDOW PROPERTY &optional CHECKER
671 
672 Return the value of the property named PROPERTY (a symbol) of WINDOW.
673 
674 Note that these are Lisp properties not X properties.
675 
676 If the optional argument CHECKER is nil, then the return value is
677 nil, either when the property value is nil, or the property is absent.
678 
679 If CHECKER is non-nil, than it returns CHECKER if the property
680 is unset.
681 ::end:: */
682 {
683     repv plist;
684     rep_DECLARE1(win, XWINDOWP);
685     plist = VWIN(win)->plist;
686     while (rep_CONSP(plist) && rep_CONSP(rep_CDR(plist)))
687     {
688 	if (rep_CAR(plist) == prop
689 	    || (!rep_SYMBOLP(prop)
690 		&& rep_value_cmp (rep_CAR(plist), prop) == 0))
691 	{
692 	    return rep_CAR(rep_CDR(plist));
693 	}
694 	plist = rep_CDR(rep_CDR(plist));
695     }
696     return checker;
697 }
698 
699 DEFUN("map-window-properties", Fmap_window_properties,
700       Smap_window_properties, (repv fun, repv win), rep_Subr2) /*
701 ::doc:sawfish.wm.windows.subrs#map-window-properties::
702 map-window-properties FUNCTION WINDOW
703 
704 Call (FUNCTION PROPERTY VALUE) for all Lisp properties set on window
705 object WINDOW.
706 ::end:: */
707 {
708     repv ret = Qnil, plist;
709     rep_GC_root gc_plist, gc_fun;
710     rep_DECLARE2 (win, XWINDOWP);
711     plist = VWIN(win)->plist;
712     rep_PUSHGC (gc_plist, plist);
713     rep_PUSHGC (gc_fun, fun);
714     while (rep_CONSP(plist) && rep_CONSP(rep_CDR(plist)))
715     {
716 	ret = rep_call_lisp2 (fun, rep_CAR (plist), rep_CADR (plist));
717 	if (ret == rep_NULL)
718 	    break;
719 	plist = rep_CDDR (plist);
720     }
721     rep_POPGC; rep_POPGC;
722     return ret;
723 }
724 
725 void
register_property_monitor(repv prop,void (* callback)(Lisp_Window *,repv,repv,repv))726 register_property_monitor (repv prop,
727 			   void (*callback) (Lisp_Window *, repv, repv, repv))
728 {
729     struct prop_handler *ph = rep_alloc (sizeof (struct prop_handler));
730     ph->next = prop_handlers;
731     prop_handlers = ph;
732     ph->prop = prop;
733     ph->callback = callback;
734 }
735 
736 DEFUN("window-put", Fwindow_put, Swindow_put,
737       (repv win, repv prop, repv val), rep_Subr3) /*
738 ::doc:sawfish.wm.windows.subrs#window-put::
739 window-put WINDOW PROPERTY VALUE
740 
741 Set the value of the property named PROPERTY (a symbol) of WINDOW to VALUE.
742 
743 Note that these are Lisp properties not X properties.
744 ::end:: */
745 {
746     repv plist;
747     rep_DECLARE1(win, XWINDOWP);
748     plist = VWIN(win)->plist;
749     while (rep_CONSP(plist) && rep_CONSP(rep_CDR(plist)))
750     {
751 	if (rep_CAR(plist) == prop
752 	    || (!rep_SYMBOLP(prop)
753 		&& rep_value_cmp (rep_CAR(plist), prop) == 0))
754 	{
755 	    struct prop_handler *ph;
756 	    for (ph = prop_handlers; ph != 0; ph = ph->next)
757 	    {
758 		repv old = rep_CADR (plist);
759 		if (ph->prop == prop && old != val)
760 		    ph->callback (VWIN (win), prop, old, val);
761 	    }
762 	    rep_CADR(plist) = val;
763 	    return val;
764 	}
765 	plist = rep_CDDR(plist);
766     }
767     plist = Fcons(prop, Fcons(val, VWIN(win)->plist));
768     if (plist != rep_NULL)
769 	VWIN(win)->plist = plist;
770     return val;
771 }
772 
773 DEFUN("window-remprop", Fwindow_remprop, Swindow_remprop,
774       (repv win, repv prop), rep_Subr2) /*
775 ::doc:sawfish.wm.windows.subrs#window-prop-del::
776 window-put WINDOW PROPERTY
777 
778 Delete PROPERTY of WINDOW. Return t for success, nil if WINDOW
779 did not have PROPERTY.
780 ::end:: */
781 {
782     repv *pplist;
783     rep_DECLARE1(win, XWINDOWP);
784     pplist = &VWIN(win)->plist;
785     while (rep_CONSP(*pplist) && rep_CONSP(rep_CDR(*pplist)))
786     {
787 	if (rep_CAR(*pplist) == prop
788 	    || (!rep_SYMBOLP(prop)
789 		&& rep_value_cmp (rep_CAR(*pplist), prop) == 0))
790 	{
791 	    struct prop_handler *ph;
792             repv old = rep_CADR(*pplist);
793             if (old != Qnil)
794                 for (ph = prop_handlers; ph != 0; ph = ph->next)
795                     if (ph->prop == prop)
796                         ph->callback(VWIN (win), prop, old, Qnil);
797             *pplist = rep_CDDR(*pplist);
798 	    return Qt;
799 	}
800 	pplist = &rep_CDDR(*pplist);
801     }
802     return Qnil;
803 }
804 
805 DEFUN("window-plist", Fwindow_plist, Swindow_plist,
806       (repv win), rep_Subr1) /*
807 ::doc:sawfish.wm.windows.subrs#window-plist::
808 window-plist WINDOW
809 
810 Returns the property list of the window window which is of the form
811 (prop value prop value ...).
812 
813 Do not attempt to change properties by modifying the property list in place.
814 Use window-put instead.
815 ::end:: */
816 {
817     rep_DECLARE1(win, XWINDOWP);
818     return VWIN(win)->plist;
819 }
820 
821 DEFUN("window-name", Fwindow_name, Swindow_name, (repv win), rep_Subr1) /*
822 ::doc:sawfish.wm.windows.subrs#window-name::
823 window-name WINDOW
824 
825 Return the name of window object WINDOW.
826 ::end:: */
827 {
828     Lisp_Window * w;
829     rep_DECLARE1(win, WINDOWP);
830     w = VWIN(win);
831     return w->net_name != Qnil ? w->net_name : w->name;
832 }
833 
834 DEFUN("window-full-name", Fwindow_full_name, Swindow_full_name,
835       (repv win), rep_Subr1) /*
836 ::doc:sawfish.wm.windows.subrs#window-full-name::
837 window-full-name WINDOW
838 
839 Return the full name of window object WINDOW.
840 ::end:: */
841 {
842     rep_DECLARE1(win, WINDOWP);
843     return VWIN(win)->full_name;
844 }
845 
846 DEFUN("window-icon-name", Fwindow_icon_name, Swindow_icon_name,
847       (repv win), rep_Subr1) /*
848 ::doc:sawfish.wm.windows.subrs#window-icon-name::
849 window-icon-name WINDOW
850 
851 Return the name of window object WINDOW's icon.
852 ::end:: */
853 {
854     Lisp_Window * w;
855     rep_DECLARE1(win, WINDOWP);
856     w = VWIN(win);
857     return w->net_icon_name != Qnil ? w->net_icon_name : w->icon_name;
858 }
859 
860 DEFUN("window-mapped-p", Fwindow_mapped_p, Swindow_mapped_p,
861       (repv win), rep_Subr1) /*
862 ::doc:sawfish.wm.windows.subrs#window-mapped-p::
863 window-mapped-p WINDOW
864 
865 Return t if the client window associated with object WINDOW is mapped.
866 (This doesn't necessarily mean that it is visible.)
867 ::end:: */
868 {
869     rep_DECLARE1(win, WINDOWP);
870     return VWIN(win)->mapped ? Qt : Qnil;
871 }
872 
873 DEFUN("window-frame", Fwindow_frame, Swindow_frame, (repv win), rep_Subr1) /*
874 ::doc:sawfish.wm.windows.subrs#window-frame::
875 window-frame WINDOW
876 
877 Return the frame object associated with WINDOW.
878 ::end:: */
879 {
880     rep_DECLARE1(win, WINDOWP);
881     return VWIN(win)->frame_style;
882 }
883 
884 DEFUN("set-window-frame", Fset_window_frame, Sset_window_frame,
885       (repv win, repv frame), rep_Subr2) /*
886 ::doc:sawfish.wm.windows.subrs#set-window-frame::
887 set-window-frame WINDOW FRAME
888 
889 Set the frame associated with the window object WINDOW to FRAME (a
890 list). If the window is mapped the old frame will be destroyed and a
891 new frame constructed as specified by FRAME.
892 ::end:: */
893 {
894     rep_DECLARE1(win, WINDOWP);
895     rep_DECLARE2(frame, rep_LISTP);
896     Fgrab_server ();
897 
898     if (VWIN(win)->reparented)
899 	destroy_window_frame (VWIN(win), TRUE);
900 
901     VWIN(win)->frame_style = frame;
902 
903     if (VWIN(win)->reparented)
904 	create_window_frame (VWIN(win));
905 
906     Fungrab_server ();
907     Fcall_window_hook (Qafter_framing_hook, win, Qnil, Qnil);
908     return VWIN(win)->frame_style;
909 }
910 
911 DEFUN("rebuild-frame", Frebuild_frame, Srebuild_frame, (repv win), rep_Subr1) /*
912 ::doc:sawfish.wm.windows.subrs#rebuild-frame::
913 rebuild-frame WINDOW
914 
915 Reinitialises and recalibrates the window frame of WINDOW.
916 ::end:: */
917 {
918     rep_DECLARE1(win, WINDOWP);
919     if (VWIN(win)->frame != 0 && VWIN(win)->rebuild_frame != 0)
920     {
921 	VWIN(win)->rebuild_frame (VWIN(win));
922 	refresh_frame_parts (VWIN(win));
923 	Fcall_window_hook (Qafter_framing_hook, win, Qnil, Qnil);
924     }
925     return win;
926 }
927 
928 DEFUN("window-position", Fwindow_position, Swindow_position,
929       (repv win), rep_Subr1) /*
930 ::doc:sawfish.wm.windows.subrs#window-position::
931 window-position WINDOW
932 
933 Return (X . Y) defining the current position of WINDOW.
934 ::end:: */
935 {
936     rep_DECLARE1(win, WINDOWP);
937     return Fcons (rep_MAKE_INT(VWIN(win)->attr.x),
938 		  rep_MAKE_INT(VWIN(win)->attr.y));
939 }
940 
941 DEFUN("window-dimensions", Fwindow_dimensions, Swindow_dimensions,
942       (repv win), rep_Subr1) /*
943 ::doc:sawfish.wm.windows.subrs#window-dimensions::
944 window-dimensions WINDOW
945 
946 Return (WIDTH . HEIGHT) defining the current dimensions of the client
947 window associated with WINDOW.
948 ::end:: */
949 {
950     rep_DECLARE1(win, WINDOWP);
951     return Fcons (rep_MAKE_INT(VWIN(win)->attr.width),
952 		  rep_MAKE_INT(VWIN(win)->attr.height));
953 }
954 
955 DEFUN("window-frame-dimensions", Fwindow_frame_dimensions,
956       Swindow_frame_dimensions, (repv win), rep_Subr1) /*
957 ::doc:sawfish.wm.windows.subrs#window-frame-dimensions::
958 window-frame-dimensions WINDOW
959 
960 Return (WIDTH . HEIGHT) defining the current dimensions of the frame
961 surrounding WINDOW.
962 ::end:: */
963 {
964     rep_DECLARE1(win, WINDOWP);
965     if (VWIN(win)->reparented)
966     {
967 	return Fcons (rep_MAKE_INT(VWIN(win)->frame_width + 2*VWIN(win)->border_width),
968 		      rep_MAKE_INT(VWIN(win)->frame_height + 2*VWIN(win)->border_width));
969     }
970     else
971 	return Fwindow_dimensions (win);
972 }
973 
974 DEFUN("window-frame-offset", Fwindow_frame_offset,
975       Swindow_frame_offset, (repv win), rep_Subr1) /*
976 ::doc:sawfish.wm.windows.subrs#window-frame-offset::
977 window-frame-offset WINDOW
978 
979 Return (X . Y) defining the offset from the origin of the client window
980 associated with WINDOW to its frame window.
981 ::end:: */
982 {
983     rep_DECLARE1(win, WINDOWP);
984     return Fcons (rep_MAKE_INT(VWIN(win)->frame_x),
985 		  rep_MAKE_INT(VWIN(win)->frame_y));
986 }
987 
988 DEFUN("windowp", Fwindowp, Swindowp, (repv win), rep_Subr1) /*
989 ::doc:sawfish.wm.windows.subrs#windowp::
990 windowp ARG
991 
992 Return t if ARG is a window object.
993 ::end:: */
994 {
995     return WINDOWP(win) ? Qt : Qnil;
996 }
997 
998 DEFUN("set-input-focus", Fset_input_focus, Sset_input_focus,
999       (repv win), rep_Subr1) /*
1000 ::doc:sawfish.wm.windows.subrs#set-input-focus::
1001 set-input-focus WINDOW
1002 
1003 Set the input focus to WINDOW. If WINDOW is nil, then no window will
1004 have the focus.
1005 ::end:: */
1006 {
1007     if (win != Qnil && win != Qroot)
1008     {
1009 	rep_DECLARE1(win, WINDOWP);
1010 	focus_on_window (VWIN(win));
1011     }
1012     else
1013 	focus_on_window (0);
1014     return win;
1015 }
1016 
1017 DEFUN("input-focus", Finput_focus, Sinput_focus, (void), rep_Subr0) /*
1018 ::doc:sawfish.wm.windows.subrs#input-focus::
1019 input-focus
1020 
1021 Return the window object that has the input focus, or nil if none does.
1022 ::end:: */
1023 {
1024     return (focus_window == 0) ? Qnil : rep_VAL(focus_window);
1025 }
1026 
1027 DEFUN("window-wants-input-p", Fwindow_wants_input_p, Swindow_wants_input_p,
1028       (repv win), rep_Subr1) /*
1029 ::doc:sawfish.wm.windows.subrs#window-wants-input-p::
1030 window-wants-input-p WINDOW
1031 
1032 Return t if the WINDOW wants focus in X sense, i.e. if it has hinted
1033 that it would like to be given the input focus when applicable.
1034 
1035 If unsure, use `window-really-wants-input-p' which also takes into
1036 account `never-focus' window property.
1037 ::end:: */
1038 {
1039     rep_DECLARE1(win, WINDOWP);
1040     if (VWIN(win)->does_wm_take_focus)
1041 	return Qt;
1042     else
1043 	return window_input_hint_p (VWIN (win)) ? Qt : Qnil;
1044 }
1045 
1046 DEFUN("managed-windows", Fmanaged_windows, Smanaged_windows,
1047       (void), rep_Subr0) /*
1048 ::doc:sawfish.wm.windows.subrs#managed-windows::
1049 managed-windows
1050 
1051 Return a list of all known client window objects.
1052 ::end:: */
1053 {
1054     repv list = Qnil;
1055     Lisp_Window *w = window_list;
1056     while (w != 0)
1057     {
1058 	if (!WINDOW_IS_GONE_P (w))
1059 	    list = Fcons (rep_VAL(w), list);
1060 	w = w->next;
1061     }
1062     return list;
1063 }
1064 
1065 DEFUN("get-window-by-id", Fget_window_by_id, Sget_window_by_id,
1066       (repv id), rep_Subr1) /*
1067 ::doc:sawfish.wm.windows.subrs#get-window-by-id::
1068 get-window-by-id ID
1069 
1070 Return the window object associated with xid ID, or nil.
1071 ::end:: */
1072 {
1073     Lisp_Window *w;
1074     rep_DECLARE1(id, rep_INTEGERP);
1075     w = find_window_by_id (rep_get_long_uint (id));
1076     return w ? rep_VAL(w) : Qnil;
1077 }
1078 
1079 DEFUN("stacking-order", Fstacking_order, Sstacking_order, (void), rep_Subr0) /*
1080 ::doc:sawfish.wm.windows.subrs#stacking-order::
1081 stacking-order
1082 
1083 Return a list of windows defining the current stacking order of all
1084 client windows.
1085 ::end:: */
1086 {
1087     return make_stacking_list ();
1088 }
1089 
1090 DEFUN("window-visibility", Fwindow_visibility, Swindow_visibility,
1091       (repv win), rep_Subr1) /*
1092 ::doc:sawfish.wm.windows.subrs#window-visibility::
1093 window-visibility WINDOW
1094 
1095 Return a symbol defining the visibility of WINDOW. Possible returned
1096 symbols are `fully-obscured', `partially-obscured' or `unobscured'.
1097 
1098 This function is deprecated. Instead use `window-obscured' and
1099 `stacking-visibility'.
1100 ::end:: */
1101 {
1102     repv sym = Qnil;
1103     rep_DECLARE1(win, WINDOWP);
1104     switch (VWIN(win)->frame_vis)
1105     {
1106     case VisibilityFullyObscured:
1107 	sym = Qfully_obscured;
1108 	break;
1109 
1110     case VisibilityPartiallyObscured:
1111 	sym = Qpartially_obscured;
1112 	break;
1113 
1114     case VisibilityUnobscured:
1115 	sym = Qunobscured;
1116 	break;
1117     }
1118     return sym;
1119 }
1120 
1121 DEFUN("window-urgent-p", Fwindow_urgent_p, Swindow_urgent_p,
1122       (repv win), rep_Subr1) /*
1123 ::doc:sawfish.wm.windows.subrs#window-urgent-p::
1124 window-urgent-p WINDOW
1125 
1126 Return true if the `Urgency' hint of the window associated with WINDOW
1127 is set.
1128 ::end:: */
1129 {
1130     rep_DECLARE1 (win, WINDOWP);
1131     return ((VWIN (win)->wmhints
1132 	     && VWIN (win)->wmhints->flags & XUrgencyHint) ? Qt : Qnil);
1133 }
1134 
1135 DEFUN("window-shaped-p", Fwindow_shaped_p, Swindow_shaped_p,
1136       (repv win), rep_Subr1) /*
1137 ::doc:sawfish.wm.windows.subrs#window-shaped-p::
1138 window-shaped-p WINDOW
1139 
1140 Return non-nil if WINDOW is shaped.
1141 ::end:: */
1142 {
1143     rep_DECLARE1(win, WINDOWP);
1144     return VWIN(win)->shaped ? Qt : Qnil;
1145 }
1146 
1147 DEFUN("hide-window", Fhide_window, Shide_window, (repv win), rep_Subr1) /*
1148 ::doc:sawfish.wm.windows.subrs#hide-window::
1149 hide-window WINDOW
1150 
1151 Prevent WINDOW from being displayed. See `show-window'.
1152 ::end:: */
1153 {
1154     rep_DECLARE1(win, WINDOWP);
1155     if (VWIN(win)->visible)
1156     {
1157 	if (VWIN(win)->mapped)
1158 	{
1159 	    if (VWIN(win)->frame)
1160 		XUnmapWindow (dpy, VWIN(win)->frame);
1161 	    if (!VWIN(win)->client_unmapped)
1162 	    {
1163 		before_local_map (VWIN(win));
1164 		XUnmapWindow (dpy, VWIN(win)->id);
1165 		VWIN(win)->client_unmapped = 1;
1166 		after_local_map (VWIN(win));
1167 	    }
1168 	}
1169 	VWIN(win)->visible = 0;
1170 	reset_frame_parts (VWIN(win));
1171     }
1172     return win;
1173 }
1174 
1175 DEFUN("show-window", Fshow_window, Sshow_window, (repv win), rep_Subr1) /*
1176 ::doc:sawfish.wm.windows.subrs#show-window::
1177 show-window WINDOW
1178 
1179 Ensure that WINDOW (if it has been mapped) is visible. See `hide-window'.
1180 ::end:: */
1181 {
1182     rep_DECLARE1(win, WINDOWP);
1183     if (!VWIN(win)->visible)
1184     {
1185 	if (VWIN(win)->mapped)
1186 	{
1187 	    if (VWIN(win)->client_unmapped && !VWIN(win)->client_hidden)
1188 	    {
1189 		before_local_map (VWIN(win));
1190 		XMapWindow (dpy, VWIN(win)->id);
1191 		VWIN(win)->client_unmapped = 0;
1192 		after_local_map (VWIN(win));
1193 	    }
1194 	    if (VWIN(win)->frame)
1195 		XMapWindow (dpy, VWIN(win)->frame);
1196 	}
1197 	VWIN(win)->visible = 1;
1198     }
1199     return win;
1200 }
1201 
1202 DEFUN("window-visible-p", Fwindow_visible_p, Swindow_visible_p,
1203       (repv win), rep_Subr1) /*
1204 ::doc:sawfish.wm.windows.subrs#window-visible-p::
1205 window-visible-p WINDOW
1206 
1207 Return t if WINDOW is not hidden by `hide-window'. Notice there're
1208 various cases where a window is not visible.
1209 ::end:: */
1210 {
1211     rep_DECLARE1(win, WINDOWP);
1212     return VWIN(win)->visible ? Qt : Qnil;
1213 }
1214 
1215 DEFUN("window-framed-p", Fwindow_framed_p, Swindow_framed_p,
1216       (repv win), rep_Subr1) /*
1217 ::doc:sawfish.wm.windows.subrs#window-framed-p::
1218 window-framed-p WINDOW
1219 
1220 Return t if WINDOW has been reparented to a frame window.
1221 ::end:: */
1222 {
1223     rep_DECLARE1(win, WINDOWP);
1224     return VWIN(win)->reparented ? Qt : Qnil;
1225 }
1226 
1227 DEFUN("window-frame-id", Fwindow_frame_id, Swindow_frame_id,
1228       (repv win), rep_Subr1) /*
1229 ::doc:sawfish.wm.windows.subrs#window-frame-id::
1230 window-frame-id WINDOW
1231 
1232 Return the numeric id of the framing window associated with object
1233 WINDOW. Returns nil if the client window has no frame.
1234 ::end:: */
1235 {
1236     rep_DECLARE1(win, WINDOWP);
1237     return VWIN(win)->frame ? rep_MAKE_INT (VWIN(win)->frame) : Qnil;
1238 }
1239 
1240 DEFUN("window-id", Fwindow_id, Swindow_id, (repv win), rep_Subr1) /*
1241 ::doc:sawfish.wm.windows.subrs#window-id::
1242 window-id WINDOW
1243 
1244 Return the numeric id of the client window associated with object
1245 WINDOW. Returns nil if the client window has been deleted.
1246 ::end:: */
1247 {
1248     rep_DECLARE1(win, WINDOWP);
1249     return VWIN(win)->id ? rep_MAKE_INT (VWIN(win)->id) : Qnil;
1250 }
1251 
1252 DEFUN("window-group-id", Fwindow_group_id, Swindow_group_id,
1253       (repv win), rep_Subr1) /*
1254 ::doc:sawfish.wm.windows.subrs#window-group-id::
1255 window-group-id WINDOW
1256 
1257 Return the numeric id defining the leader of the group that WINDOW is a
1258 member of, or nil if it is not a member of a group.
1259 ::end:: */
1260 {
1261     rep_DECLARE1(win, WINDOWP);
1262     if (VWIN(win)->wmhints == 0)
1263 	return Qnil;
1264     return ((VWIN(win)->wmhints->flags & WindowGroupHint)
1265 	    ? rep_MAKE_INT (VWIN(win)->wmhints->window_group)
1266 	    : Qnil);
1267 }
1268 
1269 DEFUN("window-border-width", Fwindow_border_width, Swindow_border_width,
1270       (repv win), rep_Subr1) /*
1271 ::doc:sawfish.wm.windows.subrs#window-border-width::
1272 window-border-width WINDOW
1273 ::end:: */
1274 {
1275     rep_DECLARE1(win, WINDOWP);
1276     return rep_MAKE_INT(VWIN(win)->border_width);
1277 }
1278 
1279 DEFUN("window-size-hints", Fwindow_size_hints, Swindow_size_hints,
1280       (repv win), rep_Subr1) /*
1281 ::doc:sawfish.wm.windows.subrs#window-size-hints::
1282 window-size-hints WINDOW
1283 
1284 Return an alist defining the size-hints specified by the client window
1285 associated with WINDOW. Possible keys in the alist are `min-height',
1286 `max-height', `min-width', `max-width', `height-inc', `width-inc',
1287 `min-aspect', `max-aspect', `base-height', `base-width',
1288 `user-position', `program-position', `user-size', `program-size',
1289 `window-gravity', `border-size'.
1290 ::end:: */
1291 {
1292     repv ret = Qnil;
1293     XSizeHints *hints;
1294     long flags;
1295     rep_DECLARE1(win, WINDOWP);
1296 
1297     hints = &VWIN(win)->hints;
1298     flags = hints->flags;
1299 
1300     /* workaround stuff like Firefox 17 that
1301      * has enormous max-width/maxh-height */
1302     if (hints->max_width >= 32767)
1303 	    hints->max_width = 32767;
1304     if (hints->max_height >= 32767)
1305 	    hints->max_height = 32767;
1306 
1307     /* Some sanity checking */
1308     if ((flags & PMinSize)
1309 	&& (hints->min_width < 0 || hints->min_height < 0))
1310 	flags &= ~PMinSize;
1311     if ((flags & PMaxSize)
1312 	&& (hints->max_width <= 0 || hints->max_height <= 0))
1313 	flags &= ~PMaxSize;
1314     if ((flags & PResizeInc)
1315 	&& (hints->width_inc <= 0 || hints->width_inc <= 0))
1316 	flags &= ~PResizeInc;
1317     if ((flags & PBaseSize)
1318 	&& (hints->base_width <= 0 || hints->base_height <= 0))
1319 	flags &= ~PBaseSize;
1320 
1321     if (flags & PMinSize)
1322     {
1323 	ret = Fcons (Fcons (Qmin_width, rep_MAKE_INT(MAX(hints->min_width,1))),
1324 		     Fcons (Fcons (Qmin_height, rep_MAKE_INT(MAX(hints->min_height, 1))), ret));
1325     }
1326     if (flags & PMaxSize)
1327     {
1328 	ret = Fcons (Fcons (Qmax_width, rep_MAKE_INT(hints->max_width)),
1329 		     Fcons (Fcons (Qmax_height,
1330 				   rep_MAKE_INT(hints->max_height)), ret));
1331     }
1332     if (flags & PResizeInc)
1333     {
1334 	ret = Fcons (Fcons (Qwidth_inc, rep_MAKE_INT(hints->width_inc)),
1335 		     Fcons (Fcons (Qheight_inc,
1336 				   rep_MAKE_INT(hints->height_inc)), ret));
1337     }
1338     if (flags & PBaseSize)
1339     {
1340 	ret = Fcons (Fcons (Qbase_width, rep_MAKE_INT(hints->base_width)),
1341 		     Fcons (Fcons (Qbase_height,
1342 				   rep_MAKE_INT(hints->base_height)), ret));
1343     }
1344     if (flags & PAspect)
1345     {
1346 	ret = Fcons (Fcons (Qmin_aspect,
1347 			    Fcons (rep_MAKE_INT(hints->min_aspect.x),
1348 				   rep_MAKE_INT(hints->min_aspect.y))),
1349 		     Fcons (Fcons (Qmax_aspect,
1350 				   Fcons (rep_MAKE_INT(hints->max_aspect.x),
1351 					  rep_MAKE_INT(hints->max_aspect.y))),
1352 			    ret));
1353     }
1354     if (flags & USPosition)
1355 	ret = Fcons (Fcons (Quser_position, Qt), ret);
1356     else if (flags & PPosition)
1357 	ret = Fcons (Fcons (Qprogram_position, Qt), ret);
1358     if (flags & USSize)
1359 	ret = Fcons (Fcons (Quser_size, Qt), ret);
1360     else if (flags & PSize)
1361 	ret = Fcons (Fcons (Qprogram_size, Qt), ret);
1362     if ((flags & PWinGravity)
1363 	&& hints->win_gravity >= ForgetGravity
1364 	&& hints->win_gravity <= StaticGravity)
1365     {
1366 	ret = Fcons (Fcons (Qwindow_gravity,
1367 			    gravity_map[hints->win_gravity]), ret);
1368     }
1369     return ret;
1370 }
1371 
1372 DEFUN("call-window-hook", Fcall_window_hook, Scall_window_hook,
1373       (repv hook, repv win, repv args, repv type), rep_Subr4) /*
1374 ::doc:sawfish.wm.windows.subrs#call-window-hook::
1375 call-window-hook HOOK WINDOW &optional ARGS HOOK-TYPE
1376 
1377 Call HOOK for WINDOW with extra arguments ARGS. See `call-hook' for a
1378 description of HOOK-TYPE.
1379 ::end:: */
1380 {
1381     repv tem;
1382     rep_GC_root gc_hook, gc_args, gc_type;
1383     rep_DECLARE1(hook, rep_SYMBOLP);
1384     rep_DECLARE2(win, XWINDOWP);
1385     args = Fcons (win, args);
1386     rep_PUSHGC(gc_hook, hook);
1387     rep_PUSHGC(gc_args, args);
1388     rep_PUSHGC(gc_type, type);
1389     tem = Fwindow_get (win, hook, Qnil);
1390     if (tem && tem != Qnil)
1391     {
1392 	tem = Fcall_hook (tem, args, type);
1393 	if (!tem || (type == Qand && tem == Qnil)
1394 	    || (type == Qor && tem != Qnil))
1395 	{
1396 	    goto out;
1397 	}
1398     }
1399     tem = Fcall_hook (hook, args, type);
1400 out:
1401     rep_POPGC; rep_POPGC; rep_POPGC;
1402     return tem;
1403 }
1404 
1405 DEFUN("window-icon-image", Fwindow_icon_image,
1406       Swindow_icon_image, (repv args), rep_SubrN) /*
1407 ::doc:sawfish.wm.windows.subrs#window-icon-image::
1408 window-icon-image WINDOW
1409 
1410 Returns an image object representing the icon currently associated with
1411 WINDOW. Returns the symbol `nil' if no such image.
1412 ::end:: */
1413 {
1414    if (!rep_CONSP(args))
1415        return rep_signal_missing_arg (1);
1416    repv win = rep_CAR(args);
1417    rep_DECLARE1 (win, WINDOWP);
1418 
1419    int iconsize = 16;
1420    if (rep_CONSP(rep_CDR(args)) && rep_INTP(rep_CAR(rep_CDR(args))))
1421    {
1422        iconsize = rep_INT(rep_CAR(rep_CDR(args)));
1423    }
1424 
1425    if (VWIN (win)->icon_image == rep_NULL || VWIN (win)->icon_size != iconsize)
1426    {
1427        VWIN (win)->icon_size = iconsize;
1428 
1429        #ifdef HAVE_GDK_PIXBUF
1430        if (!WINDOW_IS_GONE_P (VWIN (win)))
1431        {
1432 	   Atom actual_type;
1433 	   int actual_format;
1434 	   unsigned long nitems, bytes_after;
1435 	   union {
1436 	       unsigned long *l;
1437 	       unsigned char *c;
1438 	   } data;
1439 
1440 	   static Atom net_wm_icon = 0;
1441 
1442 	   if (net_wm_icon == 0)
1443 	       net_wm_icon = XInternAtom (dpy, "_NET_WM_ICON", False);
1444 
1445 	   data.l = 0;
1446 	   if (XGetWindowProperty (dpy, VWIN (win)->id, net_wm_icon,
1447 				   0L, G_MAXLONG, False, AnyPropertyType,
1448 				   &actual_type, &actual_format,
1449 				   &nitems, &bytes_after,
1450 				   &data.c) == Success)
1451 	   {
1452 	       int i;
1453 	       for (i = 0; i < nitems; i += 2 + data.l[i] * data.l[i + 1])
1454 		   if (data.l[i] == iconsize && data.l[i + 1] == iconsize)
1455 		   {
1456 			VWIN (win)->icon_image =
1457 				make_image_from_data(&(data.l[i + 2]), iconsize);
1458 			XFree (data.l);
1459 			return VWIN (win)->icon_image;
1460 		   }
1461 	   }
1462 	   if (data.l != 0)
1463 	       XFree (data.l);
1464        }
1465        #endif
1466 
1467        Window pixmap_id = 0, mask_id = 0;
1468 
1469        if (VWIN (win)->wmhints != 0)
1470        {
1471 	   if (VWIN (win)->wmhints->flags & IconPixmapHint
1472 	       && VWIN (win)->wmhints->icon_pixmap != 0)
1473 	   {
1474 	       pixmap_id = VWIN (win)->wmhints->icon_pixmap;
1475 	   }
1476 
1477 	   if (VWIN (win)->wmhints->flags & IconMaskHint
1478 	       && VWIN (win)->wmhints->icon_mask != 0)
1479 	   {
1480 	       mask_id = VWIN (win)->wmhints->icon_mask;
1481 	   }
1482        }
1483 
1484        if (pixmap_id == 0 && !WINDOW_IS_GONE_P (VWIN (win)))
1485        {
1486 	   Atom actual_type;
1487 	   int actual_format;
1488 	   unsigned long nitems, bytes_after;
1489 	   union {
1490 	       unsigned long *l;
1491 	       unsigned char *c;
1492 	   } data;
1493 
1494 	   static Atom kwm_win_icon = 0;
1495 
1496 	   if (kwm_win_icon == 0)
1497 	       kwm_win_icon = XInternAtom (dpy, "KWM_WIN_ICON", False);
1498 
1499 	   data.l = 0;
1500 	   if (XGetWindowProperty (dpy, VWIN (win)->id, kwm_win_icon,
1501 				   0, 2, False, kwm_win_icon,
1502 				   &actual_type, &actual_format,
1503 				   &nitems, &bytes_after,
1504 				   &data.c) == Success
1505 	       && actual_type == kwm_win_icon
1506 	       && bytes_after == 0)
1507 	   {
1508 	       pixmap_id = data.l[0];
1509 	       mask_id = data.l[1];
1510 	   }
1511 	   if (data.l != 0)
1512 	       XFree (data.l);
1513        }
1514 
1515        VWIN (win)->icon_image = Qnil;
1516 
1517        if (pixmap_id != 0)
1518        {
1519 	   VWIN (win)->icon_image = (Fmake_image_from_x_drawable
1520 				     (rep_MAKE_INT (pixmap_id),
1521 				      mask_id ? rep_MAKE_INT (mask_id) : Qnil));
1522        }
1523    }
1524 
1525    return VWIN (win)->icon_image;
1526 }
1527 
1528 DEFUN ("map-windows", Fmap_windows, Smap_windows, (repv fun), rep_Subr1) /*
1529 ::doc:sawfish.wm.windows.subrs#map-windows::
1530 map-windows FUN
1531 
1532 Map the single-parameter function FUN over all existing windows.
1533 ::end:: */
1534 {
1535     repv w;
1536     rep_GC_root gc_fun, gc_w;
1537     repv ret = Qnil;
1538 
1539     rep_PUSHGC (gc_fun, fun);
1540     rep_PUSHGC (gc_w, w);
1541     for (w = rep_VAL (window_list); w != rep_NULL; w = rep_VAL (VWIN(w)->next))
1542     {
1543 	if (!WINDOW_IS_GONE_P (VWIN (w)))
1544 	{
1545 	    ret = rep_call_lisp1 (fun, w);
1546 	    if (ret == rep_NULL)
1547 		break;
1548 	}
1549     }
1550     rep_POPGC; rep_POPGC;
1551     return ret;
1552 }
1553 
1554 DEFUN ("filter-windows", Ffilter_windows,
1555        Sfilter_windows, (repv pred), rep_Subr1) /*
1556 ::doc:sawfish.wm.windows.subrs#filter-windows::
1557 filter-windows PRED
1558 
1559 Return the list of windows that match the predicate function PRED.
1560 ::end:: */
1561 {
1562     repv w, output = Qnil, *ptr = &output;
1563     rep_GC_root gc_pred, gc_w, gc_output;
1564 
1565     rep_PUSHGC(gc_pred, pred);
1566     rep_PUSHGC(gc_w, w);
1567     rep_PUSHGC(gc_output, output);
1568     for (w = rep_VAL (window_list); w != rep_NULL; w = rep_VAL (VWIN(w)->next))
1569     {
1570 	if (!WINDOW_IS_GONE_P (VWIN (w)))
1571 	{
1572 	    repv tem = rep_call_lisp1 (pred, w);
1573 	    if (tem == rep_NULL)
1574 	    {
1575 		output = rep_NULL;
1576 		break;
1577 	    }
1578 	    if (tem != Qnil)
1579 	    {
1580 		*ptr = Fcons (w, Qnil);
1581 		ptr = rep_CDRLOC (*ptr);
1582 	    }
1583 	}
1584     }
1585     rep_POPGC; rep_POPGC; rep_POPGC;
1586     return output;
1587 }
1588 
1589 DEFUN ("fake-release-window", Ffake_release_window,
1590       Sfake_release_window, (void), rep_Subr0) /*
1591 ::doc:sawfish.wm.windows.subrs#fake-release-window::
1592 fake-release-window
1593 
1594 Release the grab from windows using a simulated press
1595 of the Escape key using the Xtest extension.
1596 
1597 This is a hacky solution, but in fact XSendKey is
1598 ignored by most application and thus not an alternative.
1599 
1600 fake-release-window is used by the EdgeActions. It use
1601 outside of them should be as low as possible.
1602 ::end:: */
1603 {
1604 
1605 	/* release windows */
1606 
1607 	unsigned int escape = XKeysymToKeycode(dpy, XK_Escape);
1608 
1609 	XTestFakeKeyEvent (dpy, escape, True, CurrentTime);
1610 	XTestFakeKeyEvent (dpy, escape, False, CurrentTime);
1611 	XSync (dpy, False);
1612 
1613 	return Qt;
1614 
1615 }
1616 
1617 
1618 /* type hooks */
1619 
1620 static int
window_cmp(repv w1,repv w2)1621 window_cmp (repv w1, repv w2)
1622 {
1623     return w1 != w2;
1624 }
1625 
1626 static void
window_prin(repv stream,repv win)1627 window_prin (repv stream, repv win)
1628 {
1629     char buf[128];
1630     sprintf (buf, "#<window %lx>", VWIN(win)->id);
1631     rep_stream_puts (stream, buf, -1, FALSE);
1632 }
1633 
1634 static void
window_mark(repv win)1635 window_mark (repv win)
1636 {
1637     rep_MARKVAL(VWIN(win)->plist);
1638     rep_MARKVAL(VWIN(win)->frame_style);
1639     mark_frame_parts (VWIN(win));
1640     rep_MARKVAL(VWIN(win)->name);
1641     rep_MARKVAL(VWIN(win)->full_name);
1642     rep_MARKVAL(VWIN(win)->icon_name);
1643     rep_MARKVAL(VWIN(win)->net_name);
1644     rep_MARKVAL(VWIN(win)->net_icon_name);
1645     rep_MARKVAL(VWIN(win)->icon_image);
1646 }
1647 
1648 static void
window_mark_type(void)1649 window_mark_type (void)
1650 {
1651     Lisp_Window *w;
1652     struct prop_handler *ph;
1653     for (w = window_list; w != 0; w = w->next)
1654     {
1655 	if (!WINDOW_IS_GONE_P (w) || !w->destroyed)
1656 	    rep_MARKVAL(rep_VAL(w));
1657     }
1658     for (ph = prop_handlers; ph != 0; ph = ph->next)
1659 	rep_MARKVAL (ph->prop);
1660     rep_MARKVAL (rep_VAL (focus_window));
1661 }
1662 
1663 static void
window_sweep(void)1664 window_sweep (void)
1665 {
1666     Lisp_Window **ptr = &window_list;
1667     while (*ptr != 0)
1668     {
1669 	Lisp_Window *w = *ptr;
1670 	if (!rep_GC_CELL_MARKEDP(rep_VAL(w)))
1671 	{
1672 	    assert (!window_in_stacking_list_p (w));
1673 	    destroy_window_frame (w, FALSE);
1674 	    if (w->wmhints != 0)
1675 		XFree (w->wmhints);
1676 	    if (w->n_cmap_windows > 0)
1677 		XFree (w->cmap_windows);
1678 	    *ptr = w->next;
1679 	    rep_FREE_CELL(w);
1680 	}
1681 	else
1682 	{
1683 	    ptr = &(w->next);
1684 	    rep_GC_CLR_CELL(rep_VAL(w));
1685 	}
1686     }
1687 }
1688 
1689 /* initialisation */
1690 
1691 void
manage_windows(void)1692 manage_windows (void)
1693 {
1694     Window root, parent, *children, focus;
1695     unsigned int nchildren, i;
1696     int revert_to;
1697 
1698     Fgrab_server ();
1699     /* avoid Unmap events */
1700     XSelectInput (dpy, root_window, ROOT_EVENTS & ~SubstructureNotifyMask);
1701     XGetInputFocus (dpy, &focus, &revert_to);
1702     if (focus == PointerRoot)
1703     {
1704     	Window root, child;
1705 	Bool found;
1706 	int rx, ry, wx, wy;
1707 	unsigned mask;
1708 
1709 	found = XQueryPointer (dpy, DefaultRootWindow(dpy), &root,
1710 			       &child, &rx, &ry, &wx, &wy, &mask);
1711 	if (!found)
1712 	{
1713 	    found = XQueryPointer (dpy, root, &root, &child,
1714 				   &rx, &ry, &wx, &wy, &mask);
1715 	}
1716 	focus = child;
1717     }
1718 
1719     XQueryTree (dpy, root_window, &root, &parent, &children, &nchildren);
1720     initialising = TRUE;
1721     for (i = 0; i < nchildren; i++)
1722     {
1723 	if (mapped_not_override_p (children[i]))
1724 	{
1725 	    XEvent fake;
1726 	    Lisp_Window *w;
1727 	    fake.xmaprequest.window = children[i];
1728 	    /* Make sure the window is initially unmapped. We expect to
1729 	       get map-notify events when we later remap it.. #67601 */
1730 	    // XUnmapWindow (dpy, children[i]);
1731 	    map_request (&fake);
1732 	    // w = find_window_by_id (children[i]);
1733 	}
1734     }
1735     initialising = FALSE;
1736     if (nchildren > 0)
1737 	XFree (children);
1738 
1739     /* Try to keep the current focus state. */
1740     focus_on_window (0);
1741     if (focus != None)
1742     {
1743 	Lisp_Window *w = find_window_by_id (focus);
1744 	if (w != 0)
1745 	    focus_on_window (w);
1746     }
1747 
1748     XSelectInput (dpy, root_window, ROOT_EVENTS);
1749     Fungrab_server ();
1750     Fcall_hook (Qafter_initialization_hook, Qnil, Qnil);
1751 }
1752 
1753 void
windows_init(void)1754 windows_init (void)
1755 {
1756     repv tem;
1757     window_type = rep_register_new_type ("window", window_cmp, window_prin,
1758 					 window_prin, window_sweep,
1759 					 window_mark, window_mark_type,
1760 					 0, 0, 0, 0, 0, 0);
1761 
1762     tem = rep_push_structure ("sawfish.wm.windows.subrs");
1763     rep_ADD_SUBR(Swindow_get);
1764     rep_ADD_SUBR(Smap_window_properties);
1765     rep_ADD_SUBR(Swindow_put);
1766     rep_ADD_SUBR(Swindow_remprop);
1767     rep_ADD_SUBR(Swindow_plist);
1768     rep_ADD_SUBR(Swindow_name);
1769     rep_ADD_SUBR(Swindow_full_name);
1770     rep_ADD_SUBR(Swindow_icon_name);
1771     rep_ADD_SUBR(Swindow_mapped_p);
1772     rep_ADD_SUBR(Swindow_frame);
1773     rep_ADD_SUBR(Sset_window_frame);
1774     rep_ADD_SUBR(Srebuild_frame);
1775     rep_ADD_SUBR(Swindow_position);
1776     rep_ADD_SUBR(Swindow_dimensions);
1777     rep_ADD_SUBR(Swindow_frame_dimensions);
1778     rep_ADD_SUBR(Swindow_frame_offset);
1779     rep_ADD_SUBR(Swindowp);
1780     rep_ADD_SUBR(Sset_input_focus);
1781     rep_ADD_SUBR(Sinput_focus);
1782     rep_ADD_SUBR(Swindow_wants_input_p);
1783     rep_ADD_SUBR(Smanaged_windows);
1784     rep_ADD_SUBR(Sget_window_by_id);
1785     rep_ADD_SUBR(Sstacking_order);
1786     rep_ADD_SUBR(Swindow_visibility);
1787     rep_ADD_SUBR(Swindow_urgent_p);
1788     rep_ADD_SUBR(Swindow_shaped_p);
1789     rep_ADD_SUBR(Shide_window);
1790     rep_ADD_SUBR(Sshow_window);
1791     rep_ADD_SUBR(Swindow_visible_p);
1792     rep_ADD_SUBR(Swindow_framed_p);
1793     rep_ADD_SUBR(Swindow_frame_id);
1794     rep_ADD_SUBR(Swindow_id);
1795     rep_ADD_SUBR(Swindow_group_id);
1796     rep_ADD_SUBR(Swindow_size_hints);
1797     rep_ADD_SUBR(Scall_window_hook);
1798     rep_ADD_SUBR(Swindow_border_width);
1799     rep_ADD_SUBR(Swindow_icon_image);
1800     rep_ADD_SUBR(Smap_windows);
1801     rep_ADD_SUBR(Sfilter_windows);
1802     rep_ADD_SUBR(Sfake_release_window);
1803     rep_pop_structure (tem);
1804 
1805     rep_INTERN_SPECIAL(before_add_window_hook);
1806     rep_INTERN_SPECIAL(add_window_hook);
1807     rep_INTERN_SPECIAL(after_add_window_hook);
1808     rep_INTERN_SPECIAL(place_window_hook);
1809     rep_INTERN(placed);
1810     rep_INTERN_SPECIAL(after_framing_hook);
1811     rep_INTERN_SPECIAL(after_initialization_hook);
1812     rep_INTERN_SPECIAL(remove_window_hook);
1813 
1814     Fset(Qbefore_add_window_hook, Qnil);
1815     Fset(Qadd_window_hook, Qnil);
1816     Fset(Qafter_add_window_hook, Qnil);
1817     Fset(Qplace_window_hook, Qnil);
1818     Fset(Qafter_framing_hook, Qnil);
1819     Fset(Qafter_initialization_hook, Qnil);
1820     Fset(Qremove_window_hook, Qnil);
1821 
1822     rep_INTERN(fully_obscured);
1823     rep_INTERN(partially_obscured);
1824     rep_INTERN(unobscured);
1825 
1826     rep_INTERN(min_width);
1827     rep_INTERN(min_height);
1828     rep_INTERN(max_width);
1829     rep_INTERN(max_height);
1830     rep_INTERN(width_inc);
1831     rep_INTERN(height_inc);
1832     rep_INTERN(base_width);
1833     rep_INTERN(base_height);
1834     rep_INTERN(min_aspect);
1835     rep_INTERN(max_aspect);
1836     rep_INTERN(user_size);
1837     rep_INTERN(user_position);
1838     rep_INTERN(program_size);
1839     rep_INTERN(program_position);
1840     rep_INTERN(window_gravity);
1841     rep_INTERN(forget);
1842     rep_INTERN(static);
1843     rep_INTERN(north_west);
1844     rep_INTERN(north);
1845     rep_INTERN(north_east);
1846     rep_INTERN(west);
1847     rep_INTERN(east);
1848     rep_INTERN(south_west);
1849     rep_INTERN(south);
1850     rep_INTERN(south_east);
1851 
1852     gravity_map[ForgetGravity] = Qforget;
1853     gravity_map[NorthWestGravity] = Qnorth_west;
1854     gravity_map[NorthGravity] = Qnorth;
1855     gravity_map[NorthEastGravity] = Qnorth_east;
1856     gravity_map[WestGravity] = Qwest;
1857     gravity_map[CenterGravity] = Qcenter;
1858     gravity_map[EastGravity] = Qeast;
1859     gravity_map[SouthWestGravity] = Qsouth_west;
1860     gravity_map[SouthGravity] = Qsouth;
1861     gravity_map[SouthEastGravity] = Qsouth_east;
1862     gravity_map[StaticGravity] = Qstatic;
1863 }
1864 
1865 void
windows_kill(void)1866 windows_kill (void)
1867 {
1868     Lisp_Window *w = window_list;
1869     repv next;
1870     rep_GC_root gc_next;
1871     rep_PUSHGC (gc_next, next);
1872     while (w != 0)
1873     {
1874 	next = rep_VAL (w->next);
1875 	Fcall_window_hook (Qremove_window_hook, rep_VAL (w), Qnil, Qnil);
1876 	remove_window (w, FALSE, FALSE);
1877 	w = VWIN (next);
1878     }
1879 }
1880