/* Copyright 1989 GROUPE BULL -- See license conditions in file COPYRIGHT
 * Copyright 1989 Massachusetts Institute of Technology
 */
/**************************************************************\
* 							       *
* 	X11 BULL WINDOW MANAGER.			       *
* 							       *
* 	MODULE FOR OPERATIONS THAT MAY BE CALLED FROM Lisp.    *
* 							       *
\**************************************************************/

/*  include  */

#include        "EXTERN.h"
#include "wool.h"
#include "wl_atom.h"
#include "gwm.h"
#include "wl_number.h"
#include "wl_string.h"
#include "wl_client.h"
#include "wl_menu.h"
#include "wl_name.h"
#include "wl_event.h"
#include "wl_pixmap.h"

/*  local constants  */

/*  external  */

extern XError(), NoXError();
extern WOB_METHOD ClientWindowClass[];
extern WOOL_OBJECT UserMoveWindow(), UserResizeWindow();
extern WOOL_OBJECT UserMoveSeveralWindows();
extern WOOL_OBJECT MwmLikeUserResizeWindow();
extern ClientWindow ClientWindowLeader();

/*  routines  */

char *NoRootError="Window function called on root window%s";
#define NoRoot(w) ((w)->status != RootStatus ? (w) : \
		   (wool_error(NoRootError, ""),w))

/*
 * GetTarget returns window having status in ancestors of current wob
 */

Wob
GetTarget(flag)
unsigned long   flag;
{
    Wob             wob = TargetWob;

    while ((wob -> parent != NULL) && (wob -> status & flag) == 0)
	wob = wob -> parent;
    return wob;
}

/*
 * SetTarget makes TargetWob, TargetWindow and Context
 */

SetTarget(wob)
Wob   wob;
{
    extern WOOL_Namespace ContextNamespace;

    TargetWob = wob;
    while (!(wob -> status & (TargetWindowStatus)))
	wob = wob -> parent;
    TargetWindow = (ClientWindow) wob;
    Context = TargetWindow -> screen;
    ContextNamespace -> current = Context -> screen;
}

WOOL_OBJECT
MapWindow(argc, argv)
int argc;
WOOL_Number argv[];
{
    ClientWindow    cw;

    if (argc)
	cw = (ClientWindow) argv[0] -> number;
    else
	cw = TargetWindow;
    ClientWindowMap(cw);
    return NIL;
}

WOOL_OBJECT
UnmapWindow(argc, argv)
int argc;
WOOL_Number argv[];
{
    ClientWindow    cw;

    if (argc)
	cw = (ClientWindow) argv[0] -> number;
    else
	cw = TargetWindow;
    ClientWindowUnmap(cw);
    return NIL;
}

WOOL_OBJECT
IconifyWindow()
{
    if (TargetWindow) {
	if (TargetWindow -> mapped) {
	    XUnmapWindow(dpy, TargetWindow -> hook);
	    if (TargetWindow -> client && !TargetWindow -> client_wob)
		XUnmapWindow(dpy, TargetWindow -> client);
	    TargetWindow -> mapped = 0;
	}
	if (TargetWindow -> status & IconStatus) {	/* de-iconify */
	    SetTarget(TargetWindow -> window);
	} else {			/* iconify */
	    if (!(TargetWindow -> icon)) {
		if (!RealizeIconWindow(TargetWindow)) {
		    ClientWindowMap(TargetWindow);
		    wool_error("Error in wool code for decorating icon %s",
			       ((WOOL_String) 
				TargetWindow->cached_props->iconname)->string);
		    return NIL;
		}
	    }
	    SetTarget(TargetWindow -> icon);
	}
	ClientWindowMap(TargetWindow);
    }
    return NIL;
}

/* move window returns () if Ok, else:
 *	0 if X problem with server/window
 * 	1 if pointer was not on screen
 * 	2 if user cancelled move
 *	3 if button was released before entering function
 */

WOOL_OBJECT
MoveSeveralWindows(argc, argv)
    int       argc;
    WOOL_Number argv[];
{
    ClientWindow cwl[255];
    int       i;

    for (i = 0; i < argc; i++) {
	must_be_number(argv[i], i);
	cwl[i] = (ClientWindow) argv[i]->number;
    }
    return UserMoveSeveralWindows(argc, cwl);
}


