/* aewm - a minimalist X11 window mananager. vim:sw=4:ts=4:et
 * Copyright 1998-2003 Decklin Foster <decklin@red-bean.com>
 * This program is free software; see LICENSE for details. */

#include <stdarg.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include "aewm.h"

static void quit_nicely(void);

void err(const char *fmt, ...)
{
    va_list argp;

    fprintf(stderr, "aewm: ");
    va_start(argp, fmt);
    vfprintf(stderr, fmt, argp);
    va_end(argp);
    fprintf(stderr, "\n");
}

void fork_exec(char *cmd)
{
    pid_t pid = fork();

    switch (pid) {
        case 0:
            setsid();
            execlp("/bin/sh", "sh", "-c", cmd, NULL);
            err("exec failed, cleaning up child");
            exit(1);
        case -1:
            err("can't fork");
    }
}

void sig_handler(int signum)
{
    switch (signum) {
        case SIGINT:
        case SIGTERM:
        case SIGHUP:
            quit_nicely(); break;
        case SIGCHLD:
            wait(NULL); break;
    }
}

int handle_xerror(Display *dpy, XErrorEvent *e)
{
    /* FIXME: should this be here or not? */
    /* client_t *c = find_client(e->resourceid, WINDOW); */

    if (e->error_code == BadAccess && e->resourceid == root) {
        err("root window unavailible (maybe another wm is running?)");
        exit(1);
    } else {
        char msg[255];
        XGetErrorText(dpy, e->error_code, msg, sizeof msg);
        err("X error (%#lx): %s", e->resourceid, msg);
    }

    /* if (c) remove_client(c, WITHDRAW); */
    return 0;
}

/* Ick. Argh. You didn't see this function. */

int ignore_xerror(Display *dpy, XErrorEvent *e)
{
    return 0;
}

/* Currently, only send_wm_delete uses this one... */

int send_xmessage(Window w, Atom a, long x)
{
    XEvent e;

    e.type = ClientMessage;
    e.xclient.window = w;
    e.xclient.message_type = a;
    e.xclient.format = 32;
    e.xclient.data.l[0] = x;
    e.xclient.data.l[1] = CurrentTime;

    return XSendEvent(dpy, w, False, NoEventMask, &e);
}

void get_mouse_position(int *x, int *y)
{
    Window mouse_root, mouse_win;
    int win_x, win_y;
    unsigned int mask;

    XQueryPointer(dpy, root, &mouse_root, &mouse_win,
        x, y, &win_x, &win_y, &mask);
}

#ifdef DEBUG

/* Bleh, stupid macro names. I'm not feeling creative today. */

#define SHOW_EV(name, memb) \
case name: ev_type = #name; w = e.memb.window; break;
#define SHOW(name) \
case name: return #name;

void show_event(XEvent e)
{
    char *ev_type;
    Window w;
    client_t *c;

    switch (e.type) {
        SHOW_EV(ButtonPress, xbutton)
        SHOW_EV(ButtonRelease, xbutton)
        SHOW_EV(ClientMessage, xclient)
        SHOW_EV(ColormapNotify, xcolormap)
        SHOW_EV(ConfigureNotify, xconfigure)
        SHOW_EV(ConfigureRequest, xconfigurerequest)
        SHOW_EV(CreateNotify, xcreatewindow)
        SHOW_EV(DestroyNotify, xdestroywindow)
        SHOW_EV(EnterNotify, xcrossing)
        SHOW_EV(Expose, xexpose)
        SHOW_EV(MapNotify, xmap)
        SHOW_EV(MapRequest, xmaprequest)
        SHOW_EV(MappingNotify, xmapping)
        SHOW_EV(MotionNotify, xmotion)
        SHOW_EV(PropertyNotify, xproperty)
        SHOW_EV(ReparentNotify, xreparent)
        SHOW_EV(ResizeRequest, xresizerequest)
        SHOW_EV(UnmapNotify, xunmap)
        default:
        if (shape && e.type == shape_event) {
            ev_type = "ShapeNotify"; w = ((XShapeEvent *)&e)->window;
        } else {
            ev_type = "unknown event"; w = None;
        }
        break;
    }

    if ((c = find_client(w, WINDOW))) {
        dump_title(c, ev_type, 'w');
    } else if ((c = find_client(w, FRAME))) {
        dump_title(c, ev_type, 'f');
    } else if (w == root) {
        dump_win(w, ev_type, '?');
    }

}

static const char *show_state(client_t *c)
{
    switch (get_wm_state(c)) {
        SHOW(WithdrawnState)
        SHOW(NormalState)
        SHOW(IconicState)
        default: return "unknown state";
    }
}

static const char *show_grav(client_t *c)
{
    if (!c->size || !(c->size->flags & PWinGravity))
        return "no grav (NW)";

    switch (c->size->win_gravity) {
        SHOW(UnmapGravity)
        SHOW(NorthWestGravity)
        SHOW(NorthGravity)
        SHOW(NorthEastGravity)
        SHOW(WestGravity)
        SHOW(CenterGravity)
        SHOW(EastGravity)
        SHOW(SouthWestGravity)
        SHOW(SouthGravity)
        SHOW(SouthEastGravity)
        SHOW(StaticGravity)
        default: return "unknown grav";
    }
}

void dump_title(client_t *c, const char *label, char flag)
{
    err("%15.15s: %#010lx [%c] %-40.40s", label, c->window, flag, c->name);
}

void dump_win(Window w, const char *label, char flag)
{
    err("%15.15s: %#010lx [%c] %-40.40s", label, w, flag,
        w == root ? "(root window)": "(unknown window)");
}

void dump_info(client_t *c)
{
    err("%28s[i] frame %#0lx, ignore_unmap %d", "",
        c->frame, c->ignore_unmap);
    err("%28s[i] %s, %s", "",
        show_state(c), show_grav(c));
}

void dump_geom(client_t *c, const char *label)
{
    err("%28s[g] %s %dx%d+%d+%d", "",
        label, c->width, c->height, c->x, c->y);
}

void dump_removal(client_t *c, int mode)
{
    err("%28s[r] %s, %d pending", "",
        mode == WITHDRAW ? "withdraw" : "remap", XPending(dpy));
}

void dump_clients()
{
    client_t *c;

    for (c = head_client; c; c = c->next) { 
        dump_title(c, "dump", 'd');
        dump_geom(c, "current");
        dump_info(c);
    }
}
#endif

/* We use XQueryTree here to preserve the window stacking order,
 * since the order in our linked list is different. */

static void quit_nicely(void)
{
    unsigned int nwins, i;
    Window dummyw1, dummyw2, *wins;
    client_t *c;

    XQueryTree(dpy, root, &dummyw1, &dummyw2, &wins, &nwins);
    for (i = 0; i < nwins; i++) {
        c = find_client(wins[i], FRAME);
        if (c) remove_client(c, REMAP);
    }
    XFree(wins);

    XFreeFont(dpy, font);
#ifdef XFT
    XftFontClose(dpy, xftfont);
#endif
    XFreeCursor(dpy, move_curs);
    XFreeCursor(dpy, resize_curs);
    XFreeGC(dpy, invert_gc);
    XFreeGC(dpy, border_gc);
    XFreeGC(dpy, string_gc);

    XInstallColormap(dpy, DefaultColormap(dpy, screen));
    XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);

    XCloseDisplay(dpy);
    exit(0);
}
