/* Window.cc

Copyright © 2003 David Reveman.

This file is part of Waimea.

Waimea 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.

Waimea 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 Waimea; see the file COPYING. If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */

#ifdef    HAVE_CONFIG_H
#  include "../config.h"
#endif // HAVE_CONFIG_H

extern "C" {
#ifdef    HAVE_STDIO_H
#  include <stdio.h>
#endif // HAVE_STDIO_H

#ifdef    STDC_HEADERS
#  include <string.h>
#endif // STDC_HEADERS

#ifdef    LIMITS_H
#  include <limits.h>
#endif // LIMITS_H

}

#include "Window.hh"

WaWindow::WaWindow(Window win_id, WaScreen *scrn) :
    AWindowObject(NULL, win_id, WindowType, NULL, "client") {
    XWindowAttributes init_attrib;
    XSetWindowAttributes attrib_set;
    
    ws = scrn;
    display = ws->display;
    screen_number = ws->screen_number;
    waimea = ws->waimea;
    net = waimea->net;
    wm_strut = NULL;
    move_resize = false;
    name = WA_STRDUP("");
    wclass = WA_STRDUP("");
    wclassname = WA_STRDUP("");
    host = WA_STRDUP("");
    pid = WA_STRDUP("");
    realnamelen = 0;
    master = NULL;
    init_done = remap = outline_state = false;
    window_group = (Window) 0;
    transient_for = (Window) 0;
    wm_icon_image = wm_icon_svg = NULL;
    protocol_mask = 0;
    input_field = true;
    urgent = false;
    functions = FunctionAllMask;
    pending_unmaps = 0;

    attrib_set.event_mask = PropertyChangeMask | StructureNotifyMask |
        FocusChangeMask | EnterWindowMask | LeaveWindowMask;
    attrib_set.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask |
        ButtonMotionMask;
    
    init_attrib.border_width = 0;
    init_attrib.colormap = ws->colormap;
    init_attrib.win_gravity = NorthWestGravity;
    init_attrib.x = 0;
    init_attrib.y = 0;
    init_attrib.width = 1;
    init_attrib.height = 1;

    XChangeWindowAttributes(display, id, CWEventMask | CWDontPropagate,
                            &attrib_set);
    XGetWindowAttributes(display, id, &init_attrib);
    XSetWindowBorderWidth(display, id, 0);
    
    old_bw = init_attrib.border_width;
    attrib.colormap = init_attrib.colormap;
    size.win_gravity = init_attrib.win_gravity;
    attrib.x = init_attrib.x;
    attrib.y = init_attrib.y;
    attrib.width  = init_attrib.width;
    attrib.height = init_attrib.height;
    
    want_focus = mapped = dontsend = deleted = hidden = false;

    desktop_mask = (1L << ws->current_desktop->number);
    
    top_spacing = bottom_spacing = left_spacing = right_spacing = 0;
    has_focus = mergedback = false;
    wstate = StateFocusableMask | StateTasklistMask |
        StateDecorTitleMask | StateDecorBorderMask |
        StateDecorHandlesMask | StateDecorAllMask | StateUserTimeMask;
    old_wstate = wstate;
    decormask = DecorAllMask;
    transient_for = (Window) 0;
    mergemode = NullMergeType;
    frame = NULL;

    net->getWMProtocols(this);
    net->getClassHint(this);
    if (! net->getNetName(this)) net->getXaName(this);
    net->getWMClientMachineHint(this);
    net->getWmPid(this);

    char namechar_tmp = name[realnamelen];
    name[realnamelen] = '\0';

    char window_id_str[32];
    snprintf(window_id_str, 32, "0x%lx", id);
    rid = WA_STRDUP(window_id_str);
    
    WaStringMap *sm = new WaStringMap();
    sm->add(WindowIDName, name);
    sm->add(WindowIDClassName, wclassname);
    sm->add(WindowIDClass, wclass);
    sm->add(WindowIDPID, pid);
    sm->add(WindowIDHost, host);
    sm->add(WindowIDWinID, rid);
    resetActionList(sm);
    
    frame = new WaFrameWindow(this, sm->ref());

#ifdef    SHAPE
    shaped = false;
    shapeinfo = new ShapeInfo(id);
    frame->addShapeInfo(shapeinfo);
#endif // SHAPE

    frame->commonStyleUpdate();

    name[realnamelen] = namechar_tmp;

    waimea->window_table.insert(make_pair(id, this));
    
    if (deleted)
        goto window_create_end;

    net->getWMHints(this);
    net->getWMNormalHints(this);
    initPosition();
    net->getTransientForHint(this);
    net->getWmState(this);
    net->getMWMHints(this);
    net->getWmType(this);
    net->getWmIconImage(this);
    net->getWmIconSvg(this);
    net->getVirtualPos(this);
    net->getWmStrut(this);
    net->getDesktop(this);
    net->setDesktop(this);
    net->setDesktopMask(this);

    calcSpacing();
    gravitate(ApplyGravity);
   
    reparentWin();
    updateGrabs();

    if (deleted) 
        goto window_create_end;
    
    updateAllAttributes();

    if (wstate & StateShadedMask) shade();
    
    ws->wawindow_list.push_back(this);
    ws->wawindow_list_map_order.push_back(this);
    if (! (wstate & (StateAlwaysOnTopMask | StateAlwaysAtBottomMask)))
        frame->setStacking(NormalStackingType);
    
    ws->raiseWindow(frame->id);
    net->setAllowedActions(this);
    net->sendNotify(this, CreateWindowNotify);
    net->setWmState(this);
    
    window_create_end:
    init_done = true;
    drawDecor();
    windowStateCheck();
}

WaWindow::~WaWindow(void) {
    explode();
    if (master) master->unmerge(this);
    
    XEvent ev;
    waimea->window_table.erase(id);

    if (! waimea->running)
        remap = true;

    if (wm_icon_image) wm_icon_image->unref();
    if (wm_icon_svg) wm_icon_svg->unref();
    
    net->sendNotify(this, DestroyWindowNotify);

    withdrawTransient();

    gravitate(RemoveGravity);
    if (wstate & StateShadedMask) attrib.height = restore_shade;
    if (attrib.x >= (int) ws->width)
        attrib.x = attrib.x % ws->width;
    if (attrib.y >= (int) ws->height)
        attrib.y = attrib.y % ws->height;
    
    if (attrib.x + attrib.width <= 0)
        attrib.x = ws->width + (attrib.x % ws->width);
    if (attrib.y + attrib.height <= 0)
        attrib.y = ws->height + (attrib.y % ws->height);

    if ((! deleted) && validate_window_mapped(id)) {
        XChangeSaveSet(display, id, SetModeDelete);
        XSelectInput(display, id, NoEventMask);
        XSelectInput(display, frame->id, NoEventMask);
        XUnmapWindow(display, frame->id);
        XUnmapWindow(display, id);
        XSetWindowBorderWidth(display, id, old_bw);
        if (XCheckTypedWindowEvent(display, id, ReparentNotify, &ev))
            remap = true;
        else
            XReparentWindow(display, id, ws->id, attrib.x, attrib.y);
        
        if (remap) XMapWindow(display, id);

        net->removeAllowedActions(this);
        net->removeVisibleName(this);
    }

    ws->smartNameRemove(this);
    delete [] name;
    delete [] wclass;
    delete [] wclassname;
    delete [] host;
    delete [] pid;
    delete [] rid;

    ws->wawindow_list.remove(this);
    ws->wawindow_list_map_order.remove(this);
    if (wm_strut) {
        ws->strut_list.remove(wm_strut);
        delete wm_strut;
        if (! ws->shutdown) ws->updateWorkarea();
    }

#ifdef    SHAPE
    frame->removeShapeInfo(shapeinfo);
    delete shapeinfo;
#endif // SHAPE
    
    delete frame;

    waimea->removeFromFocusHistory(id);

    if (! ws->shutdown) {
        if (has_focus) waimea->focusRevertFrom(ws, id);
        ws->net->setClientList(ws);
        ws->net->setClientListStacking(ws);
    }
}

void WaWindow::withdrawTransient(void) {
    if (transient_for) {
        if (transient_for == ws->id) {
            list<WaWindow *>::iterator it = ws->wawindow_list.begin();
            for (; it != ws->wawindow_list.end(); ++it)
                if ((*it) != this) (*it)->transients.remove(id);
        }
        else {
            WaWindow *transfor = (WaWindow *)
                waimea->findWin(transient_for, WindowType);
            if (transfor)
                transfor->transients.remove(id);
            else if (window_group) {
                list<WaWindow *>::iterator it = ws->wawindow_list.begin();
                for (;it != ws->wawindow_list.end(); ++it) {
                    if ((*it) != this &&
                        (*it)->window_group == window_group &&
                        (*it)->transient_for == (Window) 0)
                        (*it)->transients.remove(id);
                }
            }
        }
    }
    transient_for = (Window) 0;
}

void WaWindow::calcSpacing(void) {
    double _top_spacing, _bottom_spacing,_left_spacing, _right_spacing;

    calc_length(frame->style->top_spacing, frame->style->top_spacing_u,
                ws->vdpi, ws->height, &_top_spacing);
    calc_length(frame->style->bottom_spacing, frame->style->bottom_spacing_u,
                ws->vdpi, ws->height, &_bottom_spacing);
    calc_length(frame->style->left_spacing, frame->style->left_spacing_u,
                ws->hdpi, ws->width, &_left_spacing);
    calc_length(frame->style->right_spacing, frame->style->right_spacing_u,
                ws->hdpi, ws->width, &_right_spacing);

    top_spacing = WA_ROUND_U(_top_spacing);
    bottom_spacing = WA_ROUND_U(_bottom_spacing);
    left_spacing = WA_ROUND_U(_left_spacing);
    right_spacing = WA_ROUND_U(_right_spacing);
}

void WaWindow::gravitate(int multiplier) {
    switch (size.win_gravity) {
        case NorthWestGravity:
            attrib.x += multiplier * (left_spacing + right_spacing);
        case NorthEastGravity:
            attrib.x -= multiplier * right_spacing;
        case NorthGravity:
            attrib.y += multiplier * top_spacing;
            break;
        case SouthWestGravity:
            attrib.x += multiplier * (left_spacing + right_spacing);
        case SouthEastGravity:
            attrib.x -= multiplier * right_spacing;
        case SouthGravity:
            attrib.y -= multiplier * bottom_spacing;
            break;
        case CenterGravity:
            attrib.x += multiplier * ((left_spacing + right_spacing) / 2);
            attrib.y += multiplier * ((top_spacing + bottom_spacing) / 2);
            break;
        case WestGravity:
            attrib.x += multiplier * left_spacing;
            break;
        case EastGravity:
            attrib.x -= multiplier * right_spacing;
        case StaticGravity:
            break;
    }
}

void WaWindow::initPosition(void) {
    if (size.min_width > attrib.width) attrib.width = size.min_width;
    if (size.min_height > attrib.height) attrib.height = size.min_height;
    restore_max.x = attrib.x;
    restore_max.y = attrib.y;
    restore_max.width = attrib.width;
    restore_shade = restore_max.height = attrib.height;
    restore_max.misc0 = restore_max.misc1 = 0;
    old_attrib.x = old_attrib.y = INT_MIN;
    old_attrib.height = old_attrib.width = INT_MAX;
}

void WaWindow::mapWindow(void) {
    if (deleted) return;

    XMapWindow(display, id);
    
    if (desktop_mask & (1L << ws->current_desktop->number)) {
        if (! master) frame->mapRequest = true;
    } else {
        hidden = true;
    }
    mapped = true;
}