WOOL_OBJECT
MoveWindow(argc, argv)
int argc;
WOOL_Number argv[];
{
    int i;
    ClientWindow    cw = TargetWindow;

    for(i=0; i< argc; i++)		/* MOVE HOOK */
	must_be_number(argv[i], i);
    switch (argc) {
    case 1:
        cw = (ClientWindow) argv[0] -> number;
    case 0:
	return UserMoveWindow(cw);
	break;
    case 2:
	XMoveWindow(dpy, TargetWindow -> hook,
		    TargetWindow -> box.x = argv[0] -> number,
		    TargetWindow -> box.y = argv[1] -> number);
	SendSyntheticMoveEvent(TargetWindow);
	break;
    default:
	XMoveWindow(dpy, ((Wob) (argv[0] -> number)) -> hook,
		    ((Wob) (argv[0] -> number)) -> box.x = argv[1] -> number,
		    ((Wob) (argv[0] -> number)) -> box.y = argv[2] -> number);
	SendSyntheticMoveEvent(argv[0] -> number);
	break;
    }
    return NIL;
}

/* resize window returns () if Ok, else:
 *	0 if X problem with server/window
 *      1 if pointer was not on screen
 *      2 if user cancelled resize
 *	3 if button was released before entering function
 */

WOOL_OBJECT
ResizeWindow(argc, argv)
int argc;
WOOL_Number argv[];
{
    int             width, height;
    int i;
    ClientWindow    cw = TargetWindow;

    for(i=0; i< argc; i++)
	must_be_number(argv[i], i);
    switch (argc) {
    case 1:
	cw = (ClientWindow) argv[0] -> number;
    case 0:
	if (cw -> client)
	    if (GWM_resize_style)
		return MwmLikeUserResizeWindow(cw);
	    else
	    	return UserResizeWindow(cw);
	break;
    case 2:
	if (TargetWindow -> client)
	    width = argv[0] -> number;
	height = argv[1] -> number;
	dims_outer_to_inner(TargetWindow, &width, &height);
	conform_to_hints(&(TargetWindow -> cached_props -> normal_hints),
			 &width, &height);
	XResizeWindow(dpy, TargetWindow -> client, width, height);
	ReconfigureClientWindow(TargetWindow, TargetWindow -> client);
	break;
    default:
	if (!(cw = ((ClientWindow) argv[0] -> number)) -> client)
	    return NIL;
	width = argv[1] -> number;
	height = argv[2] -> number;
	dims_outer_to_inner(cw, &width, &height);
	conform_to_hints(&(cw -> cached_props -> normal_hints), 
			 &width, &height);
	XResizeWindow(dpy, cw -> client, width, height);
        ReconfigureClientWindow(cw, cw -> client);
	break;
    }
    return NIL;
}

/*
 * Raise window (optionally % other top-level window)
 */

WOOL_OBJECT
RaiseWindow(argc, argv)
int argc;
WOOL_Number argv[];
{
    XWindowChanges values;

    switch (argc) {
    case 0:
	XRaiseWindow(dpy, TargetWindow -> hook);
	break;
    case 1:
	must_be_number(argv[0], 0);
	values.sibling = ((Wob) (argv[0] -> number)) -> hook;
	values.stack_mode = Above;
	XConfigureWindow(dpy, TargetWindow -> hook,
			 CWStackMode | CWSibling, &values);
	break;
    }
    return NIL;
}

/*
 * Lower window (optionally % other top-level window)
 */

WOOL_OBJECT
LowerWindow(argc, argv)
int argc;
WOOL_Number argv[];
{
    XWindowChanges values;

    switch (argc) {
    case 0:
	XLowerWindow(dpy, TargetWindow -> hook);
	break;
    case 1:
        must_be_number(argv[0], 0);
        values.sibling = ((Wob) (argv[0] -> number)) -> hook;
        values.stack_mode = Below;
        XConfigureWindow(dpy, TargetWindow -> hook,
                         CWStackMode | CWSibling, &values);
	break;
    }
    return NIL;
}

WOOL_OBJECT
KillWindow(argc, argv)
int argc;
WOOL_Number argv[];
{
    ClientWindow    cw = TargetWindow;
    Wob             wob;

    if (argc)
	cw = (ClientWindow) argv[0] -> number;
    /*  kill always main window, not icon! */
    cw = cw -> window;
    if (wob = (Wob) LookUpWob(cw -> client))
	WOOL_send(WOOL_close, wob, (wob));	/* don't kill gwm! */
    else if (cw -> client)
	XKillClient(dpy, cw -> client);
    return NIL;
}

