#include "Manager.h"
#include "Client.h"
#include <string.h>
#include <X11/Xproto.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "Cursors.h"
#include <X11/keysym.h>
#include <gnome.h>

#define ACOUNT(x) (sizeof(x)/sizeof(x[0]))

void initAtoms() {}

Boolean WindowManager::m_looping = True;
char *nice_font;
bool click_to_focus;
bool raise_on_focus;
bool auto_raise;
unsigned int auto_raise_delay;
unsigned int CONFIG_POINTER_STOPPED_DELAY = 80;
char *border_fg;
char *frame_bg;
char *button_bg;
unsigned int title_size;
unsigned int frame_size;
Atom Atoms::wm_state;
Atom Atoms::wm_changeState;
Atom Atoms::wm_protocols;
Atom Atoms::wm_delete;
Atom Atoms::wm_takeFocus;
Atom Atoms::wm_colormaps;
Atom Atoms::wm2_running;
Atom Atoms::win_protocols;
Atom Atoms::win_workspace;
Atom Atoms::win_workspace_count;
Atom Atoms::win_workspace_names;
Atom Atoms::win_workarea;
Atom Atoms::win_icons;
Atom Atoms::win_state;
Atom Atoms::win_layer;
Atom Atoms::win_hints;
Atom Atoms::win_supporting_wm_check;
Atom Atoms::win_client_list;
Atom Atoms::win_desktop_button_proxy;
Atom Atoms::win_area;
Atom Atoms::win_area_count;
Atom Atoms::mwm_hints;
int     WindowManager::m_signalled = False;
Boolean WindowManager::m_initialising = False;
Boolean ignoreBadWindowErrors;

implementPList(ClientList, Client);