void WaWindow::show(void) {
    if ((! (wstate & StateMinimizedMask)) && hidden && mapped && (! master)) {
        XMapWindow(display, id);
        frame->mapRequest = true;
        hidden = false;
        frame->pushRenderEvent();
    }
}

void WaWindow::hide(void) {
    if (! hidden) {
        if (has_focus) waimea->focusRevertFrom(ws, id);
        XUnmapWindow(display, frame->id);
        XUnmapWindow(display, id);
        pending_unmaps++;
        hidden = true;
    }
}

void WaWindow::updateAllAttributes(void) {
    if (master) { master->updateAllAttributes(); return; }

    old_attrib.x = old_attrib.y = INT_MIN;
    old_attrib.height = old_attrib.width = INT_MAX;

    gravitate(RemoveGravity);
    calcSpacing();
    gravitate(ApplyGravity);

    XMoveWindow(display, id, left_spacing, top_spacing);

#ifdef    SHAPE
    frame->apply_shape = false;
    shapeinfo->setShapeOffset(left_spacing, top_spacing);
    frame->shapeUpdateNotify();
#endif // SHAPE

    int m_x, m_y, m_w, m_h;
    if (wstate & StateMaximizedMask) {
        m_x = restore_max.x;
        m_y = restore_max.y;
        m_w = restore_max.width;
        m_h = restore_max.height;
        wstate &= ~StateMaximizedMask;
        _maximize(restore_max.misc0, restore_max.misc1);
        restore_max.x = m_x;
        restore_max.y = m_y;
        restore_max.width = m_w;
        restore_max.height = m_h;
    } else
        redrawWindow();
}

void WaWindow::redrawWindow(bool force_if_viewable) {
    if (master) {
        sendcf = false;
        master->redrawWindow(force_if_viewable);
        if (! sendcf) {
            net->setVirtualPos(this);
            sendConfig();
        }
        return;
    }
    bool move = false, resizew = false, resizeh = false;

    if (old_attrib.x != attrib.x) {
        frame->attrib.x = attrib.x - left_spacing;
        old_attrib.x = attrib.x;
        move = true;
    }
    if (old_attrib.y != attrib.y) {
        frame->attrib.y = attrib.y - top_spacing;
        old_attrib.y = attrib.y;
        move = true;
    }
    if (old_attrib.width != attrib.width) {
        frame->attrib.width = left_spacing + attrib.width + right_spacing;
        old_attrib.width = attrib.width;

        list<WaWindow *>::iterator it = merged.begin();
        for (; it != merged.end(); it++) {
            if ((*it)->mergetype == VertMergeType)
                frame->attrib.width += (*it)->attrib.width;
        }

        resizew = true;
    }
    if (old_attrib.height != attrib.height) {
        frame->attrib.height = top_spacing + attrib.height + bottom_spacing;
        if (! (wstate & StateShadedMask)) {
            list<WaWindow *>::iterator it = merged.begin();
            for (; it != merged.end(); it++) {
                if ((*it)->attrib.height < 1) (*it)->attrib.height = 1;
                if ((*it)->mergetype == HorizMergeType)
                    frame->attrib.height += (*it)->attrib.height;
            }
        }
        old_attrib.height = attrib.height;
        
        resizeh = true;
    }
    if (move) {
        if (wstate & StateMaximizedMask) {
            restore_max.misc0 = ws->v_x + frame->attrib.x;
            restore_max.misc1 = ws->v_y + frame->attrib.y;
            net->setWmState(this);
        }
        if (! (resizew || resizeh))
            XMoveWindow(display, frame->id, frame->attrib.x, frame->attrib.y);
    }

    if ((wstate & StateMaximizedMask) &&
        (resizew || (resizeh && (! (wstate & StateShadedMask))))) {
        wstate &= ~StateMaximizedMask;
        net->setWmState(this);
    }

    if (resizew || resizeh) {
        if (wstate & StateShadedMask)
            XResizeWindow(display, id, attrib.width, restore_shade);
        else
            XResizeWindow(display, id, attrib.width, attrib.height);

        XMoveResizeWindow(display, frame->id, frame->attrib.x,
                          frame->attrib.y, frame->attrib.width,
                          frame->attrib.height);

#ifdef    SHAPE
        XResizeWindow(display, frame->apply_shape_buffer,
                      frame->attrib.width, frame->attrib.height);
        
        if (! shaped) {
            frame->apply_shape = false;
            frame->shapeUpdateNotify();
        }
#endif // SHAPE        

        if (! (wstate & StateShadedMask)) {
            int cx = left_spacing + attrib.width;
            int cy = top_spacing + attrib.height;
            list<WaWindow *>::iterator mit = merged.begin();
            for (; mit != merged.end(); mit++) {
                Window wd;
                switch ((*mit)->mergetype) {
                    case VertMergeType:
                        (*mit)->attrib.height = cy - top_spacing;
                        XMoveResizeWindow(display, (*mit)->id, cx,
                                          top_spacing,
                                          (*mit)->attrib.width,
                                          (*mit)->attrib.height);
                        cx += (*mit)->attrib.width;
                        XTranslateCoordinates(display, (*mit)->id, ws->id,
                                              0, 0, &(*mit)->attrib.x,
                                              &(*mit)->attrib.y, &wd);
                        
                        break;
                    case HorizMergeType:
                        if (! (wstate & StateShadedMask)) {
                            (*mit)->attrib.width = cx - left_spacing;
                            XMoveResizeWindow(display, (*mit)->id,
                                              left_spacing,
                                              cy, (*mit)->attrib.width,
                                              (*mit)->attrib.height);
                            cy += (*mit)->attrib.height;
                            XTranslateCoordinates(display, (*mit)->id,
                                                  ws->id,
                                                  0, 0, &(*mit)->attrib.x,
                                                  &(*mit)->attrib.y, &wd);
                        }
                        break;
                    case CloneMergeType:
                        (*mit)->attrib.width = attrib.width;
                        (*mit)->attrib.height = attrib.height;
                        XMoveResizeWindow(display, (*mit)->id,
                                          left_spacing,
                                          top_spacing,
                                          (*mit)->attrib.width,
                                          (*mit)->attrib.height);
                        XTranslateCoordinates(display, (*mit)->id, ws->id,
                                              0, 0, &(*mit)->attrib.x,
                                              &(*mit)->attrib.y, &wd);
                }
            }
        }
    }
    if ((move || resizew || resizeh) && (! (wstate & StateShadedMask)) &&
        (! dontsend)) {
        net->setVirtualPos(this);
        if ((! resizew) && (! resizeh)) sendConfig();
    }
    
    drawDecor();
}

void WaWindow::reparentWin(void) {
    XSetWindowAttributes attrib_set;
    
    attrib_set.event_mask = PropertyChangeMask | StructureNotifyMask |
        FocusChangeMask | EnterWindowMask | LeaveWindowMask;
    attrib_set.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask |
        ButtonMotionMask;
    
    if (validate_window_mapped(id)) {
        XSelectInput(display, id, NoEventMask);
        XReparentWindow(display, id, frame->id, left_spacing, top_spacing);
        XChangeSaveSet(display, id, SetModeInsert);
        XFlush(display);

        XChangeWindowAttributes(display, id, CWEventMask | CWDontPropagate,
                                &attrib_set);
        
#ifdef    SHAPE
        XRectangle *dummy = NULL;
        int n, order;
        if (waimea->shape) {
            XShapeSelectInput(display, id, ShapeNotifyMask);
            dummy = XShapeGetRectangles(display, id, ShapeBounding, &n,
                                        &order);
            if (n > 1) {
                shaped = true;
            }
        }
        XFree(dummy);

        if (shaped) {
            shapeinfo->setShapeOffset(attrib.x, attrib.y);
            frame->shapeUpdateNotify();
        } else {
            shapeinfo->setShapeOffset(attrib.x, attrib.y);
            frame->shapeUpdateNotify();
        }
#endif // SHAPE
        
    }
}

void WaWindow::updateGrabs(void) {
    XUngrabButton(display, AnyButton, AnyModifier, id);
    XUngrabKey(display, AnyKey, AnyModifier, id);

    ActionList *al = actionlists[
        STATE_FROM_MASK_AND_LIST(window_state_mask, actionlists)];
    
    list<Action *>::iterator it = al->actionlist.begin();
    for (; it != al->actionlist.end(); ++it) {
        if ((*it)->type == ButtonPress || (*it)->type == ButtonRelease ||
            (*it)->type == DoubleClick) {
            XGrabButton(display, (*it)->detail ? (*it)->detail: AnyButton,
                        AnyModifier, id, true, ButtonPressMask |
                        ButtonReleaseMask | ButtonMotionMask,
                        GrabModeSync, GrabModeSync, None, None);     
        } else if ((*it)->type == KeyPress || (*it)->type == KeyRelease) {
            XGrabKey(display, (*it)->detail ? (*it)->detail: AnyKey,
                     AnyModifier, id, true, GrabModeSync, GrabModeSync);
        }
    }
}

#ifdef    SHAPE
void WaWindow::shapeEvent(void) {
    WaFrameWindow *real_frame;

    if (master) real_frame = master->frame;
    else real_frame = frame;
        
    real_frame->shapeUpdateNotify();
}
#endif // SHAPE

void WaWindow::sendConfig(void) {
    XConfigureEvent ce;

    ce.type              = ConfigureNotify;
    ce.event             = id;
    ce.window            = id;
    ce.x                 = attrib.x;
    ce.y                 = attrib.y;
    ce.width             = attrib.width;
    ce.border_width      = old_bw;
    ce.override_redirect = false;

    if (master)
        ce.above = master->frame->id;
    else
        ce.above = frame->id;

    if (wstate & StateShadedMask)
        ce.height = restore_shade;
    else
        ce.height = attrib.height;

    sendcf = true;

    XSendEvent(display, id, false, StructureNotifyMask, (XEvent *) &ce);
    XSendEvent(display, ws->id, false, StructureNotifyMask,
               (XEvent *) &ce);

    list<WaWindow *>::iterator mit = merged.begin();
    for (; mit != merged.end(); mit++)
        (*mit)->sendConfig();

    XFlush(display);
}

void WaWindow::drawOutline(int x, int y, int width, int height) {
    if (outline_state) clearOutline();
    outline_state = true;

    outl_x = x - left_spacing;
    outl_y = y - top_spacing;
    outl_w = width + left_spacing + right_spacing;
    outl_h = height + top_spacing + bottom_spacing;

    XDrawRectangle(display, ws->id, ws->xor_gc,
                   outl_x, outl_y, outl_w, outl_h);
}

void WaWindow::clearOutline(void) {
    if (! outline_state) return;
    outline_state = false;

    XDrawRectangle(display, ws->id, ws->xor_gc,
                   outl_x, outl_y, outl_w, outl_h);
}

void WaWindow::drawDecor(void) {
    if (! init_done) return;

#ifdef    SHAPE
    frame->apply_shape = false;
#endif // SHAPE
    
    frame->pushRenderEvent();
}