WOOL_OBJECT
ReDecorateWindow(argc, argv)
int argc;
WOOL_Number argv[];
{
    ClientWindow    cw = TargetWindow;
    int             new_target = 0;
    int             was_mapped;
    Window	    window;
    WOOL_List       window_group;

    if (argc)
	cw = (ClientWindow) (argv[0] -> number);
    if (cw && cw -> type == ClientWindowClass) {
	GWM_ProcessingExistingWindows = 1;
	if (cw == TargetWindow)
	    new_target = 1;
	cw = cw -> window;
	window = cw -> client;
	if (was_mapped = cw -> mapped)
	    ClientWindowUnmap(cw);
	SetTarget(cw);
	if (cw == ClientWindowLeader(cw)) {
	    increase_reference(window_group =
			       (WOOL_List) wool_window_group_get());
	} else {
	    window_group = 0;
	}
	UnDecorateWindow(cw, 1, 1);
	if (cw = (ClientWindow) DecorateWindow(window,
					       Context->rootWob, 1, 1)) {
	    if (was_mapped) {
		ClientWindowMap(cw);
		wool_process_unmap_events(window);
	    } else {
		ClientWindowInitialMap(cw);
	    }
	    if (window_group && window_group != (WOOL_List) NIL) {
		int i;
		for (i=1; i<window_group->size; i++) {
		    AddWindowToGroupLeader(
					  ((WOOL_Number)window_group->list[i])
					   ->number, cw);
		}
	    }
	    GWM_ProcessingExistingWindows = 0;
	    if (new_target)
		SetTarget(cw);
	    return (WOOL_OBJECT) WLNumber_make(cw);
	} else {
	    XMapWindow(dpy, window);
	}
	decrease_reference(window_group);
    }
    return NIL;
}

/* returns NIL if OK
 * (pop-up [menu [item]])
 * if menu = () takes default
 * item is the nth item in the menu (starting with 0).
 * If pointer is not on screen, do nothing and returns 0
 * if cannot grab, returns 1, Ok= ()
 * returns pointer (modifier + buttons status) mask
 */

WOOL_OBJECT
PopMenu(argc, argv)
int argc;
WOOL_Menu argv[];
{
    WOOL_Menu       wl_menu = (WOOL_Menu) TargetWob -> menu;
    int             x, y, rx, ry, item = 0, i;
    Window          root, child;
    Menu            menu;
    int             floating = 1;
    unsigned int    mask;


    for (i = 0; i < argc; i++) {
	if (argv[i] -> type == WLNumber)
	    item = ((WOOL_Number) argv[i]) -> number;
	else if (argv[i] -> type == WLMenu)
	    wl_menu = argv[i];
	else if (argv[i] == (WOOL_Menu) WA_here)
	    floating = 0;
    }
    menu = wl_menu -> wob_menu;
    set_grab(Context -> rootWob);
    XQueryPointer(dpy, Context -> root, &root, &child,
		  &x, &y, &rx, &ry, &mask);
    if (root == Context -> root) {
	if (floating) {
	    PlaceMenu(item, menu, x, y, TargetWob);
	    XMoveWindow(dpy, menu -> hook, menu -> box.x, menu -> box.y);
	}
	XMapRaised(dpy, menu -> hook);
	if (NIL == set_grab(menu)) {	/* ok */
	    XSync(dpy, 0);
	    wool_process_exposes();
	    menu -> parent = (TargetWob == (Wob) menu
			      ? (Wob) Context -> rootWob :TargetWob) ;
	    return NIL;
	} else {			/* grab failed */
	    remove_grab(0);
	    XUnmapWindow(dpy, menu -> hook);
	    return (WOOL_OBJECT) WLNumber_make(1);
	}
    } else {				/* not good screen */
	remove_grab(0);
	return (WOOL_OBJECT) WLNumber_make(0);
    }
}

/* de-pop a menu previously popped by PopMenu
 */