WindowManager::WindowManager() : m_focusChanging(False) {
    fprintf(stderr, "\nwmG: Copyright (c) 1999 Scott Barnes.\nDerived from wm2, created for Gnome, and written in C++\n\n");
    m_display = XOpenDisplay(NULL);
    if(!m_display)
        fatal("can't open display");
    m_initialising = True;
    XSetErrorHandler(errorHandler);
    ignoreBadWindowErrors = True;
    signal(SIGTERM, sigHandler);
    signal(SIGINT,  sigHandler);
    signal(SIGHUP,  sigHandler);
    m_currentTime = -1;
    m_activeClient = 0;
    Atoms::wm_state = XInternAtom(m_display, "WM_STATE", False);
    Atoms::wm_changeState = XInternAtom(m_display, "WM_CHANGE_STATE", False);
    Atoms::wm_protocols = XInternAtom(m_display, "WM_PROTOCOLS", False);
    Atoms::wm_delete = XInternAtom(m_display, "WM_DELETE_WINDOW", False);
    Atoms::wm_takeFocus = XInternAtom(m_display, "WM_TAKE_FOCUS", False);
    Atoms::wm_colormaps = XInternAtom(m_display, "WM_COLORMAP_WINDOWS", False);
    Atoms::wm2_running = XInternAtom(m_display, "_WM2_RUNNING", False);
    Atoms::win_protocols = XInternAtom(m_display, "_WIN_PROTOCOLS", False);
    Atoms::win_workspace = XInternAtom(m_display, "_WIN_WORKSPACE", False);
    Atoms::win_workspace_count = XInternAtom(m_display, "_WIN_WORKSPACE_COUNT", False);
    Atoms::win_workspace_names = XInternAtom(m_display, "_WIN_WORKSPACE_NAMES", False);
    Atoms::win_workarea = XInternAtom(m_display, "_WIN_WORKAREA", False);
    Atoms::win_icons = XInternAtom(m_display, "_WIN_ICONS", False);
    Atoms::win_state = XInternAtom(m_display, "_WIN_STATE", False);
    Atoms::win_layer = XInternAtom(m_display, "_WIN_LAYER", False);
    Atoms::win_hints = XInternAtom(m_display, "_WIN_HINTS", False);
    Atoms::win_supporting_wm_check = XInternAtom(m_display, "_WIN_SUPPORTING_WM_CHECK", False);
    Atoms::win_client_list = XInternAtom(m_display, "_WIN_CLIENT_LIST", False);
    Atoms::win_desktop_button_proxy = XInternAtom(m_display, "_WIN_DESKTOP_BUTTON_PROXY", False);
    Atoms::win_area = XInternAtom(m_display, "_WIN_AREA", False);
    Atoms::win_area_count = XInternAtom(m_display, "_WIN_AREA_COUNT", False);
    Atoms::mwm_hints = XInternAtom(m_display, "_MOTIF_WM_HINTS", False);
    click_to_focus = gnome_config_get_bool("/wmG/properties/CLICK_TO_FOCUS=True");
    if(click_to_focus == False)
        raise_on_focus = gnome_config_get_bool("/wmG/properties/RAISE_ON_FOCUS=True");
    auto_raise = gnome_config_get_bool("/wmG/properties/AUTO_RAISE=False");
    auto_raise_delay = gnome_config_get_int("/wmG/properties/AUTO_RAISE_DELAY=400");
    title_size = gnome_config_get_int("/wmG/properties/TITLEBAR_SIZE=12");
    frame_size = gnome_config_get_int("/wmG/properties/FRAME_SIZE=2");
    border_fg = gnome_config_get_string("/wmG/properties/BORDER_COLOR=black");
    frame_bg = gnome_config_get_string("/wmG/properties/FRAME_BACKGROUND=gray95");
    button_bg = gnome_config_get_string("/wmG/properties/BUTTON_BACKGROUND=gray95");
    int dummy;
    if(!XShapeQueryExtension(m_display, &m_shapeEvent, &dummy))
        fatal("no shape extension, can't run without it");
    initialiseScreen();
    fprintf(stderr, "wmG: root window is %lx\n", m_root);
    unsigned int i = 0;
    Atom win_proto[11];
    win_proto[i++] = Atoms::win_workspace;
    win_proto[i++] = Atoms::win_workspace_count;
    win_proto[i++] = Atoms::win_workspace_names;
    win_proto[i++] = Atoms::win_icons;
    win_proto[i++] = Atoms::win_workarea;
    win_proto[i++] = Atoms::win_state;
    win_proto[i++] = Atoms::win_hints;
    win_proto[i++] = Atoms::win_layer;
    win_proto[i++] = Atoms::win_supporting_wm_check;
    win_proto[i++] = Atoms::win_client_list;
    win_proto[i++] = Atoms::win_desktop_button_proxy;
    XChangeProperty(display(), m_root, Atoms::win_protocols, XA_ATOM, 32, PropModeReplace, (unsigned char *)win_proto, i);
    XSetWindowAttributes attr;
    int mask = CWOverrideRedirect;
    attr.override_redirect = TRUE;
    root_proxy = XCreateWindow(display(), root(), -5, -5, 5, 5, 0, CopyFromParent, InputOutput, CopyFromParent, mask, &attr);
    XSetWMProtocols(display(), root_proxy, &Atoms::wm_delete, 1);
    XMapWindow(display(), root_proxy);
    XChangeProperty(display(), m_root, Atoms::win_supporting_wm_check, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&root_proxy, 1);
    XChangeProperty(display(), root_proxy, Atoms::win_supporting_wm_check, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&root_proxy, 1);
    XChangeProperty(display(), m_root, Atoms::win_desktop_button_proxy, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)&root_proxy, 1);
    XChangeProperty(display(), root_proxy, Atoms::win_desktop_button_proxy, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)&root_proxy, 1);
    XSync(m_display, False);
    m_initialising = False;
    m_returnCode = 0;
    clearFocus();
    scanInitialWindows();
    loop();
}

WindowManager::~WindowManager() {}

void WindowManager::release() {
    if(m_returnCode != 0)
        return;
    ClientList normalList, unparentList;
    Client *c;
    int i;
    for(i = 0; i < m_clients.count(); ++i) {
        c = m_clients.item(i);
        if(c->isNormal())
	    normalList.append(c);
        else
	    unparentList.append(c);
    }
    for(i = normalList.count()-1; i >= 0; --i) {
        unparentList.append(normalList.item(i));
    }
    m_clients.remove_all();
    for(i = 0; i < unparentList.count(); ++i) {
        unparentList.item(i)->unreparent();
        unparentList.item(i)->release();
        unparentList.item(i) = 0;
    }
    XSetInputFocus(m_display, PointerRoot, RevertToPointerRoot, timestamp(False));
    installColormap(None);
    XFreeCursor(m_display, m_cursor);
    XFreeCursor(m_display, m_xCursor);
    XFreeCursor(m_display, m_vCursor);
    XFreeCursor(m_display, m_hCursor);
    XFreeCursor(m_display, m_vhCursor);
    XCloseDisplay(m_display);
}

void WindowManager::fatal(const char *message) {
    fprintf(stderr, "wmG: ");
    perror(message);
    fprintf(stderr, "\n");
    exit(1);
}