bool WaWindow::incSizeCheck(int width, int height,
                            unsigned int *n_w, unsigned int *n_h) {
    bool resize = false;

    if (size.max_width > size.min_width ||
        size.max_height > size.min_height) {
        if (width < (int) size.min_width) width = size.min_width;
        if (height > 0 && height < (int) size.min_height)
            height = size.min_height;
        if (width > (int) size.max_width) width = size.max_width;
        if (height > (int) size.max_height) height = size.max_height;
    }
    
    *n_w = attrib.width;
    *n_h = attrib.height;
    if ((width >= (int) (attrib.width + size.width_inc)) ||
        (width <= (int) (attrib.width - size.width_inc)) ||
        (int) attrib.width == width) {
        if (width >= (int) size.min_width && width <= (int) size.max_width) {
            resize = true;
            if (size.width_inc == 1)
                *n_w = width;
            else
                *n_w = width - ((width - size.base_width) % size.width_inc);
        }
    }
    if ((height == 0) && top_spacing) {
        if (! (wstate & StateShadedMask)) {
            wstate |= StateShadedMask;
            restore_shade = attrib.height;
            net->setWmState(this);
        }
        *n_h = 0;
        return resize;
    }
    if ((height >= (int) (attrib.height + size.height_inc)) ||
        (height <= (int) (attrib.height - size.height_inc)) ||
        (int) attrib.height == height) {
        if ((height < 1) && (size.min_height <= 1) && top_spacing) {
            resize = true;
            height = 0;
            if (! (wstate & StateShadedMask)) {
                wstate |= StateShadedMask;
                restore_shade = attrib.height;
                net->setWmState(this);
            }
            *n_h = height;
        }
        else if (height > 0 &&
                 (height >= (int) size.min_height &&
                  height <= (int) size.max_height)) {
            resize = true;
            if (wstate & StateShadedMask) {
                wstate &= ~StateShadedMask;
                net->setWmState(this);
            }
            if (size.height_inc == 1)
                *n_h = height;
            else
                *n_h = height -
                    ((height - size.base_height) % size.height_inc);
        }
    }
    if (resize && size.aspect) {
        if (size.base_size) {
            *n_w -= size.base_width;
            *n_h -= size.base_height;
        }
        double aspect = (double) *n_w / (double) *n_h;
        if (aspect < size.min_aspect)
            *n_w = (unsigned int) ((double) *n_h * size.min_aspect);
        else if (aspect > size.max_aspect)
            *n_h = (unsigned int) ((double) *n_w / size.max_aspect);

        if (size.base_size) {
            *n_w += size.base_width;
            *n_h += size.base_height;
        }
    }
    return resize;
}

void WaWindow::raise(void) {
    if (master) {
        master->raise(); return;
    } else {
        list<WaWindow * >::iterator it = merged.begin();
        for (; it != merged.end(); it++)
            ws->raiseWindow((*it)->frame->id, false);
    }
    ws->raiseWindow(frame->id, false);
    ws->restackWindows();
    net->setClientListStacking(ws);
    net->sendNotify(this, RaiseNotify);
}

void WaWindow::lower(void) {
    if (master) {
        master->lower(); return;
    } else {
        list<WaWindow * >::iterator it = merged.begin();
        for (; it != merged.end(); it++)
            ws->lowerWindow((*it)->frame->id, false);
    }
    ws->lowerWindow(frame->id, false);
    ws->restackWindows();
    net->setClientListStacking(ws);
    net->sendNotify(this, LowerNotify);
}

void WaWindow::move(XEvent *e) {
    WaWindow *w;
    XEvent event, *map_ev;
    int px, py, nx, ny, i;
    list<XEvent *> *maprequest_list;
    Window wd;
    unsigned int ui;

    if (! (functions & FunctionMoveMask)) return;

    if (master) w = master;
    else w = this;
    
    XQueryPointer(display, ws->id, &wd, &wd, &px, &py, &i, &i, &ui);

    if (waimea->eh->move_resize != EndMoveResizeType) return;
    nx = w->attrib.x;
    ny = w->attrib.y;
    waimea->eh->move_resize = MoveType;
    setWindowState(wstate | StateMoveMask);
    move_resize = true;
    
    Window grab_window = frame->id;
    Cursor grab_cursor;
    DWindowObject *dwo = (DWindowObject *)
        waimea->findWin(e->xany.window, ANY_DECOR_WINDOW_TYPE);
    if (dwo) grab_cursor = dwo->sb->style->cursor;
    else grab_cursor = None;

    if (e && e->type == MapRequest) {
        nx = w->attrib.x = px + left_spacing;
        ny = w->attrib.y = py + top_spacing;
        drawOutline(nx, ny, w->attrib.width, w->attrib.height);
    }
    maprequest_list = new list<XEvent *>;
    if (XGrabPointer(display, grab_window, true, ButtonReleaseMask |
                     ButtonPressMask | PointerMotionMask | EnterWindowMask |
                     LeaveWindowMask, GrabModeAsync, GrabModeAsync,
                     None, grab_cursor, CurrentTime) != GrabSuccess) {
        move_resize = false;
        waimea->eh->move_resize = EndMoveResizeType;
        setWindowState(wstate & ~StateMoveMask);
        delete maprequest_list;
        return;
    }
    if (XGrabKeyboard(display, grab_window, true, GrabModeAsync,
                      GrabModeAsync, CurrentTime) != GrabSuccess) {
        move_resize = false;
        waimea->eh->move_resize = EndMoveResizeType;
        setWindowState(wstate & ~StateMoveMask);
        XUngrabPointer(display, CurrentTime);
        delete maprequest_list;
        return;
    }
    for (;;) {
        waimea->eh->eventLoop(&waimea->eh->moveresize_return_mask, &event);
        switch (event.type) {
            case MotionNotify:
                while (XCheckTypedWindowEvent(display, event.xmotion.window,
                                              MotionNotify, &event));
                nx += event.xmotion.x_root - px;
                ny += event.xmotion.y_root - py;
                px  = event.xmotion.x_root;
                py  = event.xmotion.y_root;
                w->drawOutline(nx, ny, w->attrib.width, w->attrib.height);
                break;
            case LeaveNotify:
            case EnterNotify:
                if (ws->west->id == event.xcrossing.window ||
                    ws->east->id == event.xcrossing.window ||
                    ws->north->id == event.xcrossing.window ||
                    ws->south->id == event.xcrossing.window) {
                    clearOutline();
                    waimea->eh->handleEvent(&event);
                    w->drawOutline(nx, ny, w->attrib.width, w->attrib.height);
                } else if (event.type == LeaveNotify) {
                    int cx, cy;
                    XQueryPointer(display, ws->id, &wd, &wd, &cx, &cy,
                                  &i, &i, &ui);
                    nx += cx - px;
                    ny += cy - py;
                    px = cx;
                    py = cy;
                    w->drawOutline(nx, ny, w->attrib.width, w->attrib.height);
                }
                break;
            case DestroyNotify:
            case UnmapNotify:
                if ((((event.type == UnmapNotify)? event.xunmap.window:
                      event.xdestroywindow.window) == w->id)) {
                    while (! maprequest_list->empty()) {
                        XPutBackEvent(display, maprequest_list->front());
                        delete maprequest_list->front();
                        maprequest_list->pop_front();
                    }
                    delete maprequest_list;
                    XPutBackEvent(display, &event);
                    w->clearOutline();
                    XUngrabKeyboard(display, CurrentTime);
                    XUngrabPointer(display, CurrentTime);
                    waimea->eh->move_resize = EndMoveResizeType;
                    setWindowState(wstate & ~StateMoveMask);
                    move_resize = false;
                    return;
                }
                waimea->eh->evUnmapDestroy(&event);
                break;
            case ConfigureRequest:
                if (event.xconfigurerequest.window != id)
                    waimea->eh->evConfigureRequest(&event.xconfigurerequest);
                break;
            case MapRequest:
                map_ev = new XEvent;
                *map_ev = event;
                maprequest_list->push_front(map_ev); break;
            case ButtonPress:
            case ButtonRelease:
                event.xbutton.window = w->id;
            case KeyPress:
            case KeyRelease:
                if (event.type == KeyPress || event.type == KeyRelease)
                    event.xkey.window = w->id;
                w->clearOutline();
                waimea->eh->handleEvent(&event);
                w->drawOutline(nx, ny, attrib.width, attrib.height);
                if (waimea->eh->move_resize != EndMoveResizeType) break;
                setWindowState(wstate & ~StateMoveMask);
                w->clearOutline();
                w->attrib.x = nx;
                w->attrib.y = ny;
                w->redrawWindow();
                checkMoveMerge(w->attrib.x, w->attrib.y);
                while (! maprequest_list->empty()) {
                    XPutBackEvent(display, maprequest_list->front());
                    delete maprequest_list->front();
                    maprequest_list->pop_front();
                }
                delete maprequest_list;
                move_resize = false;
                XUngrabKeyboard(display, CurrentTime);
                XUngrabPointer(display, CurrentTime);
                return;
        }
    }
}

void WaWindow::moveOpaque(XEvent *e) {
    int px, py, prelx, prely, i;
    int sw = attrib.width;
    int sh = attrib.height;
    unsigned int ui;
    Window wd;
    XEvent ed;
    list<XEvent *> maprequest_list;

    if (! (functions & FunctionMoveMask)) return;
  
    XQueryPointer(display, ws->id, &wd, &wd, &px, &py, &i, &i, &ui);
    prelx = px - attrib.x;
    prely = py - attrib.y;
    int pposx = px;
    int pposy = py;
  
    for (;;) {
        bool status = _moveOpaque(e, sw, sh, pposx, pposy, &maprequest_list);
        if (! status) return;

        XQueryPointer(display, ws->id, &wd, &wd, &px, &py, &i, &i, &ui);

        pposx = attrib.x + prelx;
        pposy = attrib.y + prely;   
    
        XWarpPointer(display, None, None, 0, 0, 0, 0, pposx - px, pposy - py);
        XSync(display, false);
        while (XCheckTypedEvent(display, MotionNotify, &ed));
        while (XCheckTypedEvent(display, EnterNotify, &ed));
        while (XCheckTypedEvent(display, LeaveNotify, &ed));
    }
}

