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