WOOL_OBJECT
UnpopMenu(argc, argv)
int argc;
WOOL_Menu argv[];
{
    Menu            menu;
    WOOL_Menu       wl_menu;

    menu = (Menu) GetTarget(MenuStatus);
    if (argc) {
	wl_menu = argv[0];
	if (wl_menu == (WOOL_Menu) NIL)
	    return NIL;
	if (wl_menu -> type == WLMenu)
	    menu = wl_menu -> wob_menu;
	else
	    menu = (Menu) ((WOOL_Number) wl_menu) -> number;
    }
    remove_grab(menu);
    XUnmapWindow(dpy, menu -> hook);
    menu -> parent = (Wob) Context -> rootWob;
    XSync(dpy, 0);
    return (WOOL_OBJECT) WLNumber_make(menu);
}

PlaceMenu(item, menu, x, y, wob)
int 	item;			/* item to place it */
Menu	menu;			/* the menu */
int	x,y;			/* mouse coordinates */
Wob	wob;			/* triggering wob */
{
    int             oldx, oldy;
    int             bw = 2 * menu -> box.borderwidth;

    if (item >= menu -> nbars)		/* if out put to last */
	item = menu -> nbars - 1;
    /* set pointer in item-th item */
    oldx = menu -> box.x = x - menu -> box.width / 2;
    oldy = menu -> box.y = y - (menu -> bars[item] -> box.y +
				menu -> bars[item] -> box.height / 2);
    /* place full pop on screen */
    if (menu -> box.x < 0)
	menu -> box.x = 0;
    if (menu -> box.y < 0)
	menu -> box.y = 0;
    if (menu -> box.x + menu -> box.width + bw > Context -> width)
	menu -> box.x = Context -> width - menu -> box.width - bw;
    if (menu -> box.y + menu -> box.height + bw > Context -> height)
	menu -> box.y = Context -> height - menu -> box.height - bw;
    if (!GWM_never_warp_pointer
	&& (menu -> box.x != oldx || menu -> box.y != oldy))
	XWarpPointer(dpy, None, None, 0, 0, 0, 0,
		     menu -> box.x - oldx, menu -> box.y - oldy);
}

WOOL_OBJECT
CirculateWindowsDown()
{
    XCirculateSubwindowsDown(dpy, Context->root);
    return NIL;
}

WOOL_OBJECT
CirculateWindowsUp()
{
    XCirculateSubwindowsUp(dpy, Context->root);
    return NIL;
}
	
/*
 * the "current-window" functions
 */

WOOL_OBJECT
wool_current_window()
{
    if (TargetWindow)
	return (WOOL_OBJECT) WLNumber_make(TargetWindow);
    else
	return NIL;
}

WOOL_OBJECT
wool_set_current_window(number)
WOOL_Number 	number;
{
    must_be_number(number, 0);
    if (WobIsValid(number->number)
	&& (
	    (((Wob)(number->number))->type == ClientWindowClass)
	    || (((Wob)(number->number))->type == ScreenClass))) {
	SetTarget(number -> number);
	return (WOOL_OBJECT) number;
    } else {
    	return wool_error(INTERNAL_ERROR, "Not a Window!");
    }
}

WOOL_OBJECT
wool_current_window_is_mapped()
{
    if (TargetWindow -> mapped)
	return TRU;
    else
	return NIL;
}

WOOL_OBJECT
wool_current_window_name()
{
    return (WOOL_OBJECT) NoRoot(TargetWindow) -> cached_props -> windowname;
}

WOOL_OBJECT
wool_current_window_name_set(name)
WOOL_String	name;
{
    XStoreName(dpy, NoRoot(TargetWindow) -> client, name -> string);
    return (WOOL_OBJECT) name;
}

WOOL_OBJECT
wool_current_window_client_name()
{
    return (WOOL_OBJECT) NoRoot(TargetWindow) -> cached_props -> clientname;
}

WOOL_OBJECT
wool_current_window_client_class()
{
    return (WOOL_OBJECT) NoRoot(TargetWindow) -> cached_props -> clientclass;
}

WOOL_OBJECT
wool_current_window_machine_name()
{
    return (WOOL_OBJECT) NoRoot(TargetWindow) -> cached_props -> machinename;
}

WOOL_OBJECT
wool_current_window_icon_name()
{
    return (WOOL_OBJECT) NoRoot(TargetWindow) -> cached_props -> iconname;
}