bool WaWindow::_moveOpaque(XEvent *e, int saved_w, int saved_h, int px,
                           int py, list<XEvent *> *maprequest_list) {
    WaWindow *w;
    XEvent event, *map_ev;
    int sx, sy, nx, ny, mnx, mny;

    if (master && mergemode == NullMergeType) w = master;
    else w = this;
    
    if (waimea->eh->move_resize != EndMoveResizeType) return false;
    sx = nx = mnx = attrib.x;
    sy = ny = mny = attrib.y;
    waimea->eh->move_resize = MoveOpaqueType;
    setWindowState(wstate | StateMoveMask);
    move_resize = true;
    if (master) {
        mnx = w->attrib.x;
        mny = w->attrib.y;
    }

    Window grab_window = frame->id;
    Cursor grab_cursor;
    DWindowObject *dwo = (DWindowObject *)
        waimea->findWin(e->xany.window, ANY_DECOR_WINDOW_TYPE);
    if (dwo) grab_cursor = dwo->sb->style->cursor;
    else grab_cursor = None;

    if (e && e->type == MapRequest) {
        nx = w->attrib.x = px + left_spacing;
        ny = w->attrib.y = py + top_spacing;
        w->redrawWindow();
        net->setState(this, NormalState);
        net->setVirtualPos(this);
    }
    dontsend = true;    
    if (XGrabPointer(display, grab_window, true,
                     ButtonReleaseMask | ButtonPressMask |
                     PointerMotionMask | EnterWindowMask |
                     LeaveWindowMask, GrabModeAsync, GrabModeAsync,
                     None, grab_cursor, CurrentTime) != GrabSuccess) {
        move_resize = false;
        waimea->eh->move_resize = EndMoveResizeType;
        setWindowState(wstate & ~StateMoveMask);
        return false;
    }
    if (XGrabKeyboard(display, grab_window, true, GrabModeAsync,
                      GrabModeAsync, CurrentTime) != GrabSuccess) {
        move_resize = false;
        waimea->eh->move_resize = EndMoveResizeType;
        setWindowState(wstate & ~StateMoveMask);
        XUngrabPointer(display, CurrentTime);
        return false;
    }
    for (;;) {
        waimea->eh->eventLoop(&waimea->eh->moveresize_return_mask, &event);
        switch (event.type) {
            case MotionNotify:
                while (XCheckTypedWindowEvent(display, event.xmotion.window,
                                              MotionNotify, &event));
                nx += event.xmotion.x_root - px;
                ny += event.xmotion.y_root - py;
                if (master) {
                    mnx += event.xmotion.x_root - px;
                    mny += event.xmotion.y_root - py;
                }
                px = event.xmotion.x_root;
                py = event.xmotion.y_root;
                if (mergemode != NullMergeType) {
                    if (checkMoveMerge(nx, ny, saved_w, saved_h)) {
                        XSync(display, false);
                        while (XCheckTypedEvent(display, FocusIn, &event));
                        while (XCheckTypedEvent(display, FocusOut, &event));
                        dontsend = move_resize = false;
                        waimea->eh->move_resize = EndMoveResizeType;
                        setWindowState(wstate & ~StateMoveMask);
                        return true;
                    } else if (! master) {
                        w->attrib.x = nx;
                        w->attrib.y = ny;
                        w->redrawWindow();
                    }
                } else {
                    if (master) {
                        w->attrib.x = mnx;
                        w->attrib.y = mny;
                    } else {
                        w->attrib.x = nx;
                        w->attrib.y = ny;
                    }
                    w->redrawWindow();
                }
                break;
            case LeaveNotify:
            case EnterNotify:
                if (ws->west->id == event.xcrossing.window ||
                    ws->east->id == event.xcrossing.window ||
                    ws->north->id == event.xcrossing.window ||
                    ws->south->id == event.xcrossing.window) {
                    waimea->eh->handleEvent(&event);
                } else if (event.type == LeaveNotify) {
                    unsigned int ui;
                    Window wd;
                    int cx, cy, i;
                    XQueryPointer(display, ws->id, &wd, &wd, &cx, &cy,
                                  &i, &i, &ui);
                    nx += cx - px;
                    ny += cy - py;
                    if (master) {
                        mnx += cx - px;
                        mny += cy - py;
                    }
                    px = cx;
                    py = cy;
                    if (mergemode != NullMergeType) {
                        if (checkMoveMerge(nx, ny, saved_w, saved_h)) {
                            XSync(display, false);
                            while (XCheckTypedEvent(display, FocusIn, &event));
                            while (XCheckTypedEvent(display, FocusOut,
                                                    &event));
                            dontsend = move_resize = false;
                            waimea->eh->move_resize = EndMoveResizeType;
                            setWindowState(wstate & ~StateMoveMask);
                            return true;
                        } else if (! master) {
                            w->attrib.x = nx;
                            w->attrib.y = ny;
                            w->redrawWindow();
                        }
                    } else {
                        if (master) {
                            w->attrib.x = mnx;
                            w->attrib.y = mny;
                        } else {
                            w->attrib.x = nx;
                            w->attrib.y = ny;
                        }
                        w->redrawWindow();
                    }
                }
                break;
            case DestroyNotify:
            case UnmapNotify:
                if ((((event.type == UnmapNotify)? event.xunmap.window:
                      event.xdestroywindow.window) == w->id)) {
                    while (! maprequest_list->empty()) {
                        XPutBackEvent(display, maprequest_list->front());
                        delete maprequest_list->front();
                        maprequest_list->pop_front();
                    }
                    XPutBackEvent(display, &event);
                    XUngrabKeyboard(display, CurrentTime);
                    XUngrabPointer(display, CurrentTime);
                    waimea->eh->move_resize = EndMoveResizeType;
                    setWindowState(wstate & ~StateMoveMask);
                    dontsend = move_resize = false;
                    return false;
                }
                waimea->eh->evUnmapDestroy(&event);
                break;
            case ConfigureRequest:
                if (event.xconfigurerequest.window != w->id)
                    waimea->eh->evConfigureRequest(&event.xconfigurerequest);
                break;
            case MapRequest:
                map_ev = new XEvent;
                *map_ev = event;
                maprequest_list->push_front(map_ev); break;
            case ButtonPress:
            case ButtonRelease:
                event.xbutton.window = id;
            case KeyPress:
            case KeyRelease:
                if (event.type == KeyPress || event.type == KeyRelease)
                    event.xkey.window = id;
                int merge_state = mergemode;
                waimea->eh->handleEvent(&event);
                if (merge_state != mergemode) {
                    if (checkMoveMerge(nx, ny, saved_w, saved_h)) {
                        XSync(display, false);
                        while (XCheckTypedEvent(display, FocusIn, &event));
                        while (XCheckTypedEvent(display, FocusOut,
                                                &event));
                        dontsend = move_resize = false;
                        if (waimea->eh->move_resize != EndMoveResizeType) {
                            waimea->eh->move_resize = EndMoveResizeType;
                            setWindowState(wstate & ~StateMoveMask);
                            return true;
                        }
                    }
                }
                if (waimea->eh->move_resize != EndMoveResizeType) break;
                setWindowState(wstate & ~StateMoveMask);
                if (w->attrib.x != sx || w->attrib.y != sy) {
                    w->sendConfig();
                    net->setVirtualPos(w);
                }
                while (! maprequest_list->empty()) {
                    XPutBackEvent(display, maprequest_list->front());
                    delete maprequest_list->front();
                    maprequest_list->pop_front();
                }
                dontsend = move_resize = false;
                XUngrabKeyboard(display, CurrentTime);
                XUngrabPointer(display, CurrentTime);
                return false;
        }
    }
}

void WaWindow::resize(XEvent *e, int how) {
    XEvent event, *map_ev;
    int px, py, width, height, n_x, o_x, n_y, o_y, i;
    list<XEvent *> *maprequest_list;
    Window wd;
    unsigned int ui, n_w, n_h, o_w, o_h;
    WaWindow *w;

    if (! (functions & FunctionResizeMask)) return;
    
    if (master && mergetype == CloneMergeType) w = master;
    else w = this;
    
    XQueryPointer(display, ws->id, &wd, &wd, &px, &py, &i, &i, &ui);

    if (waimea->eh->move_resize != EndMoveResizeType) return;
    n_x    = o_x = attrib.x;
    n_y    = o_y = attrib.y;
    width  = n_w = o_w = attrib.width;
    height = n_h = o_h = attrib.height;
    waimea->eh->move_resize = ResizeType;
    setWindowState(wstate | StateResizeMask);
    move_resize = true;

    Window grab_window = frame->id;
    Cursor grab_cursor;
    DWindowObject *dwo = (DWindowObject *)
        waimea->findWin(e->xany.window, ANY_DECOR_WINDOW_TYPE);
    if (dwo) grab_cursor = dwo->sb->style->cursor;
    else grab_cursor = None;

    if (e && e->type == MapRequest) {
        if (how & EastResizeTypeMask)
            n_x = attrib.x = px - attrib.width - left_spacing - right_spacing;
        else n_x = attrib.x = px;
        if (how & SouthResizeTypeMask)
            n_y = attrib.y = py - attrib.height - top_spacing - bottom_spacing;
        else n_y = attrib.y = py;
        drawOutline(n_x, n_y, n_w, n_h);
    }    
    maprequest_list = new list<XEvent *>;
    if (XGrabPointer(display, grab_window, true,
                     ButtonReleaseMask | ButtonPressMask |
                     PointerMotionMask | EnterWindowMask |
                     LeaveWindowMask, GrabModeAsync, GrabModeAsync,
                     None, grab_cursor, CurrentTime) != GrabSuccess) {
        move_resize = false;
        waimea->eh->move_resize = EndMoveResizeType;
        setWindowState(wstate & ~StateResizeMask);
        delete maprequest_list;
        return;
    }
    if (XGrabKeyboard(display, grab_window, true, GrabModeAsync,
                      GrabModeAsync, CurrentTime) != GrabSuccess) {
        move_resize = false;
        waimea->eh->move_resize = EndMoveResizeType;
        setWindowState(wstate & ~StateResizeMask);
        XUngrabPointer(display, CurrentTime);
        delete maprequest_list;
        return;
    }
    for (;;) {
        waimea->eh->eventLoop(&waimea->eh->moveresize_return_mask, &event);
        switch (event.type) {
            case MotionNotify:
                while (XCheckTypedWindowEvent(display, event.xmotion.window,
                                              MotionNotify, &event));
                if (how & EastResizeTypeMask)
                    width += event.xmotion.x_root - px;
                else if (how & WestResizeTypeMask)
                    width -= event.xmotion.x_root - px;
                if (how & SouthResizeTypeMask)
                    height += event.xmotion.y_root - py;
                else if (how & NorthResizeTypeMask)
                    height -= event.xmotion.y_root - py;
                px = event.xmotion.x_root;
                py = event.xmotion.y_root;
                if (w->incSizeCheck(width, height, &n_w, &n_h)) {
                    if (how & WestResizeTypeMask)
                        n_x -= n_w - o_w;
                    if (how & NorthResizeTypeMask)
                        n_y -= n_h - o_h;
                    o_x = n_x;
                    o_y = n_y;
                    o_w = n_w;
                    o_h = n_h;
                    drawOutline(n_x, n_y, n_w, n_h);
                }
                break;
            case LeaveNotify:
            case EnterNotify:
                if (ws->west->id == event.xcrossing.window ||
                    ws->east->id == event.xcrossing.window ||
                    ws->north->id == event.xcrossing.window ||
                    ws->south->id == event.xcrossing.window) {
                    int old_vx = ws->v_x;
                    int old_vy = ws->v_y;
                    clearOutline();
                    waimea->eh->handleEvent(&event);
                    px -= (ws->v_x - old_vx);
                    py -= (ws->v_y - old_vy);
                    n_x = attrib.x;
                    n_y = attrib.y;
                    if (how & WestResizeTypeMask)
                        n_x -= n_w - attrib.width;
                    else if (how & NorthResizeTypeMask)
                        n_y -= n_h - attrib.height;
                    drawOutline(n_x, n_y, n_w, n_h);
                } else if (event.type == LeaveNotify) {
                    int cx, cy;
                    XQueryPointer(display, ws->id, &wd, &wd, &cx, &cy,
                                  &i, &i, &ui);
                    if (how & EastResizeTypeMask)
                        width += cx - px;
                    else if (how & WestResizeTypeMask)
                        width -= cx - px;
                    if (how & SouthResizeTypeMask)
                        height += cy - py;
                    else if (how & NorthResizeTypeMask)
                        height -= cy - py;
                    px = cx;
                    py = cy;
                    if (incSizeCheck(width, height, &n_w, &n_h)) {
                        if (how & WestResizeTypeMask)
                            n_x -= n_w - o_w;
                        if (how & NorthResizeTypeMask)
                            n_y -= n_h - o_h;
                        o_x = n_x;
                        o_y = n_y;
                        o_w = n_w;
                        o_h = n_h;
                        drawOutline(n_x, n_y, n_w, n_h);
                    }
                }
                break;
            case DestroyNotify:
            case UnmapNotify:
                if ((((event.type == UnmapNotify)? event.xunmap.window:
                      event.xdestroywindow.window) == id)) {
                    while (! maprequest_list->empty()) {
                        XPutBackEvent(display, maprequest_list->front());
                        delete maprequest_list->front();
                        maprequest_list->pop_front();
                    }
                    delete maprequest_list;
                    XPutBackEvent(display, &event);
                    clearOutline();
                    XUngrabKeyboard(display, CurrentTime);
                    XUngrabPointer(display, CurrentTime);
                    waimea->eh->move_resize = EndMoveResizeType;
                    setWindowState(wstate & ~StateResizeMask);
                    move_resize = false;
                    return;
                }
                waimea->eh->evUnmapDestroy(&event);
                break;
            case ConfigureRequest:
                if (event.xconfigurerequest.window != id)
                    waimea->eh->evConfigureRequest(&event.xconfigurerequest);
                break;
            case MapRequest:
                map_ev = new XEvent;
                *map_ev = event;
                maprequest_list->push_front(map_ev); break;
            case ButtonPress:
            case ButtonRelease:
                event.xbutton.window = id;
            case KeyPress:
            case KeyRelease:
                if (event.type == KeyPress || event.type == KeyRelease)
                    event.xkey.window = id;
                clearOutline();
                waimea->eh->handleEvent(&event);
                drawOutline(n_x, n_y, n_w, n_h);
                if (waimea->eh->move_resize != EndMoveResizeType) break;
                setWindowState(wstate & ~StateResizeMask);
                clearOutline();
                w->attrib.width = n_w;
                w->attrib.height = n_h;
                w->attrib.x = n_x;
                w->attrib.y = n_y;
                if (master) {
                    if (mergetype == VertMergeType)
                        master->old_attrib.width++;
                    if (mergetype == HorizMergeType)
                        master->old_attrib.height++;
                }
                w->redrawWindow();
                while (! maprequest_list->empty()) {
                    XPutBackEvent(display, maprequest_list->front());
                    delete maprequest_list->front();
                    maprequest_list->pop_front();
                }
                delete maprequest_list;
                move_resize = false;
                XUngrabKeyboard(display, CurrentTime);
                XUngrabPointer(display, CurrentTime);
                return;
        }
    }
}