int WindowManager::errorHandler(Display *d, XErrorEvent *e) {
    if(m_initialising && (e->request_code == X_ChangeWindowAttributes) && e->error_code == BadAccess) {
        fprintf(stderr, "wmG: another window manager running?\n");
        exit(1);
    }
    if(ignoreBadWindowErrors == True && e->error_code == BadWindow)
        return 0;
    char msg[100], number[30], request[100];
    XGetErrorText(d, e->error_code, msg, 100);
    sprintf(number, "%d", e->request_code);
    XGetErrorDatabaseText(d, "XRequest", number, "", request, 100);
    if(request[0] == '\0')
        sprintf(request, "<request-code-%d>", e->request_code);
    fprintf(stderr, "wmG: %s (0x%lx): %s\n", request, e->resourceid, msg);
    if(m_initialising) {
        fprintf(stderr, "wmG: failure during initialisation, abandoning\n");
        exit(1);
    }
    return 0;
}

static Cursor makeCursor(Display *d, Window w, unsigned char *bits, unsigned char *mask_bits, int width, int height, int xhot, int yhot, XColor *fg, XColor *bg) {
    Pixmap pixmap = XCreateBitmapFromData(d, w, (const char *)bits, width, height);
    Pixmap mask = XCreateBitmapFromData(d, w, (const char *)mask_bits, width, height);
    Cursor cursor = XCreatePixmapCursor(d, pixmap, mask, fg, bg, xhot, yhot);
    XFreePixmap(d, pixmap);
    XFreePixmap(d, mask);
    return cursor;
}

void WindowManager::initialiseScreen() {
    int i = 0;
    m_screenNumber = i;
    m_root = RootWindow(m_display, i);
    m_defaultColormap = DefaultColormap(m_display, i);
    m_minimumColormaps = MinCmapsOfScreen(ScreenOfDisplay(m_display, i));
    XColor black, white, temp;
    if(!XAllocNamedColor(m_display, m_defaultColormap, "black", &black, &temp))
        fatal("couldn't load colour \"black\"!");
    if(!XAllocNamedColor(m_display, m_defaultColormap, "white", &white, &temp))
        fatal("couldn't load colour \"white\"!");
    m_cursor = makeCursor(m_display, m_root, cursor_bits, cursor_mask_bits, cursor_width, cursor_height, cursor_x_hot, cursor_y_hot, &black, &white);
    m_xCursor = makeCursor(m_display, m_root, ninja_cross_bits, ninja_cross_mask_bits, ninja_cross_width, ninja_cross_height, ninja_cross_x_hot, ninja_cross_y_hot, &black, &white);
    m_hCursor = makeCursor(m_display, m_root, cursor_right_bits, cursor_right_mask_bits, cursor_right_width, cursor_right_height, cursor_right_x_hot, cursor_right_y_hot, &black, &white);
    m_vCursor = makeCursor(m_display, m_root, cursor_down_bits, cursor_down_mask_bits, cursor_down_width, cursor_down_height, cursor_down_x_hot, cursor_down_y_hot, &black, &white);
    m_vhCursor = makeCursor(m_display, m_root, cursor_down_right_bits, cursor_down_right_mask_bits, cursor_down_right_width, cursor_down_right_height, cursor_down_right_x_hot, cursor_down_right_y_hot, &black, &white);
    XSetWindowAttributes attr;
    attr.cursor = m_cursor;
    attr.event_mask = SubstructureRedirectMask | SubstructureNotifyMask | ColormapChangeMask | KeymapNotify | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PropertyChangeMask;
    XChangeWindowAttributes(m_display, m_root, CWCursor | CWEventMask, &attr);
    XSync(m_display, False);
}

unsigned long WindowManager::allocateColour(char *name, char *desc) {
    XColor nearest, ideal;
    if(!XAllocNamedColor(display(), DefaultColormap(display(), m_screenNumber), name, &nearest, &ideal)) {
        char error[100];
        sprintf(error, "couldn't load %s colour", desc);
        fatal(error);
    } else return nearest.pixel;
}

void WindowManager::installCursor(RootCursor c) {
    installCursorOnWindow(c, m_root);
}

void WindowManager::installCursorOnWindow(RootCursor c, Window w) {
    XSetWindowAttributes attr;
    switch(c) {
        case DeleteCursor:
	    attr.cursor = m_xCursor;
            break;
        case DownCursor:
	    attr.cursor = m_vCursor;
            break;
        case RightCursor:
	    attr.cursor = m_hCursor;
            break;
        case DownrightCursor:
	    attr.cursor = m_vhCursor;
            break;
        case NormalCursor:
	    attr.cursor = m_cursor;
            break;
    }
    XChangeWindowAttributes(m_display, w, CWCursor, &attr);
}