WOOL_OBJECT
wool_current_window_icon_name_set(name)
WOOL_String	name;
{
    XSetIconName(dpy, NoRoot(TargetWindow) -> client, name -> string);
    return (WOOL_OBJECT) name;
}

WOOL_OBJECT
wool_current_window_geometry(measure)
int measure;
{
    Window          root;
    unsigned int    measures[6];

    XGetGeometry(dpy, TargetWindow -> client,
		 &root, (int *)&measures[0], (int *)&measures[1], &measures[2],
		 &measures[3], &measures[4], &measures[5]);
    return (WOOL_OBJECT) WLNumber_make(measures[measure]);
}

/* sets Context to those of the root of a window */

int
ContextOfXWindow(window)
Window window;
{
    Window          root;
    unsigned int     measures[6];
    int result;

    XSetErrorHandler(NoXError);
    result = XGetGeometry(dpy, window,
                 &root, (int *)&measures[0], (int *)&measures[1], &measures[2],
                 &measures[3], &measures[4], &measures[5]);
    XSync(dpy, 0);
    XSetErrorHandler(XError);
    if (!result) 
	return result;
    result = ScreenOfRoot(root);
    if (result == -1)
	return result;
    SetTarget(GWMManagedScreens[result] -> rootWob);
    return 0;
}

WOOL_OBJECT
wool_current_client_x()
{
    return wool_current_window_geometry(0);
}

WOOL_OBJECT
wool_current_client_y()
{
    return wool_current_window_geometry(1);
}

WOOL_OBJECT
wool_current_client_width()
{
    return wool_current_window_geometry(2);
}

WOOL_OBJECT
wool_current_client_height()
{
    return wool_current_window_geometry(3);
}

WOOL_OBJECT
wool_current_client_borderwidth()
{
    return wool_current_window_geometry(4);
}

WOOL_OBJECT
wool_current_window_x()
{
    return (WOOL_OBJECT) WLNumber_make(TargetWindow -> box.x);
}

WOOL_OBJECT
wool_current_window_y()
{
    return (WOOL_OBJECT) WLNumber_make(TargetWindow -> box.y);
}

WOOL_OBJECT
wool_current_window_width()
{
    return (WOOL_OBJECT) WLNumber_make(TargetWindow -> box.width);
}

WOOL_OBJECT
wool_current_window_height()
{
    return (WOOL_OBJECT) WLNumber_make(TargetWindow -> box.height);
}

/* returns client-managed icon window, if any, but first check that it exists
 */

WOOL_OBJECT
wool_current_window_icon_window()
{
    Window dummy_win;

    if (NoRoot(TargetWindow) -> cached_props -> wm_hints.flags
	& IconWindowHint
	&& !TrapXErrors(XGetTransientForHint(dpy,
		 TargetWindow -> cached_props -> wm_hints.icon_window,
					     &dummy_win))
	) {
	return (WOOL_OBJECT) WLNumber_make(
		      TargetWindow -> cached_props -> wm_hints.icon_window);
    }
    return NIL;
}

/* each time this function is called, construct the icon pixmap from the hints
 * (this to allow for applications changing their icons and user to change
 * color via the foreground and background variables)
 */

WOOL_OBJECT
wool_current_window_icon_bitmap()
{
    if (TargetWindow -> cached_props -> wm_hints.flags & IconPixmapHint) {
	WOOL_Pixmap pixmap = WLPixmap_make(TargetWindow -> cached_props ->
					wm_hints.icon_pixmap);
	if (TargetWindow -> cached_props -> wm_hints.flags & IconMaskHint) {
          Window root;
          int x_hot, y_hot;
	  unsigned int width, height, bw, depth;
          GC gc;
          Pixmap mask = TargetWindow -> cached_props -> wm_hints.icon_mask;
          XGetGeometry(dpy, mask, &root, &x_hot, &y_hot,
                       &width, &height, &bw, &depth);
	  Trace('p', ("wool_current_window_icon_bitmap: create pixmap %d %d %d\n",
		       width, height, depth));
          pixmap -> mask = XCreatePixmap(dpy, root, width, height, depth);
          gc = XCreateGC(dpy, pixmap -> mask, 0, 0);
          XCopyArea(dpy, mask, pixmap -> mask, gc, 0, 0,
                    width, height, 0, 0);
	}
	return (WOOL_OBJECT) pixmap;
    } else {
	return NIL;
    }
}