void WaWindow::resizeOpaque(XEvent *e, int how) {
    XEvent event, *map_ev;
    int px, py, width, height, i;
    list<XEvent *> *maprequest_list;
    Window wd;
    unsigned int n_w, n_h, sw, sh, ui;
    WaWindow *w;

    if (! (functions & FunctionResizeMask)) return;
    
    if (master && mergetype == CloneMergeType) w = master;
    else w = this;
    
    XQueryPointer(display, ws->id, &wd, &wd, &px, &py, &i, &i, &ui);

    if (waimea->eh->move_resize != EndMoveResizeType) return;
    dontsend = true;
    sw = width  = n_w = attrib.width;
    sh = height = n_h = attrib.height;
    waimea->eh->move_resize = ResizeOpaqueType;
    setWindowState(wstate | StateResizeMask);
    move_resize = true;

    Window grab_window = frame->id;
    Cursor grab_cursor;
    DWindowObject *dwo = (DWindowObject *)
        waimea->findWin(e->xany.window, ANY_DECOR_WINDOW_TYPE);
    if (dwo) grab_cursor = dwo->sb->style->cursor;
    else grab_cursor = None;
    
    if (e && e->type == MapRequest) {
        if (how & EastResizeTypeMask)
            attrib.x = px - attrib.width - left_spacing - right_spacing;
        else attrib.x = px;
        if (how & SouthResizeTypeMask)
            attrib.y = py - attrib.height - top_spacing - bottom_spacing;
        else attrib.y = py;
        redrawWindow();
        net->setState(this, NormalState);
        net->setVirtualPos(this);
    }
    
    maprequest_list = new list<XEvent *>;
    if (XGrabPointer(display, grab_window, true,
                     ButtonReleaseMask | ButtonPressMask |
                     PointerMotionMask | EnterWindowMask |
                     LeaveWindowMask, GrabModeAsync, GrabModeAsync,
                     None, grab_cursor, CurrentTime) != GrabSuccess) {
        move_resize = false;
        waimea->eh->move_resize = EndMoveResizeType;
        setWindowState(wstate & ~StateResizeMask);
        delete maprequest_list;
        return;
    }
    if (XGrabKeyboard(display, grab_window, true, GrabModeAsync,
                      GrabModeAsync, CurrentTime) != GrabSuccess) {
        move_resize = false;
        waimea->eh->move_resize = EndMoveResizeType;
        setWindowState(wstate & ~StateResizeMask);
        XUngrabPointer(display, CurrentTime);
        delete maprequest_list;
        return;
    }
    for (;;) {
        waimea->eh->eventLoop(&waimea->eh->moveresize_return_mask, &event);
        switch (event.type) {
            case MotionNotify:
                while (XCheckTypedWindowEvent(display, event.xmotion.window,
                                              MotionNotify, &event));
                if (how & EastResizeTypeMask)
                    width += event.xmotion.x_root - px;
                else if (how & WestResizeTypeMask)
                    width -= event.xmotion.x_root - px;
                if (how & SouthResizeTypeMask)
                    height += event.xmotion.y_root - py;
                else if (how & NorthResizeTypeMask)
                    height -= event.xmotion.y_root - py;
                px = event.xmotion.x_root;
                py = event.xmotion.y_root;
                if (w->incSizeCheck(width, height, &n_w, &n_h)) {
                    if (how & WestResizeTypeMask)
                        w->attrib.x -= n_w - attrib.width;
                    w->attrib.width  = n_w;
                    if (how & NorthResizeTypeMask)
                        w->attrib.y -= n_h - attrib.height;
                    w->attrib.height = n_h;
                    if (master) {
                        if (mergetype == VertMergeType)
                            master->old_attrib.width++;
                        if (mergetype == HorizMergeType)
                            master->old_attrib.height++;
                    }
                    w->redrawWindow();
                }
                break;
            case LeaveNotify:
            case EnterNotify:
                if (ws->west->id == event.xcrossing.window ||
                    ws->east->id == event.xcrossing.window ||
                    ws->north->id == event.xcrossing.window ||
                    ws->south->id == event.xcrossing.window) {
                    int old_vx = ws->v_x;
                    int old_vy = ws->v_y;
                    waimea->eh->handleEvent(&event);
                    px -= (ws->v_x - old_vx);
                    py -= (ws->v_y - old_vy);
                } else if (event.type == LeaveNotify) {
                    int cx, cy;
                    XQueryPointer(display, ws->id, &wd, &wd, &cx, &cy,
                                  &i, &i, &ui);
                    if (how & EastResizeTypeMask)
                        width += cx - px;
                    else if (how & WestResizeTypeMask)
                        width -= cx - px;
                    if (how & SouthResizeTypeMask)
                        height += cy - py;
                    else if (how & NorthResizeTypeMask)
                        height -= cy - py;
                    px = cx;
                    py = cy;
                    if (w->incSizeCheck(width, height, &n_w, &n_h)) {
                        if (how & WestResizeTypeMask)
                            w->attrib.x -= n_w - attrib.width;
                        w->attrib.width  = n_w;
                        if (how & NorthResizeTypeMask)
                            w->attrib.y -= n_h - attrib.height;
                        w->attrib.height = n_h;
                        if (master) {
                            if (mergetype == VertMergeType)
                                master->old_attrib.width++;
                            if (mergetype == HorizMergeType)
                                master->old_attrib.height++;
                        }
                        w->redrawWindow();
                    }
                }
                break;
            case DestroyNotify:
            case UnmapNotify:
                if ((((event.type == UnmapNotify)? event.xunmap.window:
                      event.xdestroywindow.window) == id)) {
                    while (! maprequest_list->empty()) {
                        XPutBackEvent(display, maprequest_list->front());
                        delete maprequest_list->front();
                        maprequest_list->pop_front();
                    }
                    delete maprequest_list;
                    XPutBackEvent(display, &event);
                    XUngrabKeyboard(display, CurrentTime);
                    XUngrabPointer(display, CurrentTime);
                    waimea->eh->move_resize = EndMoveResizeType;
                    setWindowState(wstate & ~StateResizeMask);
                    dontsend = move_resize = false;
                    return;
                }
                waimea->eh->evUnmapDestroy(&event);
                break;
            case ConfigureRequest:
                if (event.xconfigurerequest.window != id)
                    waimea->eh->evConfigureRequest(&event.xconfigurerequest);
                break;
            case MapRequest:
                map_ev = new XEvent;
                *map_ev = event;
                maprequest_list->push_front(map_ev); break;
            case ButtonPress:
            case ButtonRelease:
                event.xbutton.window = id;
            case KeyPress:
            case KeyRelease:
                if (event.type == KeyPress || event.type == KeyRelease)
                    event.xkey.window = id;
                waimea->eh->handleEvent(&event);
                width = w->attrib.width;
                height = w->attrib.height;
                if (waimea->eh->move_resize != EndMoveResizeType) break;
                setWindowState(wstate & ~StateResizeMask);
                if (w->attrib.width != sw || w->attrib.height != sh) {
                    net->setVirtualPos(w);
                }
                while (! maprequest_list->empty()) {
                    XPutBackEvent(display, maprequest_list->front());
                    delete maprequest_list->front();
                    maprequest_list->pop_front();
                }
                delete maprequest_list;
                dontsend = move_resize = false;
                XUngrabKeyboard(display, CurrentTime);
                XUngrabPointer(display, CurrentTime);
                return;
        }
    }
}

void WaWindow::resizeSmart(XEvent *e) {
    int x, y, i;
    Window wd;
    unsigned int ui;
    int resize_mask = 0;
    
    XQueryPointer(display, ws->id, &wd, &wd, &x, &y, &i, &i, &ui);

    if (x >= (attrib.x + (int) attrib.width / 2))
        resize_mask |= EastResizeTypeMask;
    else
        resize_mask |= WestResizeTypeMask;

    if (y >= (attrib.y + (int) attrib.height / 2))
        resize_mask |= SouthResizeTypeMask;
    else
        resize_mask |= NorthResizeTypeMask;

    resize(e, resize_mask);
}

void WaWindow::resizeSmartOpaque(XEvent *e) {
    int x, y, i;
    Window wd;
    unsigned int ui;
    int resize_mask = 0;
    
    XQueryPointer(display, ws->id, &wd, &wd, &x, &y, &i, &i, &ui);

    if (x >= (attrib.x + (int) attrib.width / 2))
        resize_mask |= EastResizeTypeMask;
    else
        resize_mask |= WestResizeTypeMask;

    if (y >= (attrib.y + (int) attrib.height / 2))
        resize_mask |= SouthResizeTypeMask;
    else
        resize_mask |= NorthResizeTypeMask;

    resizeOpaque(e, resize_mask);
}

void WaWindow::_maximize(int x, int y) {
    if (master) return;
    int new_width, new_height;
    unsigned int n_w, n_h;

    if (! (functions & FunctionResizeMask)) return;
    if (! (functions & FunctionMoveMask)) return;
    
    if (wstate & StateMaximizedMask) return;
    
    int workx, worky;
    unsigned int workw, workh;
    ws->getWorkareaSize(&workx, &worky, &workw, &workh);

    if (wstate & StateFullscreenMask) {
        new_width = ws->width - left_spacing - right_spacing;
        new_height = ws->height - top_spacing - bottom_spacing;
    } else {
        new_width = workw - left_spacing - right_spacing;
        new_height = workh - top_spacing - bottom_spacing;
    }

    restore_max.width = attrib.width;
    restore_max.height = attrib.height;
    int rest_x = attrib.x;
    int rest_y = attrib.y;

    list<WaWindow *>::iterator mit = merged.begin();
    for (; mit != merged.end(); mit++) {
        switch ((*mit)->mergetype) {
            case VertMergeType:
                new_width -= (*mit)->attrib.width;
                break;
            case HorizMergeType:
                new_height -= (*mit)->attrib.height;
                break;
        }
    }
    
    if (wstate & StateShadedMask) {
        restore_max.height = restore_shade;
        restore_shade = new_height;
        new_height = attrib.height;
    }
    if (incSizeCheck(new_width, new_height, &n_w, &n_h)) {
        attrib.x = workx;
        attrib.y = worky;
        restore_max.x = rest_x - attrib.x;
        restore_max.y = rest_y - attrib.y;
        if (x >= 0 && y >= 0) {
            attrib.x = (x - ws->v_x);
            attrib.y = (y - ws->v_y);
            restore_max.misc0 = x;
            restore_max.misc1 = y;
        } else {
            restore_max.misc0 = ws->v_x + attrib.x;
            restore_max.misc1 = ws->v_y + attrib.y;
        }
        if (wstate & StateFullscreenMask) {
            attrib.x = 0;
            attrib.y = 0;
        }
        attrib.x += left_spacing;
        attrib.y += top_spacing;
        attrib.width = n_w;
        attrib.height = n_h;
        redrawWindow();
        wstate |= StateMaximizedMask;
        
        net->setWmState(this);
    }
}

