/* windows.c -- window manipulation $Id: windows.c,v 1.127 2003/01/09 13:49:55 jsh Exp $ Copyright (C) 1999 John Harper This file is part of sawmill. sawmill is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. sawmill is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with sawmill; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "sawmill.h" #include #include Lisp_Window *window_list; int window_type; Lisp_Window *focus_window; int pending_destroys; static bool initialising; DEFSYM(add_window_hook, "add-window-hook"); DEFSYM(before_add_window_hook, "before-add-window-hook"); DEFSYM(after_add_window_hook, "after-add-window-hook"); DEFSYM(place_window_hook, "place-window-hook"); DEFSYM(placed, "placed"); DEFSYM(after_framing_hook, "after-framing-hook"); DEFSYM(after_initialization_hook, "after-initialization-hook"); DEFSYM(remove_window_hook, "remove-window-hook"); /* for visibility-notify-hook */ DEFSYM(fully_obscured, "fully-obscured"); DEFSYM(partially_obscured, "partially-obscured"); DEFSYM(unobscured, "unobscured"); /* for window-size-hints */ DEFSYM(min_width, "min-width"); DEFSYM(min_height, "min-height"); DEFSYM(max_width, "max-width"); DEFSYM(max_height, "max-height"); DEFSYM(width_inc, "width-inc"); DEFSYM(height_inc, "height-inc"); DEFSYM(base_width, "base-width"); DEFSYM(base_height, "base-height"); DEFSYM(min_aspect, "min-aspect"); DEFSYM(max_aspect, "max-aspect"); DEFSYM(user_size, "user-size"); DEFSYM(program_size, "program-size"); DEFSYM(user_position, "user-position"); DEFSYM(program_position, "program-position"); DEFSYM(window_gravity, "window-gravity"); /* for window-gravity */ DEFSYM(forget, "forget"); DEFSYM(static, "static"); DEFSYM(north_west, "north-west"); DEFSYM(north, "north"); DEFSYM(north_east, "north-east"); DEFSYM(west, "west"); DEFSYM(center, "center"); DEFSYM(east, "east"); DEFSYM(south_west, "south-west"); DEFSYM(south, "south"); DEFSYM(south_east, "south-east"); static repv gravity_map[StaticGravity+1]; struct prop_handler { struct prop_handler *next; repv prop; void (*callback) (Lisp_Window *w, repv prop, repv old, repv new); }; static struct prop_handler *prop_handlers; /* utilities */ /* Returns true if we should manage window ID */ bool mapped_not_override_p (Window id) { XWindowAttributes wa; XGetWindowAttributes(dpy, id, &wa); return ((wa.map_state != IsUnmapped) && (wa.override_redirect != True)); } /* Returns true if the window's Input hint is set (or defaults to being set) */ static bool window_input_hint_p (Lisp_Window *w) { if (w->wmhints != 0 && (w->wmhints->flags & InputHint)) return w->wmhints->input; else return TRUE; } static Window queued_focus_id; static bool queued_take_focus; static bool queued_set_focus; static int queued_focus_revert; static Time queued_focus_time; /* We can lose the focus sometimes, notably after a was-focused window is closed while a keyboard grab exists.. (netscape) */ static void check_for_lost_focus (void) { Window focus; int revert_to; XGetInputFocus (dpy, &focus, &revert_to); if (focus == None || focus == PointerRoot) { DB (("lost focus (%ld)\n", focus)); focus_on_window (focus_window); } } void commit_queued_focus_change (void) { if (0 && queued_focus_id == 0) check_for_lost_focus (); if (queued_focus_id != 0) { if (queued_take_focus) { DB((" sending WM_TAKE_FOCUS %x %ld\n", (unsigned) queued_focus_id, queued_focus_time)); send_client_message (queued_focus_id, xa_wm_take_focus, queued_focus_time); } if (queued_set_focus) { DB((" focusing %x %ld\n", (unsigned) queued_focus_id, queued_focus_time)); XSetInputFocus (dpy, queued_focus_id, queued_focus_revert, queued_focus_time); } queued_focus_id = 0; } } /* Give the input focus to window W, or to no window if W is null */ void focus_on_window (Lisp_Window *w) { /* something's going to change, so */ queued_focus_id = 0; queued_focus_time = last_event_time; queued_set_focus = FALSE; queued_take_focus = FALSE; if (w != 0 && !WINDOW_IS_GONE_P (w) && w->visible) { DB(("focus_on_window (%s)\n", rep_STR(w->name))); if (!w->client_unmapped) { queued_focus_id = w->id; if (w->does_wm_take_focus) { queued_take_focus = TRUE; if (window_input_hint_p (w)) queued_set_focus = TRUE; } else queued_set_focus = TRUE; } else { queued_focus_id = w->frame; queued_set_focus = TRUE; } queued_focus_revert = RevertToParent; } if (queued_focus_id == 0 || (!queued_set_focus && !queued_take_focus)) { DB(("focus_on_window (nil)\n")); queued_focus_id = no_focus_window; queued_set_focus = TRUE; queued_focus_revert = RevertToNone; queued_focus_time = last_event_time; } } /* Should be called when W is no longer focusable. */ void focus_off_window (Lisp_Window *w) { if (w == focus_window) { focus_window = 0; /* Do this immediately. Any real focus-change will be queued, so will happen after this. Doing this here just prevents us getting stuck with focus on nothing in some cases.. */ XSetInputFocus (dpy, no_focus_window, RevertToNone, last_event_time); } } /* Set flags in W relating to which window manager protocols are recognised by window W. */ void get_window_protocols (Lisp_Window *w) { Atom *prot; u_int n; w->does_wm_take_focus = 0; w->does_wm_delete_window = 0; if (XGetWMProtocols (dpy, w->id, &prot, &n) != 0) { int i; for (i = 0; i < n; i++) { if (prot[i] == xa_wm_take_focus) { w->does_wm_take_focus = 1; DB((" WM_TAKE_FOCUS is set\n")); } if (prot[i] == xa_wm_delete_window) { w->does_wm_delete_window = 1; DB((" WM_DELETE_WINDOW is set\n")); } } XFree (prot); } } /* These two functions are used to bracket Xlib requests that would map, unmap, or reparent the client window. They ensure that any StructureNotifymask events generated between calling before_local_map () and after_local_map () are discarded, but no others (so that we don't lose client-generated events) */ void before_local_map (Lisp_Window *w) { Fgrab_server (); XSelectInput (dpy, w->id, CLIENT_EVENTS & ~StructureNotifyMask); } void after_local_map (Lisp_Window *w) { XSelectInput (dpy, w->id, CLIENT_EVENTS); Fungrab_server (); } /* manipulating the Lisp window structures */ /* Return the window object containing ID, or a null pointer */ Lisp_Window * find_window_by_id (Window id) { Lisp_Window *w; w = window_list; while (w != 0 && w->id != id && w->frame != id) w = w->next; if (w != 0 && WINDOW_IS_GONE_P (w)) w = 0; return w; } /* This is different to the above in that it could return a window that doesn't have a client window. */ Lisp_Window * x_find_window_by_id (Window id) { Lisp_Window *w; w = window_list; while (w != 0 && w->saved_id != id && w->frame != id) w = w->next; return w; } void install_window_frame (Lisp_Window *w) { DB(("install_window_frame (%s)\n", rep_STR(w->name))); if (!w->reparented && w->frame != 0 && !WINDOW_IS_GONE_P (w)) { XSetWindowAttributes wa; XSelectInput (dpy, w->frame, FRAME_EVENTS); before_local_map (w); XReparentWindow (dpy, w->id, w->frame, -w->frame_x, -w->frame_y); w->reparented = TRUE; after_local_map (w); restack_window (w); if (queued_focus_id == w->id) queued_focus_id = w->frame; XAddToSaveSet (dpy, w->id); restack_frame_parts (w); reset_frame_parts (w); wa.win_gravity = StaticGravity; XChangeWindowAttributes (dpy, w->id, CWWinGravity, &wa); DB((" reparented to %lx [%dx%d%+d%+d]\n", w->frame, w->frame_width, w->frame_height, w->frame_x, w->frame_y)); } } void remove_window_frame (Lisp_Window *w) { DB(("remove_window_frame (%s)\n", rep_STR(w->name))); if (w->reparented && !WINDOW_IS_GONE_P (w)) { XSetWindowAttributes wa; /* reparent the subwindow back to the root window */ wa.win_gravity = w->attr.win_gravity; XChangeWindowAttributes (dpy, w->id, CWWinGravity, &wa); before_local_map (w); XReparentWindow (dpy, w->id, root_window, w->attr.x, w->attr.y); w->reparented = FALSE; after_local_map (w); restack_window (w); if (queued_focus_id == w->frame) queued_focus_id = w->id; if (!w->mapped) XRemoveFromSaveSet (dpy, w->id); } } /* Add the top-level window ID to the manager's data structures */ Lisp_Window * add_window (Window id) { char *tem; Lisp_Window *w = rep_ALLOC_CELL(sizeof (Lisp_Window)); if (w != 0) { rep_GC_root gc_win; repv win = rep_VAL(w); XWindowChanges xwc; u_int xwcm; long supplied; XTextProperty prop; DB(("add_window (%lx)\n", id)); if (id == root_window) DB((" ** adding root window!?\n")); rep_data_after_gc += sizeof (Lisp_Window); memset (w, 0, sizeof (Lisp_Window)); /* First initialise the Lisp stuff.. */ w->next = window_list; window_list = w; w->car = window_type; w->id = id; w->saved_id = id; w->plist = Qnil; w->frame_style = Qnil;; w->icon_image = rep_NULL; /* have to put it somewhere until it finds the right place */ insert_in_stacking_list_above_all (w); restack_window (w); /* ..now do the X11 stuff */ XSelectInput (dpy, id, CLIENT_EVENTS); XGetWindowAttributes (dpy, id, &w->attr); DB((" orig: width=%d height=%d x=%d y=%d\n", w->attr.width, w->attr.height, w->attr.x, w->attr.y)); if (XGetWMName (dpy, id, &prop) && prop.value) { if (prop.nitems > 0) { char **list; int count; prop.nitems = strlen(prop.value); if (XmbTextPropertyToTextList (dpy, &prop, &list, &count) >= Success) { if (count > 0) w->name = rep_string_dup (list[0]); XFreeStringList (list); } } XFree (prop.value); } if (w->name == 0) w->name = rep_null_string (); w->full_name = w->name; if (XGetIconName (dpy, id, &tem)) { w->icon_name = rep_string_dup (tem); XFree (tem); } else w->icon_name = w->name; w->wmhints = XGetWMHints (dpy, id); if (!XGetWMNormalHints (dpy, w->id, &w->hints, &supplied)) w->hints.flags = 0; get_window_protocols (w); if (!XGetWMColormapWindows (dpy, w->id, &w->cmap_windows, &w->n_cmap_windows)) { w->n_cmap_windows = 0; } { /* Is the window shaped? */ int xws, yws, xbs, ybs; u_int wws, hws, wbs, hbs; int bounding, clip; XShapeSelectInput (dpy, w->id, ShapeNotifyMask); XShapeQueryExtents (dpy, w->id, &bounding, &xws, &yws, &wws, &hws, &clip, &xbs, &ybs, &wbs, &hbs); w->shaped = bounding ? 1 : 0; } DB((" name=`%s' x=%d y=%d width=%d height=%d\n", rep_STR(w->name), w->attr.x, w->attr.y, w->attr.width, w->attr.height)); xwcm = CWX | CWX | CWWidth | CWHeight | CWBorderWidth; xwc.x = w->attr.x; xwc.y = w->attr.y; xwc.width = w->attr.width; xwc.height = w->attr.height; xwc.border_width = 0; XConfigureWindow (dpy, id, xwcm, &xwc); w->visible = TRUE; w->mapped = TRUE; /* only called from map request */ if (initialising) Fwindow_put (rep_VAL (w), Qplaced, Qt); /* ..then call the add-window-hook's.. */ rep_PUSHGC(gc_win, win); Fcall_window_hook (Qbefore_add_window_hook, rep_VAL(w), Qnil, Qnil); Fcall_window_hook (Qadd_window_hook, rep_VAL(w), Qnil, Qnil); rep_POPGC; /* In case the window disappeared during the hook call */ if (!WINDOW_IS_GONE_P (w)) { Fgrab_server (); /* this is where we create and reparent the window frame */ create_window_frame (w); install_window_frame (w); /* this grabs bound events in the subwindow */ grab_window_events (w, TRUE); Fungrab_server (); } else emit_pending_destroys (); if (!WINDOW_IS_GONE_P (w)) { repv tem = Fwindow_get (rep_VAL(w), Qplaced); if (initialising || (tem && tem == Qnil)) { /* ..then the place-window-hook.. */ rep_PUSHGC(gc_win, win); Fcall_window_hook (Qplace_window_hook, rep_VAL(w), Qnil, Qor); rep_POPGC; } } Fwindow_put (rep_VAL(w), Qplaced, Qt); if (!WINDOW_IS_GONE_P (w)) Fcall_window_hook (Qafter_add_window_hook, rep_VAL(w), Qnil, Qnil); if (!WINDOW_IS_GONE_P (w)) { /* Tell the window where it ended up.. */ send_synthetic_configure (w); } } return w; } /* Remove W from the managed windows. If DESTROYED is nil, then the window will be reparented back to the root window */ void remove_window (Lisp_Window *w, repv destroyed, repv from_error) { DB(("remove_window (%s, %s)\n", rep_STR(w->name), destroyed == Qnil ? "nil" : "t")); if (w->id != 0) { if (destroyed == Qnil && from_error == Qnil) { grab_window_events (w, FALSE); remove_window_frame (w); /* Restore original border width of the client */ XSetWindowBorderWidth (dpy, w->id, w->attr.border_width); } if (from_error == Qnil) destroy_window_frame (w, FALSE); if (!WINDOW_IS_GONE_P (w)) remove_from_stacking_list (w); if (from_error == Qnil) focus_off_window (w); w->id = 0; pending_destroys++; /* gc will do the rest... */ } else if (w->frame != 0 && from_error == Qnil) destroy_window_frame (w, FALSE); } void fix_window_size (Lisp_Window *w) { Fgrab_server (); if (w->frame != 0 && w->rebuild_frame != 0) w->rebuild_frame (w); else XResizeWindow (dpy, w->id, w->attr.width, w->attr.height); Fungrab_server (); } /* Call destroy-notify-hook on any newly-dead windows */ void emit_pending_destroys (void) { if (pending_destroys > 0) { Lisp_Window *w; again: for (w = window_list; w != 0 && !rep_INTERRUPTP; w = w->next) { if (WINDOW_IS_GONE_P (w) && !w->destroyed) { w->destroyed = 1; Fcall_window_hook (Qdestroy_notify_hook, rep_VAL(w), Qnil, Qnil); focus_off_window (w); /* gc may have reordered the list, so we have to start at the beginning again.. */ goto again; } } } pending_destroys = 0; } /* Lisp functions */ DEFUN("window-get", Fwindow_get, Swindow_get, (repv win, repv prop), rep_Subr2) /* ::doc:sawfish.wm.windows.subrs#window-get:: window-get WINDOW PROPERTY Return the value of the property named PROPERTY (a symbol) of WINDOW. Note that these are Lisp properties not X properties. ::end:: */ { repv plist; rep_DECLARE1(win, XWINDOWP); plist = VWIN(win)->plist; while (rep_CONSP(plist) && rep_CONSP(rep_CDR(plist))) { if (rep_CAR(plist) == prop || (!rep_SYMBOLP(prop) && rep_value_cmp (rep_CAR(plist), prop) == 0)) { return rep_CAR(rep_CDR(plist)); } plist = rep_CDR(rep_CDR(plist)); } return Qnil; } DEFUN("map-window-properties", Fmap_window_properties, Smap_window_properties, (repv fun, repv win), rep_Subr2) /* ::doc:sawfish.wm.windows.subrs#map-window-properties:: map-window-properties FUNCTION WINDOW Call (FUNCTION PROPERTY VALUE) for all Lisp properties set on window object WINDOW. ::end:: */ { repv ret = Qnil, plist; rep_GC_root gc_plist, gc_fun; rep_DECLARE2 (win, XWINDOWP); plist = VWIN(win)->plist; rep_PUSHGC (gc_plist, plist); rep_PUSHGC (gc_fun, fun); while (rep_CONSP(plist) && rep_CONSP(rep_CDR(plist))) { ret = rep_call_lisp2 (fun, rep_CAR (plist), rep_CADR (plist)); if (ret == rep_NULL) break; plist = rep_CDDR (plist); } rep_POPGC; rep_POPGC; return ret; } void register_property_monitor (repv prop, void (*callback) (Lisp_Window *, repv, repv, repv)) { struct prop_handler *ph = rep_alloc (sizeof (struct prop_handler)); ph->next = prop_handlers; prop_handlers = ph; ph->prop = prop; ph->callback = callback; } DEFUN("window-put", Fwindow_put, Swindow_put, (repv win, repv prop, repv val), rep_Subr3) /* ::doc:sawfish.wm.windows.subrs#window-put:: window-put WINDOW PROPERTY VALUE Set the value of the property named PROPERTY (a symbol) of WINDOW to VALUE. Note that these are Lisp properties not X properties. ::end:: */ { repv plist; rep_DECLARE1(win, XWINDOWP); plist = VWIN(win)->plist; while (rep_CONSP(plist) && rep_CONSP(rep_CDR(plist))) { if (rep_CAR(plist) == prop || (!rep_SYMBOLP(prop) && rep_value_cmp (rep_CAR(plist), prop) == 0)) { struct prop_handler *ph; for (ph = prop_handlers; ph != 0; ph = ph->next) { repv old = rep_CADR (plist); if (ph->prop == prop && old != val) ph->callback (VWIN (win), prop, old, val); } rep_CAR(rep_CDR(plist)) = val; return val; } plist = rep_CDR(rep_CDR(plist)); } plist = Fcons(prop, Fcons(val, VWIN(win)->plist)); if (plist != rep_NULL) VWIN(win)->plist = plist; return val; } DEFUN("window-name", Fwindow_name, Swindow_name, (repv win), rep_Subr1) /* ::doc:sawfish.wm.windows.subrs#window-name:: window-name WINDOW Return the name of window object WINDOW. ::end:: */ { rep_DECLARE1(win, WINDOWP); return VWIN(win)->name; } DEFUN("window-full-name", Fwindow_full_name, Swindow_full_name, (repv win), rep_Subr1) /* ::doc:sawfish.wm.windows.subrs#window-full-name:: window-full-name WINDOW Return the full name of window object WINDOW. ::end:: */ { rep_DECLARE1(win, WINDOWP); return VWIN(win)->full_name; } DEFUN("window-icon-name", Fwindow_icon_name, Swindow_icon_name, (repv win), rep_Subr1) /* ::doc:sawfish.wm.windows.subrs#window-icon-name:: window-icon-name WINDOW Return the name of window object WINDOW's icon. ::end:: */ { rep_DECLARE1(win, WINDOWP); return VWIN(win)->icon_name; } DEFUN("window-mapped-p", Fwindow_mapped_p, Swindow_mapped_p, (repv win), rep_Subr1) /* ::doc:sawfish.wm.windows.subrs#window-mapped-p:: window-mapped-p WINDOW Return t if the client window associated with object WINDOW is mapped. (This doesn't necessarily mean that it is visible.) ::end:: */ { rep_DECLARE1(win, WINDOWP); return VWIN(win)->mapped ? Qt : Qnil; } DEFUN("window-frame", Fwindow_frame, Swindow_frame, (repv win), rep_Subr1) /* ::doc:sawfish.wm.windows.subrs#window-frame:: window-frame WINDOW Return the frame object associated with WINDOW. ::end:: */ { rep_DECLARE1(win, WINDOWP); return VWIN(win)->frame_style; } DEFUN("set-window-frame", Fset_window_frame, Sset_window_frame, (repv win, repv frame), rep_Subr2) /* ::doc:sawfish.wm.windows.subrs#set-window-frame:: set-window-frame WINDOW FRAME Set the frame associated with the window object WINDOW to FRAME (a list). If the window is mapped the old frame will be destroyed and a new frame constructed as specified by FRAME. ::end:: */ { rep_DECLARE1(win, WINDOWP); rep_DECLARE2(frame, rep_LISTP); Fgrab_server (); if (VWIN(win)->reparented) destroy_window_frame (VWIN(win), TRUE); VWIN(win)->frame_style = frame; if (VWIN(win)->reparented) create_window_frame (VWIN(win)); Fungrab_server (); Fcall_window_hook (Qafter_framing_hook, win, Qnil, Qnil); return VWIN(win)->frame_style; } DEFUN("rebuild-frame", Frebuild_frame, Srebuild_frame, (repv win), rep_Subr1) /* ::doc:sawfish.wm.windows.subrs#rebuild-frame:: rebuild-frame WINDOW Reinitialises and recalibrates the window frame of WINDOW. ::end:: */ { rep_DECLARE1(win, WINDOWP); if (VWIN(win)->frame != 0 && VWIN(win)->rebuild_frame != 0) { VWIN(win)->rebuild_frame (VWIN(win)); refresh_frame_parts (VWIN(win)); Fcall_window_hook (Qafter_framing_hook, win, Qnil, Qnil); } return win; } DEFUN("window-position", Fwindow_position, Swindow_position, (repv win), rep_Subr1) /* ::doc:sawfish.wm.windows.subrs#window-position:: window-position WINDOW Return (X . Y) defining the current position of WINDOW. ::end:: */ { rep_DECLARE1(win, WINDOWP); return Fcons (rep_MAKE_INT(VWIN(win)->attr.x), rep_MAKE_INT(VWIN(win)->attr.y)); } DEFUN("window-dimensions", Fwindow_dimensions, Swindow_dimensions, (repv win), rep_Subr1) /* ::doc:sawfish.wm.windows.subrs#window-dimensions:: window-dimensions WINDOW Return (WIDTH . HEIGHT) defining the current dimensions of the client window associated with WINDOW. ::end:: */ { rep_DECLARE1(win, WINDOWP); return Fcons (rep_MAKE_INT(VWIN(win)->attr.width), rep_MAKE_INT(VWIN(win)->attr.height)); } DEFUN("window-frame-dimensions", Fwindow_frame_dimensions, Swindow_frame_dimensions, (repv win), rep_Subr1) /* ::doc:sawfish.wm.windows.subrs#window-frame-dimensions:: window-frame-dimensions WINDOW Return (WIDTH . HEIGHT) defining the current dimensions of the frame surrounding WINDOW. ::end:: */ { rep_DECLARE1(win, WINDOWP); if (VWIN(win)->reparented) { return Fcons (rep_MAKE_INT(VWIN(win)->frame_width), rep_MAKE_INT(VWIN(win)->frame_height)); } else return Fwindow_dimensions (win); } DEFUN("window-frame-offset", Fwindow_frame_offset, Swindow_frame_offset, (repv win), rep_Subr1) /* ::doc:sawfish.wm.windows.subrs#window-frame-offset:: window-frame-offset WINDOW Return (X . Y) defining the offset from the origin of the client window associated with WINDOW to its frame window. ::end:: */ { rep_DECLARE1(win, WINDOWP); return Fcons (rep_MAKE_INT(VWIN(win)->frame_x), rep_MAKE_INT(VWIN(win)->frame_y)); } DEFUN("windowp", Fwindowp, Swindowp, (repv win), rep_Subr1) /* ::doc:sawfish.wm.windows.subrs#windowp:: windowp ARG Return t if ARG is a window object. ::end:: */ { return WINDOWP(win) ? Qt : Qnil; } DEFUN("set-input-focus", Fset_input_focus, Sset_input_focus, (repv win), rep_Subr1) /* ::doc:sawfish.wm.windows.subrs#set-input-focus:: set-input-focus WINDOW Set the input focus to WINDOW. If WINDOW is nil, then no window will have the focus. ::end:: */ { if (win != Qnil && win != Qroot) { rep_DECLARE1(win, WINDOWP); focus_on_window (VWIN(win)); } else focus_on_window (0); return win; } DEFUN("input-focus", Finput_focus, Sinput_focus, (void), rep_Subr0) /* ::doc:sawfish.wm.windows.subrs#input-focus:: input-focus Return the window object that has the input focus, or nil if none does. ::end:: */ { return (focus_window == 0) ? Qnil : rep_VAL(focus_window); } DEFUN("window-wants-input-p", Fwindow_wants_input_p, Swindow_wants_input_p, (repv win), rep_Subr1) /* ::doc:sawfish.wm.windows.subrs#window-wants-input-p:: window-wants-input-p WINDOW Return t if the client window associated with object WINDOW has hinted that it would like to be given the input focus when applicable. ::end:: */ { rep_DECLARE1(win, WINDOWP); if (VWIN(win)->does_wm_take_focus) return Qt; else return window_input_hint_p (VWIN (win)) ? Qt : Qnil; } DEFUN("managed-windows", Fmanaged_windows, Smanaged_windows, (void), rep_Subr0) /* ::doc:sawfish.wm.windows.subrs#managed-windows:: managed-windows Return a list of all known client window objects. ::end:: */ { repv list = Qnil; Lisp_Window *w = window_list; while (w != 0) { if (!WINDOW_IS_GONE_P (w)) list = Fcons (rep_VAL(w), list); w = w->next; } return list; } DEFUN("get-window-by-id", Fget_window_by_id, Sget_window_by_id, (repv id), rep_Subr1) /* ::doc:sawfish.wm.windows.subrs#get-window-by-id:: get-window-by-id ID Return the window object associated with xid ID, or nil. ::end:: */ { Lisp_Window *w; rep_DECLARE1(id, rep_INTEGERP); w = find_window_by_id (rep_get_long_uint (id)); return w ? rep_VAL(w) : Qnil; } DEFUN("stacking-order", Fstacking_order, Sstacking_order, (void), rep_Subr0) /* ::doc:sawfish.wm.windows.subrs#stacking-order:: stacking-order Return a list of windows defining the current stacking order of all client windows. ::end:: */ { return make_stacking_list (); } DEFUN("window-visibility", Fwindow_visibility, Swindow_visibility, (repv win), rep_Subr1) /* ::doc:sawfish.wm.windows.subrs#window-visibility:: window-visibility WINDOW Return a symbol defining the visibility of WINDOW. Possible returned symbols are `fully-obscured', `partially-obscured' or `unobscured'. ::end:: */ { repv sym = Qnil; rep_DECLARE1(win, WINDOWP); switch (VWIN(win)->frame_vis) { case VisibilityFullyObscured: sym = Qfully_obscured; break; case VisibilityPartiallyObscured: sym = Qpartially_obscured; break; case VisibilityUnobscured: sym = Qunobscured; break; } return sym; } DEFUN("window-urgent-p", Fwindow_urgent_p, Swindow_urgent_p, (repv win), rep_Subr1) /* ::doc:sawfish.wm.windows.subrs#window-urgent-p:: window-urgent-p WINDOW Return true if the `Urgency' hint of the window associated with WINDOW is set. ::end:: */ { rep_DECLARE1 (win, WINDOWP); return ((VWIN (win)->wmhints && VWIN (win)->wmhints->flags & XUrgencyHint) ? Qt : Qnil); } DEFUN("window-shaped-p", Fwindow_shaped_p, Swindow_shaped_p, (repv win), rep_Subr1) /* ::doc:sawfish.wm.windows.subrs#window-shaped-p:: window-shaped-p WINDOW Return non-nil if WINDOW is shaped. ::end:: */ { rep_DECLARE1(win, WINDOWP); return VWIN(win)->shaped ? Qt : Qnil; } DEFUN("hide-window", Fhide_window, Shide_window, (repv win), rep_Subr1) /* ::doc:sawfish.wm.windows.subrs#hide-window:: hide-window WINDOW Prevent WINDOW from being displayed. See `show-window'. ::end:: */ { rep_DECLARE1(win, WINDOWP); if (VWIN(win)->visible) { if (VWIN(win)->mapped) { if (VWIN(win)->frame) XUnmapWindow (dpy, VWIN(win)->frame); if (!VWIN(win)->client_unmapped) { before_local_map (VWIN(win)); XUnmapWindow (dpy, VWIN(win)->id); VWIN(win)->client_unmapped = 1; after_local_map (VWIN(win)); } } VWIN(win)->visible = 0; reset_frame_parts (VWIN(win)); } return win; } DEFUN("show-window", Fshow_window, Sshow_window, (repv win), rep_Subr1) /* ::doc:sawfish.wm.windows.subrs#show-window:: show-window WINDOW Ensure that WINDOW (if it has been mapped) is visible. See `hide-window'. ::end:: */ { rep_DECLARE1(win, WINDOWP); if (!VWIN(win)->visible) { if (VWIN(win)->mapped) { if (VWIN(win)->client_unmapped && !VWIN(win)->client_hidden) { before_local_map (VWIN(win)); XMapWindow (dpy, VWIN(win)->id); VWIN(win)->client_unmapped = 0; after_local_map (VWIN(win)); } if (VWIN(win)->frame) XMapWindow (dpy, VWIN(win)->frame); } VWIN(win)->visible = 1; } return win; } DEFUN("window-visible-p", Fwindow_visible_p, Swindow_visible_p, (repv win), rep_Subr1) /* ::doc:sawfish.wm.windows.subrs#window-visible-p:: window-visible-p WINDOW Return t if WINDOW is currently visible (i.e. not hidden, see `hide-window'). ::end:: */ { rep_DECLARE1(win, WINDOWP); return VWIN(win)->visible ? Qt : Qnil; } DEFUN("window-framed-p", Fwindow_framed_p, Swindow_framed_p, (repv win), rep_Subr1) /* ::doc:sawfish.wm.windows.subrs#window-framed-p:: window-framed-p WINDOW Return t if WINDOW has been reparented to a frame window. ::end:: */ { rep_DECLARE1(win, WINDOWP); return VWIN(win)->reparented ? Qt : Qnil; } DEFUN("window-id", Fwindow_id, Swindow_id, (repv win), rep_Subr1) /* ::doc:sawfish.wm.windows.subrs#window-id:: window-id WINDOW Return the numeric id of the client window associated with object WINDOW. Returns nil if the client window has been deleted. ::end:: */ { rep_DECLARE1(win, WINDOWP); return VWIN(win)->id ? rep_MAKE_INT (VWIN(win)->id) : Qnil; } DEFUN("window-group-id", Fwindow_group_id, Swindow_group_id, (repv win), rep_Subr1) /* ::doc:sawfish.wm.windows.subrs#window-group-id:: window-group-id WINDOW Return the numeric id defining the leader of the group that WINDOW is a member of, or nil if it is not a member of a group. ::end:: */ { rep_DECLARE1(win, WINDOWP); if (VWIN(win)->wmhints == 0) return Qnil; return ((VWIN(win)->wmhints->flags & WindowGroupHint) ? rep_MAKE_INT (VWIN(win)->wmhints->window_group) : Qnil); } DEFUN("window-border-width", Fwindow_border_width, Swindow_border_width, (repv win), rep_Subr1) /* ::doc:sawfish.wm.windows.subrs#window-border-width:: window-border-width WINDOW ::end:: */ { rep_DECLARE1(win, WINDOWP); return rep_MAKE_INT(VWIN(win)->attr.border_width); } DEFUN("window-size-hints", Fwindow_size_hints, Swindow_size_hints, (repv win), rep_Subr1) /* ::doc:sawfish.wm.windows.subrs#window-size-hints:: window-size-hints WINDOW Return an alist defining the size-hints specified by the client window associated with WINDOW. Possible keys in the alist are `min-height', `max-height', `min-width', `max-width', `height-inc', `width-inc', `min-aspect', `max-aspect', `base-height', `base-width', `user-position', `program-position', `user-size', `program-size', `window-gravity', `border-size'. ::end:: */ { repv ret = Qnil; XSizeHints *hints; long flags; rep_DECLARE1(win, WINDOWP); hints = &VWIN(win)->hints; flags = hints->flags; /* Some sanity checking */ if ((flags & PMinSize) && (hints->min_width < 0 || hints->min_height < 0)) flags &= ~PMinSize; if ((flags & PMaxSize) && (hints->max_width <= 0 || hints->max_height <= 0)) flags &= ~PMaxSize; if ((flags & PResizeInc) && (hints->width_inc <= 0 || hints->width_inc <= 0)) flags &= ~PResizeInc; if ((flags & PBaseSize) && (hints->base_width <= 0 || hints->base_height <= 0)) flags &= ~PBaseSize; if (flags & PMinSize) { ret = Fcons (Fcons (Qmin_width, rep_MAKE_INT(MAX(hints->min_width,1))), Fcons (Fcons (Qmin_height, rep_MAKE_INT(MAX(hints->min_height, 1))), ret)); } if (flags & PMaxSize) { ret = Fcons (Fcons (Qmax_width, rep_MAKE_INT(hints->max_width)), Fcons (Fcons (Qmax_height, rep_MAKE_INT(hints->max_height)), ret)); } if (flags & PResizeInc) { ret = Fcons (Fcons (Qwidth_inc, rep_MAKE_INT(hints->width_inc)), Fcons (Fcons (Qheight_inc, rep_MAKE_INT(hints->height_inc)), ret)); } if (flags & PBaseSize) { ret = Fcons (Fcons (Qbase_width, rep_MAKE_INT(hints->base_width)), Fcons (Fcons (Qbase_height, rep_MAKE_INT(hints->base_height)), ret)); } if (flags & PAspect) { ret = Fcons (Fcons (Qmin_aspect, Fcons (rep_MAKE_INT(hints->min_aspect.x), rep_MAKE_INT(hints->min_aspect.y))), Fcons (Fcons (Qmax_aspect, Fcons (rep_MAKE_INT(hints->max_aspect.x), rep_MAKE_INT(hints->max_aspect.y))), ret)); } if (flags & USPosition) ret = Fcons (Fcons (Quser_position, Qt), ret); else if (flags & PPosition) ret = Fcons (Fcons (Qprogram_position, Qt), ret); if (flags & USSize) ret = Fcons (Fcons (Quser_size, Qt), ret); else if (flags & PSize) ret = Fcons (Fcons (Qprogram_size, Qt), ret); if ((flags & PWinGravity) && hints->win_gravity >= ForgetGravity && hints->win_gravity <= StaticGravity) { ret = Fcons (Fcons (Qwindow_gravity, gravity_map[hints->win_gravity]), ret); } return ret; } DEFUN("call-window-hook", Fcall_window_hook, Scall_window_hook, (repv hook, repv win, repv args, repv type), rep_Subr4) /* ::doc:sawfish.wm.windows.subrs#call-window-hook:: call-window-hook HOOK WINDOW &optional ARGS HOOK-TYPE Call HOOK for WINDOW with extra arguments ARGS. See `call-hook' for a description of HOOK-TYPE. ::end:: */ { repv tem; rep_GC_root gc_hook, gc_args, gc_type; rep_DECLARE1(hook, rep_SYMBOLP); rep_DECLARE2(win, XWINDOWP); args = Fcons (win, args); rep_PUSHGC(gc_hook, hook); rep_PUSHGC(gc_args, args); rep_PUSHGC(gc_type, type); tem = Fwindow_get (win, hook); if (tem && tem != Qnil) { tem = Fcall_hook (tem, args, type); if (!tem || (type == Qand && tem == Qnil) || (type == Qor && tem != Qnil)) { goto out; } } tem = Fcall_hook (hook, args, type); out: rep_POPGC; rep_POPGC; rep_POPGC; return tem; } DEFUN("window-icon-image", Fwindow_icon_image, Swindow_icon_image, (repv win), rep_Subr1) /* ::doc:sawfish.wm.windows.subrs#window-icon-image:: window-icon-image WINDOW Returns an image object representing the icon currently associated with WINDOW. Returns the symbol `nil' if no such image. ::end:: */ { rep_DECLARE1 (win, WINDOWP); if (VWIN (win)->icon_image == rep_NULL) { Window pixmap_id = 0, mask_id = 0; if (VWIN (win)->wmhints != 0) { if (VWIN (win)->wmhints->flags & IconPixmapHint && VWIN (win)->wmhints->icon_pixmap != 0) { pixmap_id = VWIN (win)->wmhints->icon_pixmap; } if (VWIN (win)->wmhints->flags & IconMaskHint && VWIN (win)->wmhints->icon_mask != 0) { mask_id = VWIN (win)->wmhints->icon_mask; } } if (pixmap_id == 0 && !WINDOW_IS_GONE_P (VWIN (win))) { Atom actual_type; int actual_format; long nitems, bytes_after; u_long *data = 0; static Atom kwm_win_icon = 0; if (kwm_win_icon == 0) kwm_win_icon = XInternAtom (dpy, "KWM_WIN_ICON", False); if (XGetWindowProperty (dpy, VWIN (win)->id, kwm_win_icon, 0, 2, False, kwm_win_icon, &actual_type, &actual_format, &nitems, &bytes_after, (u_char **) &data) == Success && actual_type == kwm_win_icon && bytes_after == 0) { pixmap_id = data[0]; mask_id = data[1]; } if (data != 0) XFree (data); } VWIN (win)->icon_image = Qnil; if (pixmap_id != 0) { VWIN (win)->icon_image = (Fmake_image_from_x_drawable (rep_MAKE_INT (pixmap_id), mask_id ? rep_MAKE_INT (mask_id) : Qnil)); } } return VWIN (win)->icon_image; } DEFUN ("map-windows", Fmap_windows, Smap_windows, (repv fun), rep_Subr1) /* ::doc:sawfish.wm.windows.subrs#map-windows:: map-windows FUN Map the single-parameter function FUN over all existing windows. ::end:: */ { repv w; rep_GC_root gc_fun, gc_w; repv ret = Qnil; rep_PUSHGC (gc_fun, fun); rep_PUSHGC (gc_w, w); for (w = rep_VAL (window_list); w != rep_NULL; w = rep_VAL (VWIN(w)->next)) { if (!WINDOW_IS_GONE_P (VWIN (w))) { ret = rep_call_lisp1 (fun, w); if (ret == rep_NULL) break; } } rep_POPGC; rep_POPGC; return ret; } DEFUN ("filter-windows", Ffilter_windows, Sfilter_windows, (repv pred), rep_Subr1) /* ::doc:sawfish.wm.windows.subrs#filter-windows:: filter-windows PRED Return the list of windows that match the predicate function PRED. ::end:: */ { repv w, output = Qnil, *ptr = &output; rep_GC_root gc_pred, gc_w, gc_output; rep_PUSHGC(gc_pred, pred); rep_PUSHGC(gc_w, w); rep_PUSHGC(gc_output, output); for (w = rep_VAL (window_list); w != rep_NULL; w = rep_VAL (VWIN(w)->next)) { if (!WINDOW_IS_GONE_P (VWIN (w))) { repv tem = rep_call_lisp1 (pred, w); if (tem == rep_NULL) { output = rep_NULL; break; } if (tem != Qnil) { *ptr = Fcons (w, Qnil); ptr = rep_CDRLOC (*ptr); } } } rep_POPGC; rep_POPGC; rep_POPGC; return output; } /* type hooks */ static int window_cmp (repv w1, repv w2) { return w1 != w2; } static void window_prin (repv stream, repv win) { char buf[128]; sprintf (buf, "#", VWIN(win)->id); rep_stream_puts (stream, buf, -1, FALSE); } static void window_mark (repv win) { rep_MARKVAL(VWIN(win)->plist); rep_MARKVAL(VWIN(win)->frame_style); mark_frame_parts (VWIN(win)); rep_MARKVAL(VWIN(win)->name); rep_MARKVAL(VWIN(win)->full_name); rep_MARKVAL(VWIN(win)->icon_name); rep_MARKVAL(VWIN(win)->icon_image); } static void window_mark_type (void) { Lisp_Window *w; struct prop_handler *ph; for (w = window_list; w != 0; w = w->next) { if (!WINDOW_IS_GONE_P (w) || !w->destroyed) rep_MARKVAL(rep_VAL(w)); } for (ph = prop_handlers; ph != 0; ph = ph->next) rep_MARKVAL (ph->prop); rep_MARKVAL (rep_VAL (focus_window)); } static void window_sweep (void) { Lisp_Window **ptr = &window_list; while (*ptr != 0) { Lisp_Window *w = *ptr; if (!rep_GC_CELL_MARKEDP(rep_VAL(w))) { assert (!window_in_stacking_list_p (w)); destroy_window_frame (w, FALSE); if (w->wmhints != 0) XFree (w->wmhints); if (w->n_cmap_windows > 0) XFree (w->cmap_windows); *ptr = w->next; rep_FREE_CELL(w); } else { ptr = &(w->next); rep_GC_CLR_CELL(rep_VAL(w)); } } } /* initialisation */ void manage_windows (void) { Window root, parent, *children, focus; unsigned int nchildren, i; int revert_to; Fgrab_server (); XGetInputFocus (dpy, &focus, &revert_to); if (focus == PointerRoot) { Window root, child; Bool found; int rx, ry, wx, wy; unsigned mask; found = XQueryPointer (dpy, DefaultRootWindow(dpy), &root, &child, &rx, &ry, &wx, &wy, &mask); if (!found) { found = XQueryPointer (dpy, root, &root, &child, &rx, &ry, &wx, &wy, &mask); } focus = child; } XQueryTree (dpy, root_window, &root, &parent, &children, &nchildren); initialising = TRUE; for (i = 0; i < nchildren; i++) { if (mapped_not_override_p (children[i])) { XEvent fake; Lisp_Window *w; fake.xmaprequest.window = children[i]; /* Make sure the window is initially unmapped. We expect to get map-notify events when we later remap it.. #67601 */ XUnmapWindow (dpy, children[i]); map_request (&fake); w = find_window_by_id (children[i]); } } initialising = FALSE; if (nchildren > 0) XFree (children); /* Try to keep the current focus state. */ focus_on_window (0); if (focus != None) { Lisp_Window *w = find_window_by_id (focus); if (w != 0) focus_on_window (w); } Fungrab_server (); Fcall_hook (Qafter_initialization_hook, Qnil, Qnil); } void windows_init (void) { repv tem; window_type = rep_register_new_type ("window", window_cmp, window_prin, window_prin, window_sweep, window_mark, window_mark_type, 0, 0, 0, 0, 0, 0); tem = rep_push_structure ("sawfish.wm.windows.subrs"); rep_ADD_SUBR(Swindow_get); rep_ADD_SUBR(Smap_window_properties); rep_ADD_SUBR(Swindow_put); rep_ADD_SUBR(Swindow_name); rep_ADD_SUBR(Swindow_full_name); rep_ADD_SUBR(Swindow_icon_name); rep_ADD_SUBR(Swindow_mapped_p); rep_ADD_SUBR(Swindow_frame); rep_ADD_SUBR(Sset_window_frame); rep_ADD_SUBR(Srebuild_frame); rep_ADD_SUBR(Swindow_position); rep_ADD_SUBR(Swindow_dimensions); rep_ADD_SUBR(Swindow_frame_dimensions); rep_ADD_SUBR(Swindow_frame_offset); rep_ADD_SUBR(Swindowp); rep_ADD_SUBR(Sset_input_focus); rep_ADD_SUBR(Sinput_focus); rep_ADD_SUBR(Swindow_wants_input_p); rep_ADD_SUBR(Smanaged_windows); rep_ADD_SUBR(Sget_window_by_id); rep_ADD_SUBR(Sstacking_order); rep_ADD_SUBR(Swindow_visibility); rep_ADD_SUBR(Swindow_urgent_p); rep_ADD_SUBR(Swindow_shaped_p); rep_ADD_SUBR(Shide_window); rep_ADD_SUBR(Sshow_window); rep_ADD_SUBR(Swindow_visible_p); rep_ADD_SUBR(Swindow_framed_p); rep_ADD_SUBR(Swindow_id); rep_ADD_SUBR(Swindow_group_id); rep_ADD_SUBR(Swindow_size_hints); rep_ADD_SUBR(Scall_window_hook); rep_ADD_SUBR(Swindow_border_width); rep_ADD_SUBR(Swindow_icon_image); rep_ADD_SUBR(Smap_windows); rep_ADD_SUBR(Sfilter_windows); rep_pop_structure (tem); rep_INTERN_SPECIAL(before_add_window_hook); rep_INTERN_SPECIAL(add_window_hook); rep_INTERN_SPECIAL(after_add_window_hook); rep_INTERN_SPECIAL(place_window_hook); rep_INTERN(placed); rep_INTERN_SPECIAL(after_framing_hook); rep_INTERN_SPECIAL(after_initialization_hook); rep_INTERN_SPECIAL(remove_window_hook); rep_INTERN(fully_obscured); rep_INTERN(partially_obscured); rep_INTERN(unobscured); rep_INTERN(min_width); rep_INTERN(min_height); rep_INTERN(max_width); rep_INTERN(max_height); rep_INTERN(width_inc); rep_INTERN(height_inc); rep_INTERN(base_width); rep_INTERN(base_height); rep_INTERN(min_aspect); rep_INTERN(max_aspect); rep_INTERN(user_size); rep_INTERN(user_position); rep_INTERN(program_size); rep_INTERN(program_position); rep_INTERN(window_gravity); rep_INTERN(forget); rep_INTERN(static); rep_INTERN(north_west); rep_INTERN(north); rep_INTERN(north_east); rep_INTERN(west); rep_INTERN(center); rep_INTERN(east); rep_INTERN(south_west); rep_INTERN(south); rep_INTERN(south_east); gravity_map[ForgetGravity] = Qforget; gravity_map[NorthWestGravity] = Qnorth_west; gravity_map[NorthGravity] = Qnorth; gravity_map[NorthEastGravity] = Qnorth_east; gravity_map[WestGravity] = Qwest; gravity_map[CenterGravity] = Qcenter; gravity_map[EastGravity] = Qeast; gravity_map[SouthWestGravity] = Qsouth_west; gravity_map[SouthGravity] = Qsouth; gravity_map[SouthEastGravity] = Qsouth_east; gravity_map[StaticGravity] = Qstatic; } void windows_kill (void) { Lisp_Window *w = window_list; repv next; rep_GC_root gc_next; rep_PUSHGC (gc_next, next); while (w != 0) { next = rep_VAL (w->next); Fcall_window_hook (Qremove_window_hook, rep_VAL (w), Qnil, Qnil); remove_window (w, Qnil, Qnil); w = VWIN (next); } }