WOOL_OBJECT
wool_current_window_icon_bitmap_id()
{
    if (TargetWindow -> cached_props -> wm_hints.flags & IconPixmapHint) {
	return (WOOL_OBJECT) WLNumber_make(TargetWindow -> cached_props ->
			  wm_hints.icon_pixmap);
    } else {
	return NIL;
    }
}

WOOL_OBJECT
wool_current_window_US_position()
{
    if (NoRoot(TargetWindow) -> status & IconStatus) {
	if (TargetWindow -> cached_props -> wm_hints.flags & IconPositionHint)
	    return TRU;
    } else {
	if (TargetWindow && TargetWindow -> cached_props -> normal_hints.flags
	    & USPosition)
	    return TRU;
    }
    return NIL;
}

WOOL_OBJECT
wool_current_window_US_size()
{
    if (TargetWindow && TargetWindow -> cached_props -> normal_hints.flags 
 	 & USSize)
	return TRU;
    else
	return NIL;
}

WOOL_OBJECT
wool_current_window_P_position()
{
   if (TargetWindow && TargetWindow -> cached_props -> normal_hints.flags
       & PPosition)
     return TRU;
   else
     return NIL;
}

WOOL_OBJECT
wool_current_window_P_size()
{
    if (TargetWindow && TargetWindow -> cached_props -> normal_hints.flags 
 	 & PSize)
	return TRU;
    else
	return NIL;
}

WOOL_OBJECT
wool_current_window_is_transient_for()
{
    if (NoRoot(TargetWindow) 
	&& TargetWindow -> cached_props -> transient_for) {
	return TargetWindow -> cached_props -> transient_for;
    } else {
	return NIL;
    }
}

WOOL_OBJECT
wool_window_map_as_icon_get()
{
    if ((TargetWindow && TargetWindow -> cached_props -> wm_hints.flags
	 & StateHint)
	&& (TargetWindow -> cached_props -> wm_hints.initial_state == 3))
	return TRU;
    else
	return NIL;
}

WOOL_OBJECT
wool_window_map_as_icon_set(flag)
WOOL_OBJECT	flag;
{
    if (TargetWindow) {
	TargetWindow -> cached_props -> wm_hints.flags |= StateHint;
	TargetWindow -> cached_props -> wm_hints.initial_state
	    = (flag == NIL ? 1 : 3);
    }
    return flag;
}

/*
 * set focus on
 * 	current client if no arg (do nothing if input hint is false)
 * 	other window's client if arg
 * 	if arg = NIL, rootwindow
 */

WOOL_OBJECT
wool_set_focus(argc, argv)
int	argc;
WOOL_Number	argv[];
{
    ClientWindow    cw = TargetWindow;

    if (!cw)
	return NIL;
    if (argc) {
	if (argv[0] == (WOOL_Number) NIL) {
	    XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot,
			   CurrentTime);
	    return TRU;
	} else {
	    cw = (ClientWindow) argv[0] -> number;
	}
    }
    if (GWM_No_set_focus)
	return NIL;
    if (GWM_check_input_focus_flag
	|| ((cw -> cached_props -> wm_hints.flags & InputHint)
	    &&
	    (cw -> cached_props -> wm_hints.input) && cw -> client)) {
	TrapXErrors(XSetInputFocus(dpy, cw -> client, RevertToPointerRoot,
				   CurrentTime));
    } 
    if (cw -> type == ClientWindowClass) {
	if (cw -> cached_props -> wm_take_focus) {
	    send_protocol_message(cw -> client, XA_WM_TAKE_FOCUS,
				  GWMTime, 0, 0);
 	    if ((cw -> cached_props ->wm_hints.flags & InputHint) &&
		(cw -> cached_props -> wm_hints.input)) {
		TrapXErrors(XSetInputFocus(dpy, cw -> client,
					   RevertToPointerRoot, CurrentTime));
 	    }
	}
    } else {
	TrapXErrors(XSetInputFocus(dpy, cw -> hook, RevertToPointerRoot,
				   CurrentTime));
    }
    return TRU;
}

/*
 * returns [x y modifiers screen]
 */