void WaWindow::unMaximize(void) {
    if (master) { master->unMaximize(); return; }
    int rest_height, tmp_shade_height = 0;
    unsigned int n_w, n_h;
    
    if (wstate & StateMaximizedMask) {
        if (wstate & StateShadedMask) {
            rest_height = attrib.height;
            tmp_shade_height = restore_max.height;
        }
        else rest_height = restore_max.height;
        if (incSizeCheck(restore_max.width, rest_height, &n_w, &n_h)) {
            attrib.x = restore_max.x + (restore_max.misc0 - ws->v_x);
            attrib.y = restore_max.y + (restore_max.misc1 - ws->v_y);
            attrib.width = n_w;
            attrib.height = n_h;
            wstate &= ~StateMaximizedMask;
            redrawWindow();
            if (wstate & StateShadedMask) restore_shade = tmp_shade_height;
            net->setWmState(this);
        }
    }
}

void WaWindow::toggleMaximize(void) {
    if (! (wstate & StateMaximizedMask)) maximize();
    else unMaximize();
}

void WaWindow::close(void) {
    XEvent ev;

    ev.type = ClientMessage;
    ev.xclient.window = id;
    ev.xclient.message_type = net->wm_protocols;
    ev.xclient.format = 32;
    ev.xclient.data.l[0] = net->wm_delete_window;
    ev.xclient.data.l[1] = CurrentTime;

    XSendEvent(display, id, false, NoEventMask, &ev);
}

void WaWindow::kill(void) {
    XKillClient(display, id);
}

void WaWindow::closeKill(void) {
    if (protocol_mask & DeleteWindowProtocalMask) close();
    else kill();
}

void WaWindow::shade(void) {
    if (! (functions & FunctionResizeMask)) return;
    
    if (master) { master->shade(); return; }
    unsigned int n_w, n_h;
    
    if (incSizeCheck(attrib.width, 0, &n_w, &n_h)) {
        attrib.width = n_w;
        attrib.height = n_h;
        redrawWindow();

        MERGED_LOOP {
            if (wstate & StateShadedMask) _mw->wstate |= StateShadedMask;
            else _mw->wstate &= ~StateShadedMask;
            net->setWmState(_mw);
        }
    }
}

void WaWindow::unShade(void) {
    if (master) { master->unShade(); return; }
    if (wstate & StateShadedMask) {
        attrib.height = restore_shade;
        
        MERGED_LOOP {
            _mw->wstate &= ~StateShadedMask;
            net->setWmState(_mw);
        }
        redrawWindow();
    }
}

void WaWindow::toggleShade(void) {
    if (wstate & StateShadedMask) unShade();
    else shade();
}

void WaWindow::sticky(void) {
    if (master) { master->sticky(); return; }
    MERGED_LOOP {
        _mw->wstate |= StateStickyMask;
        net->setWmState(_mw);
    }
}

void WaWindow::unSticky(void) {
    if (master) { master->unSticky(); return; }
    MERGED_LOOP {
        _mw->wstate &= ~StateStickyMask;
        net->setWmState(_mw);
    }
}

void WaWindow::toggleSticky(void) {
    if (master) { master->toggleSticky(); return; }

    if (wstate & StateStickyMask) wstate &= ~StateStickyMask;
    else wstate |= StateStickyMask;
    
    MERGED_LOOP {
        if (wstate & StateStickyMask) _mw->wstate |= StateStickyMask;
        else _mw->wstate &= ~StateStickyMask;
        net->setWmState(_mw);
    }
}

void WaWindow::minimize(void) {
    if (master) { master->minimize(); return; }
    if (wstate & StateMinimizedMask) return;
    MERGED_LOOP {
        net->setState(_mw, IconicState);
        net->setWmState(_mw);
    }
}

void WaWindow::unMinimize(void) {
    if (master) { master->unMinimize(); return; }
    if (! (wstate & StateMinimizedMask)) return;
    MERGED_LOOP {
        net->setState(_mw, NormalState);
        net->setWmState(_mw);
    }
}

void WaWindow::toggleMinimize(void) {
    if (master) { master->toggleMinimize(); return; }
    if (wstate & StateMinimizedMask) unMinimize();
    else minimize();
}

void WaWindow::fullscreenOn(void) {
    if (! (functions & FunctionResizeMask)) return;
    if (! (functions & FunctionMoveMask)) return;
    
    if (master) { master->fullscreenOn(); return; }
    
    if (wstate & StateFullscreenMask) return;

    wstate |= StateFullscreenMask;

    if (wstate & StateMaximizedMask) {
        wstate &= ~StateMaximizedMask;
        int res_x = restore_max.x;
        int res_y = restore_max.y;
        int res_w = restore_max.width;
        int res_h = restore_max.height;
        _maximize(restore_max.misc0, restore_max.misc1);
        restore_max.x = res_x;
        restore_max.y = res_y;
        restore_max.width = res_w;
        restore_max.height = res_h;
    }
    net->setWmState(this);
}

void WaWindow::fullscreenOff(void) {
    if (master) { master->fullscreenOff(); return; }
    if (! (wstate & StateFullscreenMask)) return;

    wstate &= ~StateFullscreenMask;

    if (wstate & StateMaximizedMask) {
        wstate &= ~StateMaximizedMask;
        int res_x = restore_max.x;
        int res_y = restore_max.y;
        int res_w = restore_max.width;
        int res_h = restore_max.height;
        _maximize(restore_max.misc0, restore_max.misc1);
        restore_max.x = res_x;
        restore_max.y = res_y;
        restore_max.width = res_w;
        restore_max.height = res_h;
    }
    net->setWmState(this);
}

void WaWindow::fullscreenToggle(void) {
    if (wstate & StateFullscreenMask) fullscreenOff();
    else fullscreenOn();
}

void WaWindow::setDecor(long newdecormask) {
    if (! (functions & FunctionSetDecorMask)) return;
    
    if (master) { master->setDecor(newdecormask); return; }

    if (newdecormask == decormask) return;

    decormask = newdecormask;
    
    MERGED_LOOP {
        if (! (_mw->functions & FunctionSetDecorMask)) return;
        
        _mw->decormask = decormask;
        if (decormask & DecorTitleMask) _mw->wstate |= StateDecorTitleMask;
        else _mw->wstate &= ~StateDecorTitleMask;

        if (decormask & DecorBorderMask) _mw->wstate |= StateDecorBorderMask;
        else _mw->wstate &= ~StateDecorBorderMask;

        if (decormask & DecorHandlesMask) _mw->wstate |= StateDecorHandlesMask;
        else _mw->wstate &= ~StateDecorHandlesMask;
        
        if (decormask & DecorAllMask) _mw->wstate |= StateDecorAllMask;
        else _mw->wstate &= ~StateDecorAllMask;
        
        net->setWmState(_mw);
    }
}

void WaWindow::alwaysontopOn(void) {
    if (! (functions & FunctionSetStackingMask)) return;
    
    if (master) { master->alwaysontopOn(); return; }
    if (wstate & StateAlwaysOnTopMask) return;
    
    MERGED_LOOP {
        _mw->wstate &= ~StateAlwaysAtBottomMask;
        _mw->wstate |= StateAlwaysOnTopMask;
        _mw->frame->setStacking(AlwaysOnTopStackingType);
        net->setWmState(_mw);
    }
    ws->restackWindows();
    net->setClientListStacking(ws);
}

void WaWindow::alwaysatbottomOn(void) {
    if (! (functions & FunctionSetStackingMask)) return;
    
    if (master) { master->alwaysatbottomOn(); return; }
    if (wstate & StateAlwaysAtBottomMask) return;

    MERGED_LOOP {
        _mw->wstate &= ~StateAlwaysOnTopMask;
        _mw->wstate |= StateAlwaysAtBottomMask;
        _mw->frame->setStacking(AlwaysAtBottomStackingType);
        net->setWmState(_mw);
    }
    ws->restackWindows();
    net->setClientListStacking(ws);
}

void WaWindow::alwaysontopOff(void) {
    if (master) { master->alwaysontopOff(); return; }
    if (! (wstate & StateAlwaysOnTopMask)) return;

    MERGED_LOOP {
        _mw->wstate &= ~StateAlwaysOnTopMask;
        _mw->frame->setStacking(NormalStackingType);
        net->setWmState(_mw);
    }
    ws->restackWindows();
    net->setClientListStacking(ws);
}

void WaWindow::alwaysatbottomOff(void) {
    if (master) { master->alwaysatbottomOff(); return; }
    if (! (wstate & StateAlwaysAtBottomMask)) return;

    MERGED_LOOP {
        _mw->wstate &= ~StateAlwaysAtBottomMask;
        _mw->frame->setStacking(NormalStackingType);
        net->setWmState(_mw);
    }
    net->setClientListStacking(ws);
}

void WaWindow::alwaysontopToggle(void) {
    if (wstate & StateAlwaysOnTopMask) alwaysontopOff();
    else alwaysontopOn();
}

void WaWindow::alwaysatbottomToggle(void) {
    if (wstate & StateAlwaysAtBottomMask) alwaysatbottomOff();
    else alwaysatbottomOn();
}

void WaWindow::moveResize(char *s) {
    int x, y, geometry;
    unsigned int width, height;

    if (! (functions & FunctionMoveMask)) return;
    if (! (functions & FunctionResizeMask)) return;
    
    if (waimea->eh->move_resize != EndMoveResizeType || ! s) return;

    width = attrib.width;
    height = attrib.height;

    geometry = XParseGeometry(s, &x, &y, &width, &height);
    incSizeCheck(width, height, &attrib.width, &attrib.height);
    
    if (geometry & XValue) {
        if (geometry & XNegative)
            attrib.x = ws->width + x - attrib.width;
        else attrib.x = x;
    }
    if (geometry & YValue) {
        if (geometry & YNegative)
            attrib.y = ws->height + y - attrib.height;
        else attrib.y = y;
    }

    redrawWindow();
    checkMoveMerge(attrib.x, attrib.y);
}

void WaWindow::moveResizeVirtual(char *s) {
    int x, y, geometry;
    unsigned int width, height;

    if (! (functions & FunctionMoveMask)) return;
    if (! (functions & FunctionResizeMask)) return;
    
    if (waimea->eh->move_resize != EndMoveResizeType || ! s) return;
    width = attrib.width;
    height = attrib.height;

    geometry = XParseGeometry(s, &x, &y, &width, &height);
    incSizeCheck(width, height, &attrib.width, &attrib.height);

    gravitate(RemoveGravity);
    if (geometry & XValue) {
        if (geometry & XNegative)
            attrib.x = ((ws->v_xmax + ws->width) +
                        x - attrib.width) - ws->v_x;
        else attrib.x = x - ws->v_x;
    }
    if (geometry & YValue) {
        if (geometry & YNegative)
            attrib.y = ((ws->v_ymax + ws->height) +
                        y - attrib.height) - ws->v_y;
        else attrib.y = y - ws->v_y;
    }
    gravitate(ApplyGravity);

    redrawWindow();
    checkMoveMerge(attrib.x, attrib.y);
}