Time WindowManager::timestamp(Boolean reset) {
    if(reset)
        m_currentTime = CurrentTime;
    if(m_currentTime == CurrentTime) {
        XEvent event;
        XChangeProperty(m_display, m_root, Atoms::wm2_running, Atoms::wm2_running, 8, PropModeAppend, (unsigned char *)"", 0);
        XMaskEvent(m_display, PropertyChangeMask, &event);
        m_currentTime = event.xproperty.time;
    }
    return m_currentTime;
}

void WindowManager::sigHandler() {
    m_looping = False;
}

void WindowManager::scanInitialWindows() {
    unsigned int i, n;
    Window w1, w2, *wins;
    XWindowAttributes attr;
    XQueryTree(m_display, m_root, &w1, &w2, &wins, &n);
    for (i = 0; i < n; i++) {
        XGetWindowAttributes(m_display, wins[i], &attr);
        if(!(attr.override_redirect))
            (void)windowToClient(wins[i], True);
    }
    XFree((void *)wins);
}

Client *WindowManager::windowToClient(Window w, Boolean create) {
    if(w == 0) return 0;
    for(int i = m_clients.count()-1; i >= 0; --i) {
        if(m_clients.item(i)->hasWindow(w)) {
            return m_clients.item(i);
        }
    }
    if(!create)
        return 0;
    else {
        Client *newC = new Client(this, w);
        m_clients.append(newC);
        return newC;
    }
}

void WindowManager::installColormap(Colormap cmap) {
    if(cmap == None) {
        XInstallColormap(m_display, m_defaultColormap);
    } else {
        XInstallColormap(m_display, cmap);
    }
}

void WindowManager::clearFocus() {
    static Window w = 0;
    Client *active = activeClient();
    if(auto_raise || !click_to_focus) {
        setActiveClient(0);
        return;
    }
    if(active) {
        setActiveClient(0);
        active->deactivate();
        for(Client *c = active->revertTo(); c; c = c->revertTo()) {
            if(c->isNormal()) {
                c->activate();
                return;
            }
        }
        installColormap(None);
    }
    if(w == 0) {
        XSetWindowAttributes attr;
        int mask = CWOverrideRedirect;
        attr.override_redirect = 1;
        w = XCreateWindow(display(), root(), 0, 0, 1, 1, 0, CopyFromParent, InputOnly, CopyFromParent, mask, &attr);
        XMapWindow(display(), w);
    }
    XSetInputFocus(display(), w, RevertToPointerRoot, timestamp(False));
}

void WindowManager::skipInRevert(Client *c, Client *myRevert) {
    for(int i = 0; i < m_clients.count(); ++i) {
        if(m_clients.item(i) != c && m_clients.item(i)->revertTo() == c) {
            m_clients.item(i)->setRevertTo(myRevert);
        }
    }
}

void WindowManager::addToHiddenList(Client *c) {
    for(int i = 0; i < m_hiddenClients.count(); ++i) {
        if(m_hiddenClients.item(i) == c)
	    return;
    }

    m_hiddenClients.append(c);
}

void WindowManager::removeFromHiddenList(Client *c) {
    for(int i = 0; i < m_hiddenClients.count(); ++i) {
        if(m_hiddenClients.item(i) == c) {
            m_hiddenClients.remove(i);
            return;
        }
    }
}


Boolean WindowManager::raiseTransients(Client *c) {
    Client *first = 0;
    if(!c->isNormal())
        return False;
    for(int i = 0; i < m_clients.count(); ++i) {
        if(m_clients.item(i)->isNormal() && m_clients.item(i)->isTransient()) {
            if(c->hasWindow(m_clients.item(i)->transientFor())) {
                if(!first)
		    first = m_clients.item(i);
                else
		    m_clients.item(i)->mapRaised();
            }
        }
    }
    if(first) {
        first->mapRaised();
        return True;
    } else {
        return False;
    }
}

void WindowManager::updateClientList() {
    long w;
    XID *ids;
    ids = new XID [m_clients.count()];
    w = 0;
    for(long i = 0; i < m_clients.count(); i++)
        if(m_clients.item(i)->managed() && !m_clients.item(i)->isWithdrawn() && m_clients.item(i)->handle() != root_proxy)
            ids[w++] = m_clients.item(i)->handle();
    XChangeProperty(display(), m_root, Atoms::win_client_list, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)ids, w);
    delete ids;
}


syntax highlighted by Code2HTML, v. 0.9.1