WOOL_OBJECT
wool_current_mouse_position()
{
    unsigned int    ptrmask;	/* state of ptr when queried */
    Window          root, sub_window;
    int             root_x, root_y, cur_x, cur_y;
    WOOL_List	    wl_list = wool_list_make(4);

    root=0;
    XQueryPointer(dpy,
		  Context->root, &root, &sub_window,
		  &root_x, &root_y, &cur_x, &cur_y, &ptrmask);
    increase_reference(wl_list -> list[0] = (WOOL_OBJECT)
	WLNumber_make(cur_x));
    increase_reference(wl_list -> list[1] = (WOOL_OBJECT)
	WLNumber_make(cur_y));
    increase_reference(wl_list -> list[2] = (WOOL_OBJECT)
	WLNumber_make(ptrmask));
    increase_reference(wl_list -> list[3] = (WOOL_OBJECT)
        (root ? WLNumber_make(ScreenOfRoot(root))
	      : WLNumber_make(0)));
    return (WOOL_OBJECT) wl_list;
}

/* warps pointer to pos
 * (warp-pointer x y [window-relative-to])
 */

WOOL_OBJECT
wool_warp_pointer(argc, argv)
int		argc;
WOOL_Number	argv[];
{
    Window          dest_w = None;

    if (argc < 2 || argc > 3)
	wool_error(BAD_NUMBER_OF_ARGS, argc);
    must_be_number(argv[0], 0);
    must_be_number(argv[1], 1);
    if (argc == 3) {
	must_be_number(argv[2], 2);
	dest_w = ((Wob) (argv[2] -> number)) -> hook;
    }
    if (!GWM_never_warp_pointer) {
	XWarpPointer(dpy, None, dest_w,
		     0, 0, 0, 0, argv[0] -> number, argv[1] -> number);
    }
    return NIL;
}

/*
 * logical size of a window is size divided by resize increments (+ base)
 */

WOOL_OBJECT
wool_window_logical_size_get()
{
    WOOL_List       wl_list = wool_list_make(2);
    unsigned int    w = 0, h = 0;

    if (TargetWindow -> client) {
	pixel_to_logical_size(&(TargetWindow -> cached_props -> normal_hints),
	 TargetWindow -> inner_width, TargetWindow -> inner_height, &w, &h);
    }
    increase_reference(wl_list -> list[0] = (WOOL_OBJECT) WLNumber_make(w));
    increase_reference(wl_list -> list[1] = (WOOL_OBJECT) WLNumber_make(h));
    return (WOOL_OBJECT) wl_list;
}

WOOL_OBJECT
wool_window_logical_size_set(list)
WOOL_List	list;
{
    unsigned int    w, h;

    if (list -> type != WLList || list == (WOOL_List) NIL || list -> size != 2
	|| list -> list[0] -> type != WLNumber
	|| list -> list[1] -> type != WLNumber)
	bad_argument(list, 0, "list of two numbers");
    logical_to_pixel_size(&(TargetWindow -> cached_props -> normal_hints),
			  ((WOOL_Number) list -> list[0]) -> number,
			  ((WOOL_Number) list -> list[1]) -> number,
			  &w, &h);
    XResizeWindow(dpy, TargetWindow -> client, w, h);
    ReconfigureClientWindow(TargetWindow, TargetWindow -> client);
    return NIL;
}

/* moves a window at x,y and gravity
 * WARNING: xpt & yptr are pointers on SHORTS
 */

PlaceWinWithGravity(w, gravity, anchor_x, anchor_y, xptr, yptr, width, height, 
		    borderwidth)
Window  w;
int     gravity, anchor_x, anchor_y;
short   *xptr, *yptr;
unsigned int width, height, borderwidth;
{
    int x = *xptr, y = *yptr;

    switch((gravity - 1) % 3) {
    case 0:
	x = anchor_x;
	break;
    case 1:
	x = anchor_x - width/2 - borderwidth;
	break;
    case 2:
	x = anchor_x - width - 2*borderwidth;
	break;
    }
    switch((gravity - 1) / 3) {
    case 0:
	y = anchor_y;
	break;
    case 1:
	y = anchor_y - height/2 - borderwidth;
	break;
    case 2:
	y = anchor_y - height - 2*borderwidth;
	break;
    }
    if (*xptr != x || *yptr != y) {
	*xptr = x;
	*yptr = y;
	XMoveWindow(dpy, w, x, y);
    }
}