void WaWindow::moveWindowToPointer(XEvent *e) {
    if (! (functions & FunctionMoveMask)) return;
    
    attrib.x = e->xbutton.x_root - attrib.width / 2;
    attrib.y = e->xbutton.y_root - attrib.height / 2;

    int workx, worky;
    unsigned int workw, workh;
    ws->getWorkareaSize(&workx, &worky, &workw, &workh);
    
    if ((attrib.x + right_spacing + attrib.width) > (workx + workw))
        attrib.x = (workx + workw) - attrib.width - right_spacing;
    else if (attrib.x < workx) attrib.x = workx + left_spacing;
    
    if ((attrib.y + attrib.height) > (worky + workh))
        attrib.y = (worky + workh) - bottom_spacing - attrib.height;
    else if (attrib.y < worky)
        attrib.y = worky + top_spacing;

    redrawWindow();
    checkMoveMerge(attrib.x, attrib.y);
}

void WaWindow::moveWindowToSmartPlace(void) {
    if (! (functions & FunctionMoveMask)) return;
    
    int temp_h, temp_w;
    int workx, worky;
    unsigned int workw, workh;
    ws->getWorkareaSize(&workx, &worky, &workw, &workh);
    int test_x = frame->attrib.x;
    int test_y = frame->attrib.y;
    int loc_ok = false, tw, tx, ty, th;
    temp_h = frame->attrib.height;
    temp_w = frame->attrib.width;
    int first = true;

    while (((test_y + temp_h) < (int) workh) && !loc_ok) {
        if (! first) test_x = 0;
        while (((test_x + temp_w) < (int) workw) && !loc_ok) {
            loc_ok = true;
            list<WaWindow *>::iterator it = ws->wawindow_list.begin();
            for (; it != ws->wawindow_list.end() && (loc_ok == True); it++) {
                if ((*it != this) && ((*it)->wstate & StateTasklistMask) &&
                    (! (*it)->master) &&
                    ((*it)->desktop_mask &
                     (1L << ws->current_desktop->number)) &&
                    ((((*it)->frame->attrib.x + 
                       (*it)->frame->attrib.width) > 0 &&
                      (*it)->frame->attrib.x < (int) workw) && 
                     (((*it)->frame->attrib.y + 
                       (*it)->frame->attrib.height) > 0 &&
                      (*it)->frame->attrib.y < (int) workh))) {
                    
                    th = (*it)->frame->attrib.height;
                    tw = (*it)->frame->attrib.width;

                    tx = (*it)->frame->attrib.x - workx;
                    ty = (*it)->frame->attrib.y - worky;

                    if ((tx < (test_x + temp_w)) &&
                        ((tx + tw) > test_x) &&
                        (ty < (test_y + temp_h)) &&
                        ((ty + th) > test_y)) {
                        loc_ok = False;
                        test_x = tx + tw;
                    }
                }
            }
            test_x += 1;
            
            if ((!loc_ok) && first) {
              first = false;
              test_x = test_y = 0;
            }
        }
        test_y += 1;
    }
    if (loc_ok) {
        attrib.x = left_spacing + test_x + workx - 2;
        attrib.y = top_spacing + test_y + worky - 1;
        redrawWindow();
        checkMoveMerge(attrib.x, attrib.y);
    }
}

void WaWindow::desktopMask(char *s) {
    if (! (functions & FunctionDesktopMask)) return;
    
    if (master) { master->desktopMask(s); return; }
    if (s) {
        if (! strncasecmp("all", s, 3))
            desktop_mask = (1L << 16) - 1;
        else {
            desktop_mask = 0;
            char *token = strtok(s, " \t");
            while (token) {
                unsigned int desk = (unsigned int) atoi(token);
                if (desk < ws->config.desktops)
                    desktop_mask |= (1L << desk);
                token = strtok(NULL, " \t");
            }
        }
        if (desktop_mask == 0) desktop_mask = (1L << 0);
        
        if (desktop_mask & (1L << ws->current_desktop->number))
            show();
        else
            hide();

        MERGED_LOOP {
            _mw->desktop_mask = desktop_mask;
            net->setDesktop(_mw);
            net->setDesktopMask(_mw);
        }
    }
}

void WaWindow::joinDesktop(char *s) {
    if (! (functions & FunctionDesktopMask)) return;
    
    if (master) { master->joinDesktop(s); return; }
    if (s) {
        unsigned int desk = (unsigned int) atoi(s);
        if (desk < ws->config.desktops) {
            desktop_mask |= (1L << desk);
            if (desktop_mask & (1L << ws->current_desktop->number))
                show();
            MERGED_LOOP {
                _mw->desktop_mask = desktop_mask;
                net->setDesktop(_mw);
                net->setDesktopMask(_mw);
            }
        }
    }
}

void WaWindow::partDesktop(char *s) {
    if (! (functions & FunctionDesktopMask)) return;
    
    if (master) { master->partDesktop(s); return; }
    if (s) {
        unsigned int desk = (unsigned int) atoi(s);
        if (desk < ws->config.desktops) {
            long int new_mask = desktop_mask & ~(1L << desk);
            if (new_mask) {
                desktop_mask = new_mask;
                if (! (desktop_mask &
                       (1L << ws->current_desktop->number))) 
                    hide();
                MERGED_LOOP {
                    _mw->desktop_mask = desktop_mask;
                    net->setDesktop(_mw);
                    net->setDesktopMask(_mw);
                }
            }
        }
    }
}

void WaWindow::partCurrentDesktop(void) {
    if (! (functions & FunctionDesktopMask)) return;
    
    if (master) { master->partCurrentDesktop(); return; }
    long int new_mask = desktop_mask &
        ~(1L << ws->current_desktop->number);
    if (new_mask) {
        desktop_mask = new_mask;
        hide();
        MERGED_LOOP {
            _mw->desktop_mask = desktop_mask;
            net->setDesktop(_mw);
            net->setDesktopMask(_mw);
        }
    }
}

void WaWindow::joinCurrentDesktop(void) {
    if (! (functions & FunctionDesktopMask)) return;
    
    if (master) { master->joinCurrentDesktop(); return; }
    desktop_mask |= (1L << ws->current_desktop->number);
    show();
    MERGED_LOOP {
        _mw->desktop_mask = desktop_mask;
        net->setDesktop(_mw);
        net->setDesktopMask(_mw);
    }
}

void WaWindow::joinAllDesktops(void) {
    if (! (functions & FunctionDesktopMask)) return;
    
    if (master) { master->joinAllDesktops(); return; }
    desktop_mask = (1L << 16) - 1;
    show();
    MERGED_LOOP {
        _mw->desktop_mask = desktop_mask;
        net->setDesktop(_mw);
        net->setDesktopMask(_mw);
    }
}

void WaWindow::partAllDesktopsExceptCurrent(void) {
    if (! (functions & FunctionDesktopMask)) return;
    
    desktop_mask = (1L << ws->current_desktop->number);
    show();
    MERGED_LOOP {
        _mw->desktop_mask = desktop_mask;
        net->setDesktop(_mw);
        net->setDesktopMask(_mw);
    }
}

void WaWindow::partCurrentJoinDesktop(char *s) {
    if (! (functions & FunctionDesktopMask)) return;
    
    if (master) { master->partCurrentJoinDesktop(s); return; }
    if (s) {
        unsigned int desk = (unsigned int) atoi(s);
        if (desk < ws->config.desktops) {
            desktop_mask = desktop_mask &
                ~(1L << ws->current_desktop->number);
            desktop_mask |= (1L << desk);
            if (desktop_mask & (1L << ws->current_desktop->number))
                show();
            else hide();
            MERGED_LOOP {
                _mw->desktop_mask = desktop_mask;
                net->setDesktop(_mw);
                net->setDesktopMask(_mw);
            }
        }
    }
}

void WaWindow::merge(WaWindow *child, int mtype) {
    if (! (functions & FunctionMergeMask)) return;
    
    if (mtype != CloneMergeType && mtype != VertMergeType &&
        mtype != HorizMergeType) return;
    if (child->master) child->master->unmerge(child);
    if (child == this) return;
    if (! child->merged.empty()) return;
    if (master) return;

    bool had_focus = child->has_focus;
        
    wa_grab_server();
    if (validate_drawable(id)) {
        XSelectInput(display, child->id, NoEventMask);
        XReparentWindow(display, child->id, frame->id,
                        -child->attrib.width, -child->attrib.height);
        XSelectInput(display, child->id, PropertyChangeMask |
                     StructureNotifyMask | FocusChangeMask |
                     EnterWindowMask | LeaveWindowMask);
    } else {
        wa_ungrab_server();
        return;
    }
    wa_ungrab_server();

    merged.push_back(child);

    child->master = this;
    child->mergetype = mtype;
    child->hide();

    /* merge decorations */
    
    if (mtype == CloneMergeType) {
        child->mergedback = true;
        if (waimea->eh) child->toFront();
    }

    updateAllAttributes();
    if (! waimea->eh) {
        net->getMergeOrder(this);
        net->getMergeAtfront(this);
    }
    net->setMergedState(child);
    net->setMergeOrder(this);

    if (had_focus) {
        child->has_focus = false;
        waimea->focusNew(child->id, false);
    }
}

void WaWindow::unmerge(WaWindow *child) {
    list<WaWindow *>::iterator it = merged.begin();
    for (; it != merged.end() && *it != child; it++);

    if (it == merged.end()) return;

    bool had_focus = child->has_focus;

    wa_grab_server();
    if (validate_drawable(child->id)) {
        XSelectInput(display, child->id, NoEventMask);
        XReparentWindow(display, child->id, child->frame->id, 0,
                        child->top_spacing);
        XLowerWindow(display, child->id);
        XSelectInput(display, child->id, PropertyChangeMask |
                     StructureNotifyMask | FocusChangeMask |
                     EnterWindowMask | LeaveWindowMask);
    }
    wa_ungrab_server();

    merged.remove(child);

    /* merge decorations */

    if (child->mergetype == CloneMergeType && !child->mergedback)
        toFront();

    updateAllAttributes();   

    child->master = NULL;
    child->mergetype = NullMergeType;
    if (child->desktop_mask & (1L << ws->current_desktop->number)) {
        child->hidden = true;
        child->show();
    }
    child->updateAllAttributes();
    
    if (! ws->shutdown) {
        net->setMergedState(child);
        net->setMergeOrder(this);
    }
    
    if (had_focus) {
        waimea->focusNew(child->id, false);
    } else
        child->has_focus = false;
}

void WaWindow::mergeWithWindow(char *s, int mtype) {
    if (! s) return;
    if (! (functions & FunctionMergeMask)) return;

    char *str = WA_STRDUP(s);
    WaWindow *mw = NULL;
    list<AWindowObject *> awos;
    Tst<char *> *attr;
    Doing *doing = new Doing(ws);
    
    attr = short_do_string_to_tst(str);

    doing->applyAttributes(NULL, attr);

    delete attr;

    ws->getRegexTargets(doing->wreg, WindowType, false, &awos);
    if (! awos.empty()) {
        mw = (WaWindow *) *awos.begin();
        awos.clear();
    }

    delete doing;
    delete [] str;
    
    if (mw && mw != this)
        mw->merge(this, mtype);
}

void WaWindow::explode(void) {
    while (! merged.empty())
        unmerge(merged.back());
}

