/* builtins.c: the built-in commands */ /* Copyright (C) 1999 by the Massachusetts Institute of Technology. * * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without * fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting * documentation, and that the name of M.I.T. not be used in * advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" * without express or implied warranty. */ #include #include #include #include #include #include "nawm.h" #include "lang.h" #include "parser.h" #include "builtins.h" #include "cache.h" #include #include #include /* commands */ function BEEP = { beep_cmd, 0, -1, 0 }; function CUT = { cut_cmd, 0, -1, 1, { T_STR } }; function DELETE = { delete_cmd, 0, -1, 1, { T_WIN } }; function DESTROY = { destroy_cmd, 0, -1, 1, { T_WIN } }; function DSIZE = { dsize_cmd, 0, -1, 3, { T_WIN, T_INT, T_INT } }; function DSIZETO = { dsizeto_cmd, 0, -1, 3, { T_WIN, T_INT, T_INT } }; function EXEC = { exec_cmd, 0, -1, 1, { T_STR } }; function EXIT = { exit_cmd, 0, -1, 0 }; function FIND = { find_cmd, 0, -1, 1, { T_WIN } }; function FREE = { free_cmd, 0, -1, 0 }; function GRAB = { grab_cmd, 0, -1, 0 }; function LOWER = { lower_cmd, 0, -1, 1, { T_WIN } }; function MAP = { map_cmd, 0, -1, 1, { T_WIN } }; function MOUSECLICK = { mouseclick_cmd, 0, -1, 3, { T_STR, T_INT, T_INT } }; function MOVE = { move_cmd, 0, -1, 3, { T_WIN, T_INT, T_INT } }; function MOVETO = { moveto_cmd, 0, -1, 3, { T_WIN, T_INT, T_INT } }; function PUT = { put_cmd, 0, -1, 1, { T_STR } }; function RAISE = { raise_cmd, 0, -1, 1, { T_WIN } }; function REFRESH = { refresh_cmd, 0, -1, 0 }; function RESTART = { restart_cmd, 0, -1, 0 }; function SETLED = { setled_cmd, 0, -1, 2, { T_INT, T_INT } }; function SETMODE = { setmode_cmd, 0, -1, 1, { T_STR } }; function SETREPEAT = { setrepeat_cmd, 0, -1, 2, { T_STR, T_INT } }; function SIZE = { size_cmd, 0, -1, 3, { T_WIN, T_INT, T_INT } }; function SIZETO = { sizeto_cmd, 0, -1, 3, { T_WIN, T_INT, T_INT } }; function SYNC = { sync_cmd, 0, -1, 0 }; function SYSTEM = { system_cmd, 0, -1, 1, { T_STR } }; function TYPE = { type_cmd, 0, -1, 1, { T_STR } }; function TYPEKEY = { typekey_cmd, 0, -1, 1, { T_STR } }; function UNFOCUS = { unfocus_cmd, 0, -1, 0 }; function UNGRAB = { ungrab_cmd, 0, -1, 0 }; function UNMAP = { unmap_cmd, 0, -1, 1, { T_WIN } }; function WARP = { warp_cmd, 0, -1, 2, { T_INT, T_INT } }; function WARPTO = { warpto_cmd, 0, -1, 2, { T_INT, T_INT } }; function WARPTOWINDOW = { warptowindow_cmd, 0, -1, 1, { T_WIN } }; /* functions */ function AT = { at_fun, T_WIN, -1, 2, { T_INT, T_INT } }; function ATOI = { atoi_fun, T_INT, -1, 1, { T_STR } }; function DHEIGHT = { dheight_fun, T_INT, -1, 1, { T_WIN } }; function DWIDTH = { dwidth_fun, T_INT, -1, 1, { T_WIN } }; function ENV = { env_fun, T_STR, -1, 1, { T_STR } }; function FOCUSWINDOW = { focuswindow_fun, T_WIN, -1, 0 }; function GETLED = { getled_fun, T_INT, -1, 1, { T_INT } }; function GETREPEAT = { getrepeat_fun, T_INT, -1, 1, { T_STR } }; function HASNAME = { hasname_fun, T_INT, -1, 2, { T_WIN, T_STR } }; function HEIGHT = { height_fun, T_INT, -1, 1, { T_WIN } }; function ITOA = { itoa_fun, T_STR, -1, 1, { T_INT } }; function MAPPED = { mapped_fun, T_INT, -1, 1, { T_WIN } }; function NAME = { name_fun, T_STR, -1, 1, { T_WIN } }; function PICK = { pick_fun, T_WIN, -1, 0 }; function POINTERWINDOW = { pointerwindow_fun, T_WIN, -1, 0 }; function PX = { px_fun, T_INT, -1, 0 }; function PY = { py_fun, T_INT, -1, 0 }; function STR2WIN = { str2win_fun, T_WIN, -1, 1, { T_STR } }; function WIDTH = { width_fun, T_INT, -1, 1, { T_WIN } }; /* type for WINDOWS will be filled in by initbuiltins */ function WINDOWS = { windows_fun, 0, -1, 0 }; function XLOC = { xloc_fun, T_INT, -1, 1, { T_WIN } }; function YLOC = { yloc_fun, T_INT, -1, 1, { T_WIN } }; /* variables */ variable CURRENTWINDOW = { T_WIN, -1, 0 }; variable ROOT = { T_WIN, -1, 0 }; variable SCREENHEIGHT = { T_INT, -1, 0 }; variable SCREENWIDTH = { T_INT, -1, 0 }; varbinding builtins[] = { { "at", FUN, &AT }, { "atoi", FUN, &ATOI }, { "beep", CMD, &BEEP }, { "currentwindow", VAR, &CURRENTWINDOW }, { "cut", CMD, &CUT }, { "delete", CMD, &DELETE }, { "destroy", CMD, &DESTROY }, { "dheight", FUN, &DHEIGHT }, { "dsize", CMD, &DSIZE }, { "dsizeto", CMD, &DSIZETO }, { "dwidth", FUN, &DWIDTH }, { "env", FUN, &ENV }, { "exec", CMD, &EXEC }, { "exit", CMD, &EXIT }, { "find", CMD, &FIND }, { "focuswindow", FUN, &FOCUSWINDOW }, { "free", CMD, &FREE }, { "getled", FUN, &GETLED }, { "getrepeat", FUN, &GETREPEAT }, { "grab", CMD, &GRAB }, { "hasname", FUN, &HASNAME }, { "height", FUN, &HEIGHT }, { "itoa", FUN, &ITOA }, { "lower", CMD, &LOWER }, { "map", CMD, &MAP }, { "mapped", FUN, &MAPPED }, { "mouseclick", CMD, &MOUSECLICK }, { "move", CMD, &MOVE }, { "moveto", CMD, &MOVETO }, { "name", FUN, &NAME }, { "pick", FUN, &PICK }, { "pointerwindow", FUN, &POINTERWINDOW }, { "put", CMD, &PUT }, { "px", FUN, &PX }, { "py", FUN, &PY }, { "raise", CMD, &RAISE }, { "refresh", CMD, &REFRESH }, { "restart", CMD, &RESTART }, { "root", VAR, &ROOT }, { "screenheight", VAR, &SCREENHEIGHT }, { "screenwidth", VAR, &SCREENWIDTH }, { "setled", CMD, &SETLED }, { "setmode", CMD, &SETMODE }, { "setrepeat", CMD, &SETREPEAT }, { "size", CMD, &SIZE }, { "sizeto", CMD, &SIZETO }, { "sync", CMD, &SYNC }, { "system", CMD, &SYSTEM }, { "type", CMD, &TYPE }, { "typekey", CMD, &TYPEKEY }, { "unfocus", CMD, &UNFOCUS }, { "ungrab", CMD, &UNGRAB }, { "unmap", CMD, &UNMAP }, { "warp", CMD, &WARP }, { "warpto", CMD, &WARPTO }, { "warptowindow", CMD, &WARPTOWINDOW }, { "width", FUN, &WIDTH }, { "win", FUN, &STR2WIN }, { "windows", FUN, &WINDOWS }, { "xloc", FUN, &XLOC }, { "yloc", FUN, &YLOC }, }; int numbuiltins = sizeof(builtins)/sizeof(varbinding); void *search_builtins(char *name, int *type) { int hi = numbuiltins, lo = 0, mid, cmp; while (hi >= lo && lo < numbuiltins) { mid = (lo + hi) / 2; cmp = strcmp(name, builtins[mid].name); if (cmp == 0) { *type = builtins[mid].type; return builtins[mid].data; } else if (cmp < 0) { if (hi > mid) hi = mid; else hi = mid - 1; } else { if (lo < mid) lo = mid; else lo = mid + 1; } } return NULL; } char *nameof_builtin(void *data) { int i; for (i = 0; i < numbuiltins; i++) { if (builtins[i].data == data) return builtins[i].name; } return NULL; } extern Window root; extern int screenwidth, screenheight; extern Display *dpy; extern int screen; extern int quit, window_manager; extern cacheinfo *cachehead; static Atom XA_WM_DELETE_WINDOW, XA_WM_PROTOCOLS; void initbuiltins(void) { XA_WM_DELETE_WINDOW = XInternAtom(dpy, "WM_DELETE_WINDOW", False); XA_WM_PROTOCOLS = XInternAtom(dpy, "WM_PROTOCOLS", False); WINDOWS.type = array_type(T_WIN, T_INT); } enum {SIZE_EXACT, SIZE_DELTA, SIZE_DISCRETE}; #define hcoord(x) ( (x) < 0 ? screenwidth + (x) : (x) ) #define vcoord(y) ( (y) < 0 ? screenheight + (y) : (y) ) void do_size(Window win, int w, int h, int mode); void getsize(Window win, int *width, int *height, int *dwidth, int *dheight); void querypointer(Window *pw, int *px, int *py); void at_fun(int argc, nawmval *argv, nawmval *ret) { cacheinfo ci; ci.x = hcoord(argv[0]); ci.y = vcoord(argv[1]); if (find_window(&ci, NAWM_CACHE_LOCATION)) *ret = (nawmval)ci.win; else *ret = 0; } void atoi_fun(int argc, nawmval *argv, nawmval *ret) { char *str = (char *)argv[0]; *ret = atol(str); } void beep_cmd(int argc, nawmval *argv) { XBell(dpy, 100); } void cut_cmd(int argc, nawmval *argv) { } void delete_cmd(int argc, nawmval *argv) { Window win = (Window)argv[0]; XClientMessageEvent ev; ev.type = ClientMessage; ev.window = win; ev.message_type = XA_WM_PROTOCOLS; ev.format = 32; ev.data.l[0] = XA_WM_DELETE_WINDOW; ev.data.l[1] = CurrentTime; XSendEvent(dpy, win, False, 0L, (XEvent *)&ev); } void destroy_cmd(int argc, nawmval *argv) { Window win = (Window)argv[0]; XDestroyWindow(dpy, win); } void dheight_fun(int argc, nawmval *argv, nawmval *ret) { Window win = (Window)argv[0]; int dh, dw, h, w; getsize(win, &w, &h, &dw, &dh); *ret = dh; } void dsize_cmd(int argc, nawmval *argv) { Window win = (Window)argv[0]; int w = argv[1], h = argv[2]; do_size(win, w, h, SIZE_DELTA | SIZE_DISCRETE); } void dsizeto_cmd(int argc, nawmval *argv) { Window win = (Window)argv[0]; int w = argv[1], h = argv[2]; do_size(win, w, h, SIZE_DISCRETE); } void dwidth_fun(int argc, nawmval *argv, nawmval *ret) { Window win = (Window)argv[0]; int dh, dw, h, w; getsize(win, &w, &h, &dw, &dh); *ret = dw; } void env_fun(int argc, nawmval *argv, nawmval *ret) { char **val = (char **)ret; char *data; data = getenv((char *)argv[0]); if (!data) *val = gcstrdup(""); else *val = gcstrdup(data); } void exec_cmd(int argc, nawmval *argv) { } void exit_cmd(int argc, nawmval *argv) { quit = 1; } void find_cmd(int argc, nawmval *argv) { Window win = (Window)argv[0]; assign_var(&CURRENTWINDOW, (nawmval)win); } void focuswindow_fun(int argc, nawmval *argv, nawmval *ret) { Window *fw = (Window *)ret; int revert; XGetInputFocus(dpy, fw, &revert); } void free_cmd(int argc, nawmval *argv) { XSetInputFocus(dpy, PointerRoot, None, CurrentTime); } void getled_fun(int argc, nawmval *argv, nawmval *ret) { int led = argv[0]; XKeyboardState state; XGetKeyboardControl(dpy, &state); *ret = (state.led_mask & (1 << (led - 1))) ? 1 : 0; } void getrepeat_fun(int argc, nawmval *argv, nawmval *ret) { char *keyname = (char *)argv[0]; KeyCode kc; XKeyboardState state; kc = parse_key(keyname); XGetKeyboardControl(dpy, &state); *ret = KEYSTATE(state.auto_repeats, kc) ? 1 : 0; } void grab_cmd(int argc, nawmval *argv) { XGrabServer(dpy); } void hasname_fun(int argc, nawmval *argv, nawmval *ret) { Window win = (Window)argv[0]; char *name = (char *)argv[1]; *ret = hasname(win, name); } void height_fun(int argc, nawmval *argv, nawmval *ret) { Window win = (Window)argv[0]; int dw, dh, w, h; getsize(win, &w, &h, &dw, &dh); *ret = h; } void itoa_fun(int argc, nawmval *argv, nawmval *ret) { long num = argv[0]; char **str = (char **)ret; char buf[32]; sprintf(buf, "%ld", num); *str = gcstrdup(buf); } void lower_cmd(int argc, nawmval *argv) { Window win = (Window)argv[0]; XLowerWindow(dpy, win); } void map_cmd(int argc, nawmval *argv) { Window win = (Window)argv[0]; XMapWindow(dpy, win); } void mapped_fun(int argc, nawmval *argv, nawmval *ret) { Window win = (Window)argv[0]; cacheinfo ci; ci.win = win; if (find_window(&ci, NAWM_CACHE_WINDOW | NAWM_CACHE_MAPPED)) *ret = 1; else *ret = 0; } void mouseclick_cmd(int argc, nawmval *argv) { char *but = (char *)argv[0]; int x = (int)argv[1], y = (int)argv[2]; Window rr, pr; int oldx, oldy, rx, ry, mods, button; unsigned int k; void *state; mods = parse_mods(&but); button = parse_button(but); XQueryPointer(dpy, root, &rr, &pr, &rx, &ry, &oldx, &oldy, &k); XTestFakeMotionEvent(dpy, screen, hcoord(x), vcoord(y), CurrentTime); state = set_modifier_state(mods); XTestFakeButtonEvent(dpy, button, True, CurrentTime); XTestFakeButtonEvent(dpy, button, False, CurrentTime); reset_modifier_state(state); XTestFakeMotionEvent(dpy, screen, oldx, oldy, CurrentTime); } void move_cmd(int argc, nawmval *argv) { Window win = (Window)argv[0]; XWindowAttributes attr; XGetWindowAttributes(dpy, manager_window(win), &attr); argv[1] += attr.x; argv[2] += attr.y; moveto_cmd(argc, argv); } void moveto_cmd(int argc, nawmval *argv) { Window win = (Window)argv[0]; int x = argv[1], y = argv[2]; XWindowAttributes attr; if (window_manager == NAWM_WM_VTWM_GAMMA) { XWindowChanges xwc; XGetWindowAttributes(dpy, win, &attr); xwc.width = attr.width; xwc.height = attr.height; win = manager_window(win); XGetWindowAttributes(dpy, win, &attr); xwc.x = x; xwc.y = y; XConfigureWindow(dpy, win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &xwc); } else { win = manager_window(win); XGetWindowAttributes(dpy, win, &attr); XMoveWindow(dpy, win, attr.border_width + x, attr.border_width + y); } } void name_fun(int argc, nawmval *argv, nawmval *ret) { Window win = (Window)argv[0]; char **name = (char **)ret; unsigned long items, left; Atom type; int format; Window cwin = client_window(win); unsigned char *nm; if (XGetWindowProperty(dpy, cwin, XA_WM_NAME, 0, 1000, False, AnyPropertyType, &type, &format, &items, &left, &nm) != 0) *name = gcstrdup(""); else { *name = gcstrdup((char *)nm); XFree(nm); } } void pick_fun(int argc, nawmval *argv, nawmval *ret) { Cursor cursor; int status, buttons = 0; XEvent event; Window *w = (Window *)ret; *w = None; cursor = XCreateFontCursor(dpy, XC_crosshair); status = XGrabPointer(dpy, root, False, ButtonPressMask | ButtonReleaseMask, GrabModeSync, GrabModeAsync, root, cursor, CurrentTime); if (status != GrabSuccess) { XBell(dpy, 100); return; } while (*w == None || buttons) { XAllowEvents(dpy, SyncPointer, CurrentTime); XWindowEvent(dpy, root, ButtonPressMask | ButtonReleaseMask, &event); switch (event.type) { case ButtonPress: if (*w == None) { if (event.xbutton.subwindow != None) *w = event.xbutton.subwindow; else *w = root; } buttons++; break; case ButtonRelease: if (buttons) buttons--; break; } } assign_var(&CURRENTWINDOW, (nawmval)*w); } void pointerwindow_fun(int argc, nawmval *argv, nawmval *ret) { int px, py; Window *pw = (Window *)ret; querypointer(pw, &px, &py); if (*pw) *pw = client_window(*pw); } void px_fun(int argc, nawmval *argv, nawmval *ret) { Window pw; int py, px; querypointer(&pw, &px, &py); *ret = px; } void py_fun(int argc, nawmval *argv, nawmval *ret) { Window pw; int px, py; querypointer(&pw, &px, &py); *ret = py; } void querypointer(Window *pw, int *px, int *py) { Window rr; int x, y; unsigned int k; XQueryPointer(dpy, root, &rr, pw, px, py, &x, &y, &k); } void put_cmd(int argc, nawmval *argv) { puts((char *)argv[0]); } void raise_cmd(int argc, nawmval *argv) { Window win = (Window)argv[0]; XRaiseWindow(dpy, win); } void refresh_cmd(int argc, nawmval *argv) { XSetWindowAttributes attributes; unsigned long valuemask; Window w; valuemask = CWBackPixel | CWBackingStore | CWSaveUnder | CWOverrideRedirect; attributes.background_pixel = BlackPixel(dpy, screen); attributes.backing_store = NotUseful; attributes.save_under = False; attributes.override_redirect = True; w = XCreateWindow(dpy, root, 0, 0, screenwidth, screenheight, 0, CopyFromParent, CopyFromParent, (Visual *) CopyFromParent, valuemask, &attributes); XMapWindow(dpy, w); XDestroyWindow(dpy, w); XFlush(dpy); } void restart_cmd(int argc, nawmval *argv) { extern char **Argv; XSync(dpy, 0); execvp(Argv[0], Argv); } void setled_cmd(int argc, nawmval *argv) { int led = argv[0], val = argv[1]; XKeyboardControl ctl; ctl.led = led; ctl.led_mode = val ? LedModeOn : LedModeOff; XChangeKeyboardControl(dpy, KBLed | KBLedMode, &ctl); } void setmode_cmd(int argc, nawmval *argv) { set_mode((char *)argv[0]); } void setrepeat_cmd(int argc, nawmval *argv) { char *keyname = (char *)argv[0]; int state = argv[1]; KeyCode kc; XKeyboardControl ctl; kc = parse_key(keyname); ctl.key = kc; ctl.auto_repeat_mode = state ? AutoRepeatModeOn : AutoRepeatModeOff; XChangeKeyboardControl(dpy, KBKey | KBAutoRepeatMode, &ctl); } void size_cmd(int argc, nawmval *argv) { Window win = (Window)argv[0]; int w = argv[1], h = argv[2]; do_size(win, w, h, SIZE_DELTA); } void sizeto_cmd(int argc, nawmval *argv) { Window win = (Window)argv[0]; int w = argv[1], h = argv[2]; do_size(win, w, h, SIZE_EXACT); } void str2win_fun(int argc, nawmval *argv, nawmval *ret) { Window *ans = (Window *)ret; cacheinfo ci; ci.names[0] = (char *)argv[0]; if (find_window(&ci, NAWM_CACHE_NAME | NAWM_CACHE_MAPPED)) *ret = (nawmval)ci.win; else if (find_window(&ci, NAWM_CACHE_NAME)) *ret = (nawmval)ci.win; else *ret = 0; } void sync_cmd(int argc, nawmval *argv) { XSync(dpy, False); } void system_cmd(int argc, nawmval *argv) { char *str = (char *)argv[0]; char *exec_argv[4] = { "sh", "-c", NULL, NULL }; exec_argv[2] = str; switch (fork()) { case 0: execvp("/bin/sh", exec_argv); _exit(1); case -1: XBell(dpy, 100); break; } } void type_cmd(int argc, nawmval *argv) { char *str = (char *)argv[0]; void *state; state = set_modifier_state(0); for (; *str; str++) typechar(*str); reset_modifier_state(state); } void typekey_cmd(int argc, nawmval *argv) { char *keyname = (char *)argv[0]; int mods; KeyCode kc; void *state; mods = parse_mods(&keyname); kc = parse_key(keyname); state = set_modifier_state(mods); XTestFakeKeyEvent(dpy, kc, True, CurrentTime); XTestFakeKeyEvent(dpy, kc, False, CurrentTime); reset_modifier_state(state); } void unfocus_cmd(int argc, nawmval *argv) { XSetInputFocus(dpy, PointerRoot, None, CurrentTime); } void ungrab_cmd(int argc, nawmval *argv) { XUngrabServer(dpy); } void unmap_cmd(int argc, nawmval *argv) { Window win = (Window)argv[0]; XUnmapWindow(dpy, win); } void warp_cmd(int argc, nawmval *argv) { int x = argv[0], y = argv[1]; XWarpPointer(dpy, None, None, 0, 0, 0, 0, x, y); } void warpto_cmd(int argc, nawmval *argv) { int x = argv[0], y = argv[1]; XWarpPointer(dpy, None, root, 0, 0, 0, 0, hcoord(x), vcoord(y)); } void warptowindow_cmd(int argc, nawmval *argv) { Window win = (Window)argv[0]; XWindowAttributes attr; if (!XGetWindowAttributes(dpy, win, &attr)) return; XWarpPointer(dpy, None, win, 0, 0, 0, 0, attr.width / 2, attr.height / 2); assign_var(&CURRENTWINDOW, (nawmval)win); } void width_fun(int argc, nawmval *argv, nawmval *ret) { Window win = (Window)argv[0]; int dw, dh, w, h; getsize(win, &w, &h, &dw, &dh); *ret = w; } void windows_fun(int argc, nawmval *argv, nawmval *ret) { cacheinfo *ci; array *ans; int n; ans = create_array(array_type(T_WIN, T_INT), 10); for (n = 0, ci = cachehead; ci; ci = ci->next) array_insert(ans, n++, ci->win); *ret = (nawmval)ans; } void xloc_fun(int argc, nawmval *argv, nawmval *ret) { Window win = (Window)argv[0]; XWindowAttributes attr; if (!XGetWindowAttributes(dpy, win, &attr)) *ret = 0; else *ret = attr.x; } void yloc_fun(int argc, nawmval *argv, nawmval *ret) { Window win = (Window)argv[0]; XWindowAttributes attr; if (!XGetWindowAttributes(dpy, win, &attr)) *ret = 0; else *ret = attr.y; } void do_size(Window win, int w, int h, int mode) { XWindowAttributes attr; XSizeHints hints; long data; XGetWMNormalHints(dpy, win, &hints, &data); XGetWindowAttributes(dpy, win, &attr); if ((mode & SIZE_DISCRETE) && (hints.flags & PResizeInc)) { w *= hints.width_inc; h *= hints.height_inc; } /* If we're doing a relative resize, add in the current height and width */ if (mode & SIZE_DELTA) { w += attr.width; h += attr.height; } /* Otherwise, add in the base size */ else if (hints.flags & PBaseSize) { w += hints.base_width; h += hints.base_height; } /* If base size isn't specified, it defaults to equal the min size */ else if (hints.flags & PMinSize) { w += hints.min_width; h += hints.min_height; } /* check limits */ if ((((w < hints.min_width) || (h < hints.min_height)) && hints.flags & PMinSize) || (((w > hints.max_width) || (h > hints.max_height)) && hints.flags & PMaxSize)) return; XResizeWindow(dpy, win, w, h); } void getsize(Window win, int *width, int *height, int *dwidth, int *dheight) { XWindowAttributes attr; XSizeHints hints; long data; if (!XGetWindowAttributes(dpy, win, &attr)) { *width = *height = *dwidth = *dheight = 0; return; } *width = *dwidth = attr.width; *height = *dheight = attr.height; if (!XGetWMNormalHints(dpy, win, &hints, &data)) return; if (hints.flags & PBaseSize) { *width = *dwidth = attr.width - hints.base_width; *height = *dheight = attr.height - hints.base_height; } else if (hints.flags & PMinSize) { *width = *dwidth = attr.width - hints.min_width; *height = *dheight = attr.height - hints.min_height; } if (hints.flags & PResizeInc) { *dwidth /= hints.width_inc; *dheight /= hints.height_inc; } }