void WaWindow::setMergeMode(char *s) {
    if (s) {
        if (! strncasecmp(s, "vert", 4))
            mergemode = VertMergeType;
        else if (! strncasecmp(s, "horiz", 5))
            mergemode = HorizMergeType;
        else if (! strncasecmp(s, "clone", 5))
            mergemode = CloneMergeType;
        else
            mergemode = NullMergeType;
    }
}

void WaWindow::nextMergeMode(void) {
    if (mergemode == VertMergeType)
        mergemode = NullMergeType;
    else mergemode++;
}

void WaWindow::prevMergeMode(void) {
    if (mergemode == NullMergeType)
        mergemode = VertMergeType;
    else mergemode--;
}

void WaWindow::mergeTo(XEvent *e, int mtype) {
    WaWindow *wt = (WaWindow *) waimea->findWin(e->xany.window, WindowType);
    if (wt)
        merge(wt, mtype);
}

void WaWindow::toFront(void) {
    if (mergedback) {
        list<WaWindow *>::iterator it = merged.begin();
        list<WaWindow *>::iterator it_end = merged.end();
        if (master) {
            it = master->merged.begin();
            it_end = master->merged.end();
            XLowerWindow(display, master->id);
        }   
        for (; it != it_end; it++) {
            if ((*it)->id != id)
                XLowerWindow(display, (*it)->id);
        }
        
        int focused = false;
        if (master) {
            net->setMergeAtfront(master, id);
            master->mergedback = true;
            it = master->merged.begin();
            for (; it != master->merged.end(); it++) {
                if ((*it)->mergetype == CloneMergeType) {
                    if ((*it)->has_focus) focused = true;
                    (*it)->mergedback = true;
                }
            }
        } else {
            net->setMergeAtfront(this, id);
            it = merged.begin();
            for (; it != merged.end(); it++) {
                if ((*it)->mergetype == CloneMergeType) {
                    if ((*it)->has_focus) focused = true;
                    (*it)->mergedback = true;
                }
            }
        }        
        mergedback = false;        

        if (focused) waimea->focusNew(id, false);
    }
}

bool WaWindow::checkMoveMerge(int x, int y, int width, int height) {
    if (! merged.empty()) return false;
    if (! (functions & FunctionMergeMask)) return false;
    
    list<WaWindow *> matchlist;
    WaWindow *bestmatch = NULL;

    if (width == 0) width = attrib.width;
    if (height == 0) height = attrib.height;
    int _x = x + width / 2;
    int _y = y + height / 2;

    if (mergemode == NullMergeType && master) {
        attrib.x = x;
        attrib.y = y;
        old_attrib.width = attrib.width;
        old_attrib.height = attrib.height;
        attrib.width = width;
        attrib.height = height;
        master->unmerge(this);
        return true;
    }

    list<WaWindow *>::iterator it = ws->wawindow_list.begin();
    for (; it != ws->wawindow_list.end(); it++) {     
        if (*it != this && (! (*it)->master) && (! (*it)->hidden) &&
            (! ((*it)->wstate & StateShadedMask)) &&
            ((*it)->wstate & StateTasklistMask)) {
            if (_x > (*it)->frame->attrib.x &&
                _x < ((*it)->frame->attrib.x +
                      (int) (*it)->frame->attrib.width) &&
                _y > (*it)->frame->attrib.y &&
                _y < ((*it)->frame->attrib.y +
                      (int) (*it)->frame->attrib.height)) {
                matchlist.push_back(*it);
            }
        }
    }
    if (! matchlist.empty()) {
        if (matchlist.size() > 1) {
            list<Window>::iterator wit = ws->aot_stacking_list.begin();
            for (; !bestmatch &&
                     wit != ws->aot_stacking_list.end(); wit++) {
                for (it = matchlist.begin(); it != matchlist.end(); it++)
                    if ((*it)->frame->id == *wit) {
                        bestmatch = *it;
                        break;
                    }
            }
            wit = ws->stacking_list.begin();
            for (; !bestmatch &&
                     wit != ws->stacking_list.end(); wit++) {
                for (it = matchlist.begin(); it != matchlist.end(); it++)
                    if ((*it)->frame->id == *wit) {
                        bestmatch = *it;
                        break;
                    }
            }
            wit = ws->aab_stacking_list.begin();
            for (; !bestmatch &&
                     wit != ws->aab_stacking_list.end(); wit++) {
                for (it = matchlist.begin(); it != matchlist.end(); it++)
                    if ((*it)->frame->id == *wit) {
                        bestmatch = *it;
                        break;
                    }
            }
        }
        else {
            bestmatch = matchlist.front();
        }
    }

    LISTCLEAR(matchlist);

    if (bestmatch) {
        if (master != bestmatch) {
            bestmatch->merge(this, mergemode);
            return true;
        } else if (mergemode != mergetype) {
            bestmatch->merge(this, mergemode);
            return true;
        }
    }
    else if (master) {
        if ((_x + 25) > master->frame->attrib.x &&
            _x < (master->frame->attrib.x +
                  (int) master->frame->attrib.width + 25) &&
            (_y + 25) > master->frame->attrib.y &&
            _y < (master->frame->attrib.y +
                  (int) master->frame->attrib.height) + 25) {
            return false;
        }
        attrib.x = x;
        attrib.y = y;
        old_attrib.width = attrib.width;
        old_attrib.height = attrib.height;
        attrib.width = width;
        attrib.height = height;
        master->unmerge(this);
        return true;
    }
    return false;
}

void WaWindow::windowStateCheck(bool force) {
    if (! init_done) return;
    
    if (force || wstate != old_wstate) {
        EventDetail ed;
        ed.x = ed.y = INT_MAX;
        ed.x11mod = ed.wamod = 0;
        ed.type = WindowStateChangeNotify;
        ws->waimea->eh->evAct(NULL, frame->id, &ed);
        
        signalMonitors();

        old_wstate = wstate;
    }
}

void WaWindow::setWindowState(long int newstate) {
    if (newstate != wstate) {
        wstate = newstate;
        windowStateCheck();
    }
}

void WaWindow::addMonitor(AWindowObject *monitor, long int type) {
    EventDetail ed;
    ed.x = ed.y = INT_MAX;
    ed.x11mod = ed.wamod = 0;

    monitors.push_back(new FlagMonitor(monitor, type));

    if (type) {
        if (wstate & type) ed.type = StateAddNotify;
        else ed.type = StateRemoveNotify;
    
        ws->waimea->eh->evAct(NULL, monitors.back()->monitor->id, &ed);
    }
}

void WaWindow::removeMonitor(AWindowObject *monitor) {
    list<FlagMonitor *>::iterator it = monitors.begin();
    for (; it != monitors.end(); it++)
        if ((*it)->monitor == monitor)
            it = monitors.erase(it);
}

void WaWindow::signalMonitors(void) {
    EventDetail ed;
    ed.x = ed.y = INT_MAX;
    ed.x11mod = ed.wamod = 0;
    
    list<FlagMonitor *>::iterator it = monitors.begin();
    for (; it != monitors.end(); it++) {
        ed.type = WindowStateChangeNotify;
        ws->waimea->eh->evAct(NULL, (*it)->monitor->id, &ed);

        if ((*it)->type) {
            if ((old_wstate & (*it)->type) != (wstate & (*it)->type)) {
                
                if (wstate & (*it)->type) ed.type = StateAddNotify;
                else ed.type = StateRemoveNotify;
            
                ws->waimea->eh->evAct(NULL, (*it)->monitor->id, &ed);
            }
        }
    }
}

WaFrameWindow::WaFrameWindow(WaWindow *wa_win, WaStringMap *sm) :
    RootWindowObject(wa_win->ws, 0, WindowFrameType, sm, "frame") {
    XSetWindowAttributes attrib_set;
    
    wa = wa_win;

    int create_mask = CWOverrideRedirect | CWEventMask | CWColormap;

    if (ws->screen_depth == 32) {
        attrib_set.background_pixmap = None;
        attrib_set.background_pixel = 0x00000000;
        attrib_set.border_pixel = 0x00000000;
        create_mask |= CWBackPixel | CWBorderPixel;
    } else {
        attrib_set.background_pixmap = ParentRelative;
    }
    create_mask |= CWBackPixmap;
    
    attrib_set.colormap = ws->colormap;
    attrib_set.override_redirect = true;
    attrib_set.event_mask = SubstructureRedirectMask |
      StructureNotifyMask | SubstructureNotifyMask |
      EnterWindowMask | LeaveWindowMask;

    attrib.x = wa->attrib.x;
    attrib.y = wa->attrib.y;
    attrib.width = wa->attrib.width;
    attrib.height = wa->attrib.height;
    
    id = XCreateWindow(ws->display, ws->id, attrib.x, attrib.y,
                       attrib.width, attrib.height, 0, ws->screen_depth,
                       InputOutput, ws->visual, create_mask,
                       &attrib_set);
    
#ifdef    SHAPE
    shape_count = 0;
    apply_shape = false;
    apply_shape_buffer = XCreateWindow(ws->display, ws->id, 0, 0,
                                       attrib.width, attrib.height, 0,
                                       CopyFromParent, CopyFromParent,
                                       CopyFromParent, CWOverrideRedirect,
                                       &attrib_set);

#endif // SHAPE
    
    wa->waimea->window_table.insert(make_pair(id, this));
}

WaFrameWindow::~WaFrameWindow(void) {

#ifdef    SHAPE
    while (! shape_infos.empty()) {
        shape_infos.back()->in_list = false;
        shape_infos.pop_back();
    }
    XDestroyWindow(ws->display, apply_shape_buffer);
#endif // SHAPE
    
    wa->waimea->window_table.erase(id);
}

void WaFrameWindow::styleUpdate(bool, bool size_change) {
    if (size_change)
        wa->updateAllAttributes();
    else
        wa->drawDecor();
}

#ifdef    SHAPE
void WaFrameWindow::startRender(void) {
    apply_shape = false;
}

void WaFrameWindow::endRender(Pixmap) {
    apply_shape = true;
    applyShape();
}

void WaFrameWindow::addShapeInfo(ShapeInfo *sinfo) {
    shape_infos.push_back(sinfo);
}

void WaFrameWindow::removeShapeInfo(ShapeInfo *sinfo) {
    shape_infos.remove(sinfo);
    shapeUpdateNotify();
}

void WaFrameWindow::shapeUpdateNotify(void) {
    shape_count++;
    applyShape();
}

void WaFrameWindow::applyShape(void) {
    if (! apply_shape) return;
    if (! shape_count) return;
    
    XShapeCombineMask(ws->display, apply_shape_buffer, ShapeBounding,
                      0, 0, None, ShapeSet);

    XRectangle rect;
    rect.x = rect.y = 0;
    rect.width = attrib.width;
    rect.height = attrib.height;
    
    XShapeCombineRectangles(ws->display, apply_shape_buffer, ShapeBounding,
                            0, 0, &rect, 1, ShapeSubtract, Unsorted);

    list<ShapeInfo *>::iterator it = shape_infos.begin();
    for (; it != shape_infos.end(); it++)
        XShapeCombineShape(ws->display, apply_shape_buffer, ShapeBounding,
                           (*it)->xoff, (*it)->yoff, (*it)->window,
                           ShapeBounding, ShapeUnion);
    
    XShapeCombineShape(ws->display, id, ShapeBounding,
                       0, 0, apply_shape_buffer, ShapeBounding, ShapeSet);
    shape_count = 0;
}

ShapeInfo::ShapeInfo(Window _window) {
    xoff = 0;
    yoff = 0;
    in_list = false;
    window = _window;
}

void ShapeInfo::setShapeOffset(int newxoff, int newyoff) {
    xoff = newxoff;
    yoff = newyoff;
}
#endif // SHAPE


syntax highlighted by Code2HTML, v. 0.9.1