/* Net.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" {
#include <X11/Xatom.h>

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

#include "Net.hh"

NetHandler::NetHandler(Waimea *wa) {
    waimea = wa;
    display = waimea->display;
    size_hints = XAllocSizeHints();
    classhint = XAllocClassHint();
    
    utf8_string = XInternAtom(display, "UTF8_STRING", false);

    wm_protocols = XInternAtom(display, "WM_PROTOCOLS", false);
    wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", false);
    wm_take_focus = XInternAtom(display, "WM_TAKE_FOCUS", false);
    wm_state = XInternAtom(display, "WM_STATE", false);
    wm_change_state = XInternAtom(display, "WM_CHANGE_STATE", false);

    mwm_hints = XInternAtom(display, "_MOTIF_WM_HINTS", false);
 
    net_supported = XInternAtom(display, "_NET_SUPPORTED", false);
    net_supported_wm_check =
        XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", false);

    net_client_list = XInternAtom(display, "_NET_CLIENT_LIST", false);
    net_client_list_stacking =
        XInternAtom(display, "_NET_CLIENT_LIST_STACKING", false);
    net_active_window = XInternAtom(display, "_NET_ACTIVE_WINDOW", false);

    net_desktop_viewport =
        XInternAtom(display, "_NET_DESKTOP_VIEWPORT", false);
    net_desktop_geometry =
        XInternAtom(display, "_NET_DESKTOP_GEOMETRY", false);
    net_current_desktop = XInternAtom(display, "_NET_CURRENT_DESKTOP", false);
    net_number_of_desktops =
        XInternAtom(display, "_NET_NUMBER_OF_DESKTOPS", false);
    net_desktop_names = XInternAtom(display, "_NET_DESKTOP_NAMES", false);
    net_workarea = XInternAtom(display, "_NET_WORKAREA", false);
    
    net_wm_desktop = XInternAtom(display, "_NET_WM_DESKTOP", false);
    net_wm_name = XInternAtom(display, "_NET_WM_NAME", false);
    net_wm_visible_name = XInternAtom(display, "_NET_WM_VISIBLE_NAME", false);
    net_wm_strut = XInternAtom(display, "_NET_WM_STRUT", false);
    net_wm_strut_partial = XInternAtom(display, "_NET_WM_STRUT_PARTIAL",
                                       false);
    net_wm_pid = XInternAtom(display, "_NET_WM_PID", false);
    net_wm_user_time = XInternAtom(display, "_NET_WM_USER_TIME", false);
    
    net_wm_state = XInternAtom(display, "_NET_WM_STATE", false);
    net_wm_state_sticky = XInternAtom(display, "_NET_WM_STATE_STICKY", false);
    net_wm_state_shaded = XInternAtom(display, "_NET_WM_STATE_SHADED", false);
    net_wm_state_hidden = XInternAtom(display, "_NET_WM_STATE_HIDDEN", false);
    net_wm_maximized_vert =
        XInternAtom(display, "_NET_WM_STATE_MAXIMIZED_VERT", false);
    net_wm_maximized_horz =
        XInternAtom(display, "_NET_WM_STATE_MAXIMIZED_HORZ", false);
    net_wm_state_above = XInternAtom(display, "_NET_WM_STATE_ABOVE", false);
    net_wm_state_below = XInternAtom(display, "_NET_WM_STATE_BELOW", false);
    net_wm_state_stays_on_top =
        XInternAtom(display, "_NET_WM_STATE_STAYS_ON_TOP", false);
    net_wm_state_stays_at_bottom =
        XInternAtom(display, "_NET_WM_STATE_STAYS_AT_BOTTOM", false);
    net_wm_state_fullscreen =
        XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", false);
    net_wm_state_skip_taskbar =
        XInternAtom(display, "_NET_WM_STATE_SKIP_TASKBAR", false);

    net_wm_allowed_actions =
        XInternAtom(display, "_NET_WM_ALLOWED_ACTIONS", false);
    net_wm_action_move = XInternAtom(display, "_NET_WM_ACTION_MOVE", false);
    net_wm_action_resize =
        XInternAtom(display, "_NET_WM_ACTION_RESIZE", false);
    net_wm_action_minimize =
        XInternAtom(display, "_NET_WM_ACTION_MINIMIZE", false);
    net_wm_action_shade = XInternAtom(display, "_NET_WM_ACTION_SHADE", false);
    net_wm_action_stick = XInternAtom(display, "_NET_WM_ACTION_STICK", false);
    net_wm_action_maximize_horz =
        XInternAtom(display, "_NET_WM_ACTION_MAXIMIZE_HORZ", false);
    net_wm_action_maximize_vert =
        XInternAtom(display, "_NET_WM_ACTION_MAXIMIZE_VERT", false);
    net_wm_action_fullscreen =
        XInternAtom(display, "_NET_WM_ACTION_FULLSCREEN", false);
    net_wm_action_change_desktop =
        XInternAtom(display, "_NET_WM_ACTION_CHANGE_DESKTOP", false);
    net_wm_action_close =
        XInternAtom(display, "_NET_WM_ACTION_CLOSE", false);

    net_wm_window_type = XInternAtom(display, "_NET_WM_WINDOW_TYPE", false);
    net_wm_window_type_desktop =
        XInternAtom(display, "_NET_WM_WINDOW_TYPE_DESKTOP", false);
    net_wm_window_type_dock =
        XInternAtom(display, "_NET_WM_WINDOW_TYPE_DOCK", false);
    net_wm_window_type_toolbar =
        XInternAtom(display, "_NET_WM_WINDOW_TYPE_TOOLBAR", false);
    net_wm_window_type_menu =
        XInternAtom(display, "_NET_WM_WINDOW_TYPE_MENU", false);
    net_wm_window_type_splash =
        XInternAtom(display, "_NET_WM_WINDOW_TYPE_SPLASH", false);
    net_wm_window_type_normal =
        XInternAtom(display, "_NET_WM_WINDOW_TYPE_NORMAL", false);
    net_wm_window_type_dialog =
        XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", false);
    net_wm_window_type_utility =
        XInternAtom(display, "_NET_WM_WINDOW_TYPE_UTILITY", false);

    net_close_window = XInternAtom(display, "_NET_CLOSE_WINDOW", false);
    net_moveresize_window =
        XInternAtom(display, "_NET_MOVERESIZE_WINDOW", false);
    net_wm_moveresize = XInternAtom(display, "_NET_WM_MOVERESIZE", false);

    net_wm_icon = XInternAtom(display, "_NET_WM_ICON", false);
    net_wm_icon_image = XInternAtom(display, "_NET_WM_ICON_IMAGE", false);
    net_wm_icon_svg = XInternAtom(display, "_NET_WM_ICON_SVG", false);
    
    waimea_net_wm_state_decor =
        XInternAtom(display, "_WAIMEA_NET_WM_STATE_DECOR", false);
    waimea_net_wm_state_decortitle =
        XInternAtom(display, "_WAIMEA_NET_WM_STATE_DECOR_TITLE", false);
    waimea_net_wm_state_decorborder =
        XInternAtom(display, "_WAIMEA_NET_WM_STATE_DECOR_BORDER", false);
    waimea_net_wm_state_decorhandles =
        XInternAtom(display, "_WAIMEA_NET_WM_STATE_DECOR_HANDLES", false);    
    
    waimea_net_maximized_restore =
        XInternAtom(display, "_WAIMEA_NET_MAXIMIZED_RESTORE", false);
    waimea_net_virtual_pos =
        XInternAtom(display, "_WAIMEA_NET_VIRTUAL_POS", false);
    waimea_net_wm_desktop_mask =
        XInternAtom(display, "_WAIMEA_NET_WM_DESKTOP_MASK", false);
    
    waimea_net_wm_merged_to =
        XInternAtom(display, "_WAIMEA_NET_WM_MERGED_TO", false);
    waimea_net_wm_merged_type =
        XInternAtom(display, "_WAIMEA_NET_WM_MERGED_TYPE", false);
    waimea_net_wm_merge_order =
        XInternAtom(display, "_WAIMEA_NET_WM_MERGE_ORDER", false);
    waimea_net_wm_merge_atfront =
        XInternAtom(display, "_WAIMEA_NET_WM_MERGE_ATFRONT", false);

    waimea_net_restart = XInternAtom(display, "_WAIMEA_NET_RESTART", false);
    waimea_net_shutdown = XInternAtom(display, "_WAIMEA_NET_SHUTDOWN", false);
    
    xdndaware = XInternAtom(display, "XdndAware", false);
    xdndenter = XInternAtom(display, "XdndEnter", false);
    xdndleave = XInternAtom(display, "XdndLeave", false);

    kde_net_system_tray_windows =
        XInternAtom(display, "_KDE_NET_SYSTEM_TRAY_WINDOWS", false);
    kde_net_wm_system_tray_window_for =
        XInternAtom(display, "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", false);
    
    xrootpmap_id = XInternAtom(display, "_XROOTPMAP_ID", false);

    waimea_net_event_notify =
        XInternAtom(display, "_WAIMEA_NET_EVENT_NOTIFY", false);

    waimea_net_dockapp_holder =
        XInternAtom(display, "_WAIMEA_NET_DOCKAPP_HOLDER", false);
    waimea_net_dockapp_prio =
        XInternAtom(display, "_WAIMEA_NET_DOCKAPP_PRIO", false);

    waimea_net_cfg = XInternAtom(display, "_WAIMEA_NET_CFG", false);

    cme.type = ClientMessage;
    cme.display = display;
    cme.format = 32;
    cme.data.l[0] = cme.data.l[1] = cme.data.l[2] = cme.data.l[3] =
        cme.data.l[4] = 0L;

    waimea_xrootpmap_id = None;
}

void NetHandler::getWMProtocols(WaWindow *ww) {
    int n;
    Atom *protocols;
    
    if (XGetWMProtocols(display, ww->id, &protocols, &n)) {
        for (int i = 0; i < n; i++) {
            if (protocols[i] == wm_delete_window)
                ww->protocol_mask |= DeleteWindowProtocalMask;
            else if (protocols[i] == wm_take_focus)
                ww->protocol_mask |= TakeFocusProtocalMask;
        }
        XFree(protocols);
    }
}

void NetHandler::getWMHints(WaWindow *ww) {
    XWMHints *wm_hints = NULL;
    ww->state = NormalState;

    wm_hints = XGetWMHints(display, ww->id);
    
    if (wm_hints) {
        if (wm_hints->flags & StateHint) {
            ww->state = wm_hints->initial_state;
            if (ww->state != IconicState)
                ww->state = NormalState;
        }
        if (wm_hints->flags & InputHint) {
            ww->input_field = wm_hints->input;
        }
        
        if (wm_hints->flags & WindowGroupHint) {
            if (ww->window_group != wm_hints->window_group) {
                ww->wstate &= ~StateGroupLeaderMask;
                ww->wstate &= ~StateGroupMemberMask;
                
                ww->window_group = wm_hints->window_group;
                
                if (ww->window_group == ww->id)
                    ww->wstate |= StateGroupLeaderMask;
                else
                    ww->wstate |= StateGroupMemberMask;
            }
        } else {
            if (ww->window_group) {
                ww->wstate &= ~StateGroupLeaderMask;
                ww->wstate &= ~StateGroupMemberMask;
            }
            ww->window_group = None;
        }

        if (wm_hints->flags & UrgencyHint)
            ww->wstate |= StateUrgentMask;
        else
            ww->wstate &= ~StateUrgentMask;

        XFree(wm_hints);

        ww->windowStateCheck();
    }
}

void NetHandler::getClassHint(WaWindow *ww) {
    int status = 0;
    
    status = XGetClassHint(ww->display, ww->id, classhint);
    
    if (status) {
        if (classhint->res_class) {
            char *newwclass = wa_locale_to_utf8((char *) classhint->res_class);
            if (newwclass) {
                if (ww->frame) DWIN_RENDER_GET(ww->frame);
                delete [] ww->wclass;
                ww->wclass = newwclass;
                if (ww->frame) DWIN_RENDER_RELEASE(ww->frame);
            }            
            XFree(classhint->res_class);
        }
        if (classhint->res_name) {            
            char *newwclassname =
                wa_locale_to_utf8((char *) classhint->res_name);
            if (newwclassname) {
                if (ww->frame) DWIN_RENDER_GET(ww->frame);
                delete [] ww->wclassname;
                ww->wclassname = newwclassname;
                if (ww->frame) DWIN_RENDER_RELEASE(ww->frame);
            }
            XFree(classhint->res_name);
        }
    }
}

void NetHandler::getWMClientMachineHint(WaWindow *ww) {
    int status = 0, n;
    XTextProperty text_prop;
    char **cl;
    
    status = XGetWMClientMachine(ww->display, ww->id, &text_prop);
    
    if (status) {
	     if (text_prop.encoding == XA_STRING) {
            char *newhostname =
                wa_locale_to_utf8((char *) text_prop.value);
            if (newhostname) {
                if (ww->frame) DWIN_RENDER_GET(ww->frame);
                delete [] ww->host;
                ww->host = newhostname;
                if (ww->frame) DWIN_RENDER_RELEASE(ww->frame);
            }
        } else {

#ifndef   X_HAVE_UTF8_STRING
#  define Xutf8TextPropertyToTextList XmbTextPropertyToTextList
#endif // !X_HAVE_UTF8_STRING

            Xutf8TextPropertyToTextList(display, &text_prop, &cl, &n);
            if (cl) {
                if (ww->frame) DWIN_RENDER_GET(ww->frame);
                delete [] ww->host;
                ww->host = WA_STRDUP(cl[0]);
                if (ww->frame) DWIN_RENDER_RELEASE(ww->frame);
                XFreeStringList(cl);
            }
        }
    }
}

void NetHandler::getTransientForHint(WaWindow *ww) {
    int status = 0;
    Window trans;

    Window was_transient_for = ww->transient_for;
    ww->withdrawTransient();
    
    status = XGetTransientForHint(display, ww->id, &trans);
    
    if (ww->ws->config.transient_above) {
        if (status && (trans != ww->id)) {
            if (trans == None || trans == ww->ws->id) {
                ww->transient_for = ww->ws->id;
                list<WaWindow *>::iterator it = ww->ws->wawindow_list.begin();
                for (; it != ww->ws->wawindow_list.end(); ++it)
                    if ((*it) != ww) (*it)->transients.push_back(ww->id);
            } else {
                WaWindow *transfor = (WaWindow *)
                    waimea->findWin(trans, WindowType);
                if (transfor) {
                    if (transfor != ww) {
                        ww->transient_for = trans;
                        transfor->transients.push_back(ww->id);
                    }
                } else if (ww->window_group) {
                    list<WaWindow *>::iterator it =
                        ww->ws->wawindow_list.begin();
                    for (;it != ww->ws->wawindow_list.end(); ++it) {
                        if ((*it) != ww &&
                            (*it)->window_group == ww->window_group &&
                            (*it)->transient_for == (Window) 0)
                            (*it)->transients.push_back(ww->id);
                    }
                }
            }
        }
    }

    if (status && (trans != ww->id))
        ww->setWindowState(ww->wstate | StateTransientMask);
    else if (was_transient_for)
        ww->setWindowState(ww->wstate & ~StateTransientMask);
}

void NetHandler::getMWMHints(WaWindow *ww) {
    MwmHints *mwmhints;
    int status = Success - 1;
    
    status = XGetWindowProperty(display, ww->id, mwm_hints, 0L, 20L,
                                false, mwm_hints, &real_type,
                                &real_format, &items_read, &items_left,
                                (unsigned char **) &mwmhints);

    if (status == Success && items_read) {
        if (items_read >= PropMotifWmHintsElements) {
            if (mwmhints->flags & MwmHintsDecorations) {
                if (mwmhints->decorations & MwmDecorAll) {
                    ww->decorAllOn();
                } else {
                    long decormask = 0L;
                    if (mwmhints->decorations & MwmDecorTitle)
                        decormask |= DecorTitleMask;
                    if (mwmhints->decorations & MwmDecorBorder)
                        decormask |= DecorBorderMask;
                    if (mwmhints->decorations & MwmDecorHandle)
                        decormask |= DecorHandlesMask;
                    ww->setDecor(decormask);
                }
            }
        }
        XFree(mwmhints);
    }
}

void NetHandler::getWMNormalHints(WaWindow *ww) {
    long dummy;
    int status = 0;

    ww->size.max_width = ww->size.max_height = 65536;
    ww->size.min_width = ww->size.min_height = ww->size.width_inc =
        ww->size.height_inc = 1;
    ww->size.base_size = false;
    ww->size.base_width = ww->size.base_height = 0;
    ww->size.win_gravity = NorthWestGravity;
    ww->size.min_aspect = ww->size.max_aspect = 0.0;
    ww->size.aspect = false;

    size_hints->flags = 0;

    status = XGetWMNormalHints(display, ww->id, size_hints, &dummy);
    
    if (status) {
        if (size_hints->flags & (PPosition | USPosition))
            ww->wstate |= StatePositionedMask;
        if (size_hints->flags & PBaseSize) {
            ww->size.base_size = true;
            ww->size.base_width = size_hints->base_width;
            ww->size.base_height = size_hints->base_height;
        }
        if (size_hints->flags & PMinSize) {
            if (size_hints->min_width >= 0)
                ww->size.min_width = size_hints->min_width;
            if (size_hints->min_height >= 0)
                ww->size.min_height = size_hints->min_height;
        } else if (ww->size.base_size) {
            ww->size.min_width = ww->size.base_width;
            ww->size.min_height = ww->size.base_height;
        }
        if (size_hints->flags & PMaxSize) {
            if (size_hints->max_width > (signed int) ww->size.max_width)
                ww->size.max_width = size_hints->max_width;
            else
                ww->size.max_width = ww->size.min_width;
            
            if (size_hints->max_height > (signed int) ww->size.max_height)
                ww->size.max_height = size_hints->max_height;
            else
                ww->size.max_height = ww->size.min_height;
        }
        if (size_hints->flags & PResizeInc) {
            ww->size.width_inc = size_hints->width_inc;
            ww->size.height_inc = size_hints->height_inc;
        }
        if (size_hints->flags & PWinGravity)
            ww->size.win_gravity = size_hints->win_gravity;
        
        if (size_hints->flags & PAspect) {
            ww->size.min_aspect = (double) size_hints->min_aspect.x /
                (double) size_hints->min_aspect.y;
            ww->size.max_aspect = (double) size_hints->max_aspect.x /
                (double) size_hints->max_aspect.y;
            if (ww->size.min_aspect <= ww->size.max_aspect)
                ww->size.aspect = true;
        }
    }
    ww->size.base_width = (ww->size.base_width) ? ww->size.base_width :
        ww->size.min_width;
    ww->size.base_height = (ww->size.base_height) ? ww->size.base_height :
        ww->size.min_height;
}

void NetHandler::getState(WaWindow *ww) {
    CARD32 *data;

    ww->state = WithdrawnState;

    if (XGetWindowProperty(display, ww->id, wm_state, 0L, 1L, false,
                           wm_state, &real_type, &real_format, &items_read,
                           &items_left, (unsigned char **) &data) ==
        Success && items_read) {
        ww->state = *data;
        XFree(data);
    } else ww->deleted = true;
}

void NetHandler::setState(WaWindow *ww, int newstate) {
    CARD32 data[2];
    
    ww->state = newstate;
    switch (ww->state) {
        case IconicState:
            ww->wstate |= StateMinimizedMask;
            ww->hide();
            setWmState(ww);
            break;
        case NormalState:
            ww->wstate &= ~StateMinimizedMask;
            if (! ww->mapped) ww->mapWindow();
            else if (ww->desktop_mask &
                     (1L << ww->ws->current_desktop->number))
                ww->show();
            setWmState(ww);
    }
    if (ww->want_focus && ww->mapped && (! ww->hidden))
        ww->waimea->focusNew(ww->id, false);

    ww->want_focus = false;
    
    data[0] = ww->state;
    data[1] = None;

    XChangeProperty(display, ww->id, wm_state, wm_state,
                    32, PropModeReplace, (unsigned char *) data, 2);

    ww->sendConfig();
}

void NetHandler::getWmState(WaWindow *ww) {
    CARD32 *data;
    bool vert = false, horz = false, shaded = false, decor = false;
    unsigned int i;
    long decormask = 0L;
    int status = Success - 1;

    status = XGetWindowProperty(display, ww->id, net_wm_state, 0L, 13L,
                                false, XA_ATOM, &real_type,
                                &real_format, &items_read, &items_left, 
                                (unsigned char **) &data);

    if (status == Success && items_read) {
        for (i = 0; i < items_read; i++) {
            if (data[i] == net_wm_state_sticky)
                ww->wstate |= StateStickyMask;
            else if (data[i] == net_wm_state_shaded) shaded = true;
            else if (data[i] == net_wm_maximized_vert) vert = true;
            else if (data[i] == net_wm_maximized_horz) horz = true;
            else if (data[i] == net_wm_state_hidden)
                ww->wstate |= StateMinimizedMask;
            else if (data[i] == net_wm_state_skip_taskbar)
                ww->wstate &= ~StateTasklistMask;
            else if (data[i] == net_wm_state_above ||
                     data[i] == net_wm_state_stays_on_top)
                ww->alwaysontopOn();
            else if (data[i] == net_wm_state_below ||
                     data[i] == net_wm_state_stays_at_bottom)
                ww->alwaysatbottomOn();
            else if (data[i] == net_wm_state_fullscreen)
                ww->wstate |= StateFullscreenMask;
            else if (data[i] == waimea_net_wm_state_decor) decor = true;
            else if (data[i] == waimea_net_wm_state_decortitle)
                decormask |= DecorTitleMask;
            else if (data[i] == waimea_net_wm_state_decorborder)
                decormask |= DecorBorderMask;
            else if (data[i] == waimea_net_wm_state_decorhandles)
                decormask |= DecorHandlesMask;
        }
        if (decor) ww->setDecor(decormask);
        XFree(data);
    }
    
    if (vert && horz) {
        status = Success - 1;
        status = XGetWindowProperty(display, ww->id,
                                    waimea_net_maximized_restore,
                                    0L, 6L, false, XA_CARDINAL, &real_type,
                                    &real_format, &items_read,
                                    &items_left, (unsigned char **) &data);
        
        if (status == Success && items_read) {
            if (items_read >= 6) {
                ww->_maximize(data[4], data[5]);
                ww->restore_max.x = data[0];
                ww->restore_max.y = data[1];
                ww->restore_max.width = data[2];
                ww->restore_max.height = data[3];
            }
            XFree(data);
        }
    }
    if (shaded) ww->wstate |= StateShadedMask;

    ww->windowStateCheck();
}

void NetHandler::setWmState(WaWindow *ww) {
    int i = 0;
    CARD32 data[16];
    CARD32 data2[6];
    
    if (ww->wstate & StateStickyMask) data[i++] = net_wm_state_sticky;
    if (ww->wstate & StateShadedMask) data[i++] = net_wm_state_shaded;
    if (ww->wstate & StateAlwaysOnTopMask) {
        data[i++] = net_wm_state_above;
        data[i++] = net_wm_state_stays_on_top;
    }
    if (ww->wstate & StateAlwaysAtBottomMask) {
        data[i++] = net_wm_state_below;
        data[i++] = net_wm_state_stays_at_bottom;
    }
    if (ww->wstate & StateMinimizedMask) data[i++] = net_wm_state_hidden;
    if (ww->wstate & StateFullscreenMask) data[i++] = net_wm_state_fullscreen;
    if (ww->wstate & StateMaximizedMask) {
        data[i++] = net_wm_maximized_vert;
        data[i++] = net_wm_maximized_horz;
            
        data2[0] = ww->restore_max.x;
        data2[1] = ww->restore_max.y;
        data2[2] = ww->restore_max.width;
        data2[3] = ww->restore_max.height;
        data2[4] = ww->restore_max.misc0;
        data2[5] = ww->restore_max.misc1;

        XChangeProperty(display, ww->id, waimea_net_maximized_restore,
                        XA_CARDINAL, 32, PropModeReplace,
                        (unsigned char *) data2, 6);
    } else
        XDeleteProperty(display, ww->id, waimea_net_maximized_restore);
    
    data[i++] = waimea_net_wm_state_decor;
    if (ww->wstate & StateDecorTitleMask)
        data[i++] = waimea_net_wm_state_decortitle;
    if (ww->wstate & StateDecorBorderMask)
        data[i++] = waimea_net_wm_state_decorborder;
    if (ww->wstate & StateDecorHandlesMask)
        data[i++] = waimea_net_wm_state_decorhandles;
    
    XChangeProperty(display, ww->id, net_wm_state, XA_ATOM, 32,
                    PropModeReplace, (unsigned char *) data, i);
    
    ww->windowStateCheck();
}

void NetHandler::setSupported(WaScreen *ws) {
    CARD32 data[54];
    int i = 0;
    
    data[i++] = net_supported;
    data[i++] = net_supported_wm_check;
    
    data[i++] = net_client_list;
    data[i++] = net_client_list_stacking;
    data[i++] = net_active_window;
    
    data[i++] = net_desktop_viewport;
    data[i++] = net_desktop_geometry;
    data[i++] = net_current_desktop;
    data[i++] = net_number_of_desktops;
    data[i++] = net_desktop_names;
    data[i++] = net_workarea;
    
    data[i++] = net_wm_desktop;
    data[i++] = net_wm_name;
    data[i++] = net_wm_visible_name;
    data[i++] = net_wm_strut;
    data[i++] = net_wm_strut_partial;
    data[i++] = net_wm_pid;
    data[i++] = net_wm_user_time;
    
    data[i++] = net_wm_state;
    data[i++] = net_wm_state_sticky;
    data[i++] = net_wm_state_shaded;
    data[i++] = net_wm_state_hidden;
    data[i++] = net_wm_maximized_vert;
    data[i++] = net_wm_maximized_horz;
    data[i++] = net_wm_state_above;
    data[i++] = net_wm_state_below;
    data[i++] = net_wm_state_stays_on_top;
    data[i++] = net_wm_state_stays_at_bottom;
    data[i++] = net_wm_state_fullscreen;
    data[i++] = net_wm_state_skip_taskbar;

    data[i++] = net_wm_allowed_actions;
    data[i++] = net_wm_action_move;
    data[i++] = net_wm_action_resize;
    data[i++] = net_wm_action_minimize;
    data[i++] = net_wm_action_shade;
    data[i++] = net_wm_action_stick;
    data[i++] = net_wm_action_maximize_horz;
    data[i++] = net_wm_action_maximize_vert;
    data[i++] = net_wm_action_fullscreen;
    data[i++] = net_wm_action_change_desktop;
    data[i++] = net_wm_action_close;
    
    data[i++] = net_wm_window_type;
    data[i++] = net_wm_window_type_desktop;
    data[i++] = net_wm_window_type_dock;
    data[i++] = net_wm_window_type_toolbar;
    data[i++] = net_wm_window_type_menu;
    data[i++] = net_wm_window_type_splash;
    data[i++] = net_wm_window_type_dialog;
    data[i++] = net_wm_window_type_utility;
    data[i++] = net_wm_window_type_normal;
    
    data[i++] = net_close_window;
    data[i++] = net_moveresize_window;
    data[i++] = net_wm_moveresize;
    
    XChangeProperty(display, ws->id, net_supported, XA_ATOM, 32,
                    PropModeReplace, (unsigned char *) data, i);
}

void NetHandler::setSupportedWMCheck(WaScreen *ws, Window child) {
    XChangeProperty(display, ws->id, net_supported_wm_check, XA_WINDOW, 32,
                    PropModeReplace, (unsigned char *) &child, 1);

    XChangeProperty(display, child, net_supported_wm_check, XA_WINDOW, 32,
                    PropModeReplace, (unsigned char *) &child, 1);

    XChangeProperty(display, child, net_wm_name, utf8_string, 8,
                    PropModeReplace, (unsigned char *) PACKAGE,
                    strlen(PACKAGE));
}

void NetHandler::setClientList(WaScreen *ws) {
    CARD32 *data;
    int i = 0;

    data = new CARD32[ws->wawindow_list_map_order.size() + 1];

    list<WaWindow *>::iterator it =
        ws->wawindow_list_map_order.begin();
    for (; it != ws->wawindow_list_map_order.end(); ++it) {
        data[i++] = (*it)->id;
    }

    XChangeProperty(display, ws->id, net_client_list, XA_WINDOW, 32,
                    PropModeReplace, (unsigned char *) data, i);

    delete [] data;
}

void NetHandler::setClientListStacking(WaScreen *ws) {
    CARD32 *data;
    int i = 0;
    WaFrameWindow *wf;

    data = new CARD32[ws->wawindow_list.size() + 1];

    list<Window>::reverse_iterator it = ws->aab_stacking_list.rbegin();
    for (; it != ws->aab_stacking_list.rend(); ++it) {
        wf = (WaFrameWindow *) waimea->findWin(*it, WindowFrameType);
        if (wf) data[i++] = wf->wa->id;
    }
    it = ws->stacking_list.rbegin();
    for (; it != ws->stacking_list.rend(); ++it) {
        wf = (WaFrameWindow *) waimea->findWin(*it, WindowFrameType);
        if (wf) data[i++] = wf->wa->id;
    }
    it = ws->aot_stacking_list.rbegin();
    for (; it != ws->aot_stacking_list.rend(); ++it) {
        wf = (WaFrameWindow *) waimea->findWin(*it, WindowFrameType);
        if (wf) data[i++] = wf->wa->id;
    }
    
    XChangeProperty(display, ws->id, net_client_list_stacking, XA_WINDOW,
                    32, PropModeReplace, (unsigned char *) data, i);
    
    delete [] data;
}

void NetHandler::getClientListStacking(WaScreen *ws) {
    CARD32 *data;
    unsigned int i;
    
    if (XGetWindowProperty(display, ws->id, net_client_list_stacking,
                           0L, 8192L,
                           false, XA_WINDOW, &real_type,
                           &real_format, &items_read, &items_left, 
                           (unsigned char **) &data) == Success && 
        items_read) {
        for (i = 0; i < items_read; i++) {
            WaWindow *ww = (WaWindow *) waimea->findWin(data[i], WindowType);
            if (ww) ws->raiseWindow(ww->frame->id, false);
        }
    }
}

void NetHandler::setActiveWindow(WaScreen *ws, WaWindow *ww) {
    CARD32 *data;
    list<WaWindow *>::iterator it;
    int i = 0;

    data = new CARD32[ws->wawindow_list.size() + 1];
    
    if (ww) {
        ws->focused = false;
        ws->wawindow_list.remove(ww);
        ws->wawindow_list.push_front(ww);
    } else
        data[i++] = None;

    it = ws->wawindow_list.begin();
    for (; it != ws->wawindow_list.end(); ++it)
        data[i++] = (*it)->id;
    
    XChangeProperty(display, ws->id, net_active_window, XA_WINDOW, 32,
                    PropModeReplace, (unsigned char *) data, i);

    delete [] data;

    sendNotify(ws, ActiveWindowChangeNotify, (ww)? ww->id: 0);
}

void NetHandler::getActiveWindow(WaScreen *ws) {
    WaWindow *ww;
    CARD32 *data;
    int i;
    
    if (XGetWindowProperty(display, ws->id, net_active_window, 0L,
                           ws->wawindow_list.size(), 
                           false, XA_WINDOW, &real_type, &real_format, 
                           &items_read, &items_left, 
                           (unsigned char **) &data) == Success && 
        items_read) {
        for (i = items_read - 1; i >= 0; i--) {
            if (i == 0 && data[0] == None) {
                ws->waimea->focusNew(ws->id);
                break;
            }
            ww = (WaWindow *) waimea->findWin(data[i], WindowType);
            if (ww) {
                ws->wawindow_list.remove(ww);
                ws->wawindow_list.push_front(ww);
                if (i == 0)
                    ww->waimea->focusNew(ww->id);
            }
        }
        XFree(data);
    }
}

void NetHandler::getVirtualPos(WaWindow *ww) {
    int *data;
    
    if (XGetWindowProperty(display, ww->id, waimea_net_virtual_pos,
                           0L, 2L, false, XA_INTEGER, &real_type,
                           &real_format, &items_read, &items_left, 
                           (unsigned char **) &data) == Success &&
        items_read) {
        if (items_read >= 2) {
            ww->attrib.x = data[0] - ww->ws->v_x;
            ww->attrib.y = data[1] - ww->ws->v_y;
            if (ww->wstate & StateStickyMask) {
                ww->attrib.x = ww->attrib.x % ww->ws->width;
                ww->attrib.y = ww->attrib.y % ww->ws->height;
            }
        }
        XFree(data);
    }
}

void NetHandler::getXaName(WaWindow *ww) {
    int status = 0, n;
    XTextProperty text_prop;
    char **cl;
    
    status = XGetWMName(display, ww->id, &text_prop);
    
    if (status) {
	    if (text_prop.encoding == XA_STRING) {
            char *newname = wa_locale_to_utf8((char *) text_prop.value);
            if (newname) {
                ww->ws->smartNameRemove(ww);
                if (ww->frame) DWIN_RENDER_GET(ww->frame);
                delete [] ww->name;
                ww->name = newname;
                ww->realnamelen = strlen(ww->name);
                ww->ws->smartName(ww);
                if (ww->frame) DWIN_RENDER_RELEASE(ww->frame);
                setVisibleName(ww);
            }
        } else {

#ifndef   X_HAVE_UTF8_STRING
#  define Xutf8TextPropertyToTextList XmbTextPropertyToTextList
#endif // !X_HAVE_UTF8_STRING

            Xutf8TextPropertyToTextList(display, &text_prop, &cl, &n);
            if (cl) {
                ww->ws->smartNameRemove(ww);
                if (ww->frame) DWIN_RENDER_GET(ww->frame);
                delete [] ww->name;
                ww->name = WA_STRDUP(cl[0]);
                ww->realnamelen = strlen(ww->name);
                ww->ws->smartName(ww);
                if (ww->frame) DWIN_RENDER_RELEASE(ww->frame);
                setVisibleName(ww);
                XFreeStringList(cl);
            }
        }
    } else {
        ww->ws->smartNameRemove(ww);
        if (ww->frame) DWIN_RENDER_GET(ww->frame);
        delete [] ww->name;
        ww->name = WA_STRDUP("");
        ww->realnamelen = 0;
        ww->ws->smartName(ww);
        if (ww->frame) DWIN_RENDER_RELEASE(ww->frame);
        setVisibleName(ww);
    }
}

bool NetHandler::getNetName(WaWindow *ww) {
    char *data;
    int status = Success - 1;
    
    status = XGetWindowProperty(display, ww->id, net_wm_name, 0L, 8192L, 
                                false, utf8_string, &real_type,
                                &real_format, &items_read, &items_left, 
                                (unsigned char **) &data);
    
    if (status == Success && items_read) {
        ww->ws->smartNameRemove(ww);
        if (ww->frame) DWIN_RENDER_GET(ww->frame);
        delete [] ww->name;
        ww->name = WA_STRDUP(data);
        ww->realnamelen = strlen(ww->name);
        ww->ws->smartName(ww);
        if (ww->frame) DWIN_RENDER_RELEASE(ww->frame);
        setVisibleName(ww);
        XFree(data);
        return true;
    }
    return false;
}

void NetHandler::setVisibleName(WaWindow *ww) {
    XChangeProperty(display, ww->id, net_wm_visible_name,
                    utf8_string, 8, PropModeReplace,
                    (unsigned char *) ww->name, strlen(ww->name));
}

void NetHandler::removeVisibleName(WaWindow *ww) {
    XDeleteProperty(display, ww->id, net_wm_visible_name);
}

void NetHandler::setVirtualPos(WaWindow *ww) {
    int data[2];
    
    ww->gravitate(RemoveGravity);
    data[0] = ww->ws->v_x + ww->attrib.x;
    data[1] = ww->ws->v_y + ww->attrib.y;
    ww->gravitate(ApplyGravity);

    XChangeProperty(display, ww->id, waimea_net_virtual_pos, XA_INTEGER,
                    32, PropModeReplace, (unsigned char *) data, 2);
    
    list<WaWindow *>::iterator mit = ww->merged.begin();
    for (; mit != ww->merged.end(); mit++) {
        if ((*mit) != ww) setVirtualPos(*mit);
    }
}

void NetHandler::getWmStrut(WaWindow *ww) {
    CARD32 *data;
    WMstrut *wm_strut;
    bool found = false;
    int status = Success - 1;

    status = XGetWindowProperty(display, ww->id, net_wm_strut_partial,
                                0L, 4L, false, XA_CARDINAL, &real_type,
                                &real_format, &items_read, &items_left, 
                                (unsigned char **) &data);
    
    if (status != Success) {
        status = Success - 1;
        status = XGetWindowProperty(display, ww->id, net_wm_strut, 0L, 4L, 
                                    false, XA_CARDINAL, &real_type,
                                    &real_format, &items_read, &items_left, 
                                    (unsigned char **) &data);
    }
    
    if (status == Success && items_read) {
        if (items_read >= 4) {
            list<WMstrut *>::iterator it = ww->ws->strut_list.begin();
            for (; it != ww->ws->strut_list.end(); ++it) {
                if ((*it)->window == ww->id) {
                    (*it)->left = data[0];
                    (*it)->right = data[1];
                    (*it)->top = data[2];
                    (*it)->bottom = data[3];
                    found = true;
                    ww->ws->updateWorkarea();
                }
            }
            if (! found) {
                wm_strut = new WMstrut;
                wm_strut->window = ww->id;
                wm_strut->left = data[0];
                wm_strut->right = data[1];
                wm_strut->top = data[2];
                wm_strut->bottom = data[3];
                ww->wm_strut = wm_strut;
                ww->ws->strut_list.push_back(wm_strut);
                ww->ws->updateWorkarea();
            }
        }
        XFree(data);
    }
}

void NetHandler::getWmPid(WaWindow *ww) {
    char tmp[32];
    CARD32 *data;
    int status = Success - 1;

    status = XGetWindowProperty(ww->display, ww->id, net_wm_pid, 0L, 1L, 
                                false, XA_CARDINAL, &real_type,
                                &real_format, &items_read, &items_left,
                                (unsigned char **) &data);
    
    if (status == Success && items_read) {
        snprintf(tmp, 32, "%d" , (unsigned int) *data);
        if (ww->frame) DWIN_RENDER_GET(ww->frame);
        delete [] ww->pid;
        ww->pid = WA_STRDUP(tmp);
        if (ww->frame) DWIN_RENDER_RELEASE(ww->frame);
        XFree(data);
    }
}

void NetHandler::getWmUserTime(WaWindow *ww) {
    CARD32 *data;
    int status = Success - 1;

    status = XGetWindowProperty(ww->display, ww->id, net_wm_user_time, 0L, 1L, 
                                false, XA_CARDINAL, &real_type,
                                &real_format, &items_read, &items_left,
                                (unsigned char **) &data);
    
    if (status == Success && items_read) {
        if ((unsigned int) *data)
            ww->wstate |= StateUserTimeMask;
        else
            ww->wstate &= ~StateUserTimeMask;
        
        XFree(data);

        ww->windowStateCheck();
    }
}

void NetHandler::getDesktopViewPort(WaScreen *ws) {
    CARD32 *data;
    
    if (XGetWindowProperty(display, ws->id, net_desktop_viewport, 0L, 32L,
                           false, XA_CARDINAL, &real_type,
                           &real_format, &items_read, &items_left, 
                           (unsigned char **) &data) == Success &&
        items_read) {
        if (items_read >= 2 && ((items_read % 2) == 0)) {
            unsigned int i = 0;
            list<Desktop *>::iterator it = ws->desktop_list.begin();
            for (; (i * 2) < items_read &&
                     it != ws->desktop_list.end(); ++it) {
                (*it)->v_x = data[i++];
                (*it)->v_y = data[i++];
            }
            ws->moveViewportTo(ws->current_desktop->v_x,
                               ws->current_desktop->v_y);
        }
        XFree(data);
    }
}

void NetHandler::setDesktopViewPort(WaScreen *ws) {
    CARD32 data[16 * 2];

    unsigned int i = 0;
    list<Desktop *>::iterator it = ws->desktop_list.begin();
    for (; it != ws->desktop_list.end(); ++it, ++i) {
        data[(i * 2) + 0] = (*it)->v_x;
        data[(i * 2) + 1] = (*it)->v_y;
    }
    XChangeProperty(display, ws->id, net_desktop_viewport, XA_CARDINAL, 32,
                    PropModeReplace, (unsigned char *) data, i * 2);
    
    sendNotify(ws, ViewportChangeNotify, ws->current_desktop->v_x,
               ws->current_desktop->v_y);
}

void NetHandler::setDesktopGeometry(WaScreen *ws) {
    CARD32 data[2];

    data[0] = ws->v_xmax + ws->width;
    data[1] = ws->v_ymax + ws->height;    
    XChangeProperty(display, ws->id, net_desktop_geometry, XA_CARDINAL, 32,
                    PropModeReplace, (unsigned char *) data, 2);
}

void NetHandler::setNumberOfDesktops(WaScreen *ws) {
    CARD32 data[1];

    data[0] = ws->desktop_list.size();
    XChangeProperty(display, ws->id, net_number_of_desktops, XA_CARDINAL, 32,
                    PropModeReplace, (unsigned char *) data, 1);
}
    
void NetHandler::setCurrentDesktop(WaScreen *ws) {
    CARD32 data[1];
    
    data[0] = ws->current_desktop->number;
    XChangeProperty(display, ws->id, net_current_desktop, XA_CARDINAL, 32,
                    PropModeReplace, (unsigned char *) data, 1);

    sendNotify(ws, DesktopChangeNotify, ws->current_desktop->number);
}

void NetHandler::getCurrentDesktop(WaScreen *ws) {
    CARD32 *data;

    if (XGetWindowProperty(display, ws->id, net_current_desktop, 0L, 1L, 
                           false, XA_CARDINAL, &real_type, &real_format, 
                           &items_read, &items_left, 
                           (unsigned char **) &data) == Success && 
        items_read) {
        ws->goToDesktop(data[0]);
        XFree(data);
    }
}

void NetHandler::setDesktopNames(WaScreen *ws, char *names) {
    int i;
    
    for (i = 0; i < 8192; i++) {
        if (names[i] == ',') names[i] = '\0';
        else if (names[i] == '\0') break;
    }
    names[i] = '\0';

    if (i)
        XChangeProperty(display, ws->id, net_desktop_names, utf8_string, 8,
                        PropModeReplace, (unsigned char *) names, i + 1);
}

void NetHandler::setWorkarea(WaScreen *ws) {
    CARD32 data[16 * 4];

    unsigned int i = 0;
    list<Desktop *>::iterator it = ws->desktop_list.begin();
    for (; it != ws->desktop_list.end(); ++it, ++i) {
        data[(i * 4) + 0] = (*it)->workarea.x;
        data[(i * 4) + 1] = (*it)->workarea.y;
        data[(i * 4) + 2] = (*it)->workarea.width;
        data[(i * 4) + 3] = (*it)->workarea.height;
    }
    XChangeProperty(waimea->display, ws->id, net_workarea, XA_CARDINAL, 32,
                    PropModeReplace, (unsigned char *) data, i * 4);
}

void NetHandler::wXDNDMakeAwareness(Window window) {
    long int xdnd_version = 3;

    XChangeProperty(waimea->display, window, xdndaware, XA_ATOM,
                    32, PropModeReplace, (unsigned char *) &xdnd_version, 1);
}

void NetHandler::wXDNDClearAwareness(Window window) {
    XDeleteProperty(waimea->display, window, xdndaware);
}

void NetHandler::deleteSupported(WaScreen *ws) {
    XDeleteProperty(display, ws->id, net_desktop_geometry);
    XDeleteProperty(display, ws->id, net_workarea);
    XDeleteProperty(display, ws->id, net_supported_wm_check);
    XDeleteProperty(display, ws->id, net_supported);
}

void NetHandler::getXRootPMapId(WaScreen *ws) {
    CARD32 *data;
    
    int status = XGetWindowProperty(ws->display, ws->id, xrootpmap_id, 0L, 1L, 
                                    false, XA_PIXMAP, &real_type,
                                    &real_format, &items_read, &items_left, 
                                    (unsigned char **) &data);
    
    if (status == Success && items_read) {
        if ((Pixmap) *data != waimea_xrootpmap_id) {
            unsigned int w, h;
            if (validate_drawable((Pixmap) (*data), &w, &h)) {

                RENDER_GET;

                if (waimea_xrootpmap_id)
                    ws->config.external_bg = true;
                
                if (ws->bg_surface) {
                    if (ws->bg_surface->pixmap != waimea_xrootpmap_id)
                        ws->bg_surface->pixmap = None;
                    
                    ws->bg_surface->unref();
                }
                ws->bg_surface = new WaSurface(ws->display, NULL,
                                               (Pixmap) (*data), None, NULL,
                                               w, h);

                if (ws->waimea->running) {
                    ws->destroySubwindows();
                    ws->forceRenderOfWindows(ANY_DECOR_WINDOW_TYPE);
                }

                RENDER_RELEASE;
                
            }
        }
        XFree(data);
    } else {

        RENDER_GET;

        if (ws->bg_surface) ws->bg_surface->unref();
        ws->bg_surface = NULL;

        RENDER_RELEASE;
        
    }
}

void NetHandler::setXRootPMapId(WaScreen *ws, WaSurface *bg_surface) {
    if (! bg_surface) {
        if (ws->bg_surface && ws->bg_surface->pixmap == waimea_xrootpmap_id) {
            XDeleteProperty(ws->display, ws->id, xrootpmap_id);
        }
        return;
    }
    XChangeProperty(ws->display, ws->id, xrootpmap_id, XA_PIXMAP,
                    32, PropModeReplace,
                    (unsigned char *) &bg_surface->pixmap, 1);
    WaSurface *old_bg = ws->bg_surface;

    RENDER_GET_ONE;
    
    ws->bg_surface = bg_surface->ref();

    RENDER_RELEASE;

    if (old_bg) {
        if (old_bg->pixmap && old_bg->pixmap != waimea_xrootpmap_id) {
            XKillClient(ws->display, old_bg->pixmap);
            old_bg->pixmap = None;
        }
        old_bg->unref();
    }

    waimea_xrootpmap_id = bg_surface->pixmap;

    ws->forceRenderOfWindows(ANY_DECOR_WINDOW_TYPE & ~RootType);
}

void NetHandler::getWmType(WaWindow *ww) {
    CARD32 *data;
    int status = Success - 1;
    
    status = XGetWindowProperty(display, ww->id, net_wm_window_type,
                                0L, 8L, false, XA_ATOM,
                                &real_type, &real_format, &items_read,
                                &items_left, (unsigned char **) &data);
    
    if (status == Success && items_read) {
        for (unsigned int i = 0; i < items_read; ++i) {
            if (data[i] == net_wm_window_type_desktop) {
                ww->desktop_mask = (1L << 16) - 1;
                ww->wstate &= ~StateTasklistMask;
                ww->wstate |= StateStickyMask;
                ww->setDecor(0L);
                ww->size.max_width = ww->ws->width;
                ww->size.min_width = ww->ws->width;
                ww->size.max_height = ww->ws->height;
                ww->size.min_height = ww->ws->height;
                ww->attrib.x = 0;
                ww->attrib.y = 0;
                ww->alwaysatbottomOn();
                ww->functions = FunctionDesktopMask;
            }
            else if (data[i] == net_wm_window_type_toolbar ||
                     data[i] == net_wm_window_type_dock) {
                ww->desktop_mask = (1L << 16) - 1;
                ww->wstate &= ~StateTasklistMask;
                ww->wstate |= StateStickyMask;
                ww->setDecor(0L);
                ww->alwaysontopOn();
                ww->functions = FunctionDesktopMask | FunctionSetStackingMask;
            }
            else if (data[i] == net_wm_window_type_splash ||
                     data[i] == net_wm_window_type_menu) {
                ww->wstate &= ~StateTasklistMask;
                ww->setDecor(0L);
                ww->alwaysontopOn();
                ww->functions = FunctionMoveMask | FunctionSetDecorMask |
                    FunctionSetStackingMask | FunctionDesktopMask;
            }
            else {
                if (ww->attrib.x == 0) {
                    if (ww->ws->current_desktop->workarea.x >
                        ww->attrib.x)
                        ww->attrib.x =
                            ww->ws->current_desktop->workarea.x;
                }
                if (ww->attrib.y == 0) {
                    if (ww->ws->current_desktop->workarea.y >
                        ww->attrib.y)
                        ww->attrib.y =
                            ww->ws->current_desktop->workarea.y;
                }
            }
        }
        XFree(data);
        ww->windowStateCheck();
    } else {
        if (ww->attrib.x == 0) {
            if (ww->ws->current_desktop->workarea.x > ww->attrib.x)
                ww->attrib.x = ww->ws->current_desktop->workarea.x;
        }
        if (ww->attrib.y == 0) {
            if (ww->ws->current_desktop->workarea.y > ww->attrib.y)
                ww->attrib.y = ww->ws->current_desktop->workarea.y;
        }
    }
}

void NetHandler::setDesktop(WaWindow *ww) {
    CARD32 data[1];

    data[0] = 0;
    if (ww->desktop_mask & (1L << ww->ws->current_desktop->number))
        data[0] = ww->ws->current_desktop->number;
    else {
        int i;
        for (i = 0; i < 16; i++)
            if (ww->desktop_mask & (1L << i)) {
                data[0] = i;
                break;
            }
    }
    if (ww->desktop_mask == ((1L << 16) - 1))
        data[0] = 0xffffffff;
    
    XChangeProperty(display, ww->id, net_wm_desktop, XA_CARDINAL, 32,
                    PropModeReplace, (unsigned char *) data, 1);
}

void NetHandler::setDesktopMask(WaWindow *ww) {
    CARD32 data[1];

    data[0] = ww->desktop_mask;

    XChangeProperty(display, ww->id, waimea_net_wm_desktop_mask,
                    XA_CARDINAL, 32, PropModeReplace,
                    (unsigned char *) data, 1);
}

void NetHandler::getDesktop(WaWindow *ww) {
    CARD32 *data;

    if (XGetWindowProperty(display, ww->id, net_wm_desktop, 0L, 1L, 
                           false, XA_CARDINAL, &real_type, &real_format, 
                           &items_read, &items_left,
                           (unsigned char **) &data) == Success && 
        items_read) {
        if (data[0] == 0xffffffff || data[0] == 0xfffffffe)
            ww->desktop_mask = ((1L << 16) - 1);
        else if (data[0] < 15) {
            ww->desktop_mask = (1L << data[0]);
        }
        XFree(data);
    }
    if (XGetWindowProperty(display, ww->id, waimea_net_wm_desktop_mask,
                           0L, 1L, false, XA_CARDINAL, &real_type,
                           &real_format, &items_read, &items_left, 
                           (unsigned char **) &data) == Success && 
        items_read) {
        ww->desktop_mask = data[0];
        XFree(data);
    }
}

bool NetHandler::isSystrayWindow(Window w) {
    CARD32 *data;
    
    items_read = 0;
    if (XGetWindowProperty(display, w, kde_net_wm_system_tray_window_for,
                           0L, 1L, false, XA_WINDOW, &real_type,
                           &real_format, &items_read, &items_left,
                           (unsigned char **) &data) != Success) {
        items_read = 0;
    }
    
    return ((items_read)? true: false);
}

void NetHandler::setSystrayWindows(WaScreen *ws) {
    CARD32 *data;
    int i = 0;

    data = new CARD32[ws->systray_window_list.size() + 1];

    list<Window>::iterator it = ws->systray_window_list.begin();
    for (; it != ws->systray_window_list.end(); it++)
        data[i++] = *it;

    XChangeProperty(display, ws->id, kde_net_system_tray_windows, XA_WINDOW,
                    32, PropModeReplace, (unsigned char *) data, i);

    delete [] data;
}

void NetHandler::getMergedState(WaWindow *ww) {
    CARD32 *data;
    Window mwin = (Window) 0;
    int mtype = NullMergeType;

    if (XGetWindowProperty(display, ww->id, waimea_net_wm_merged_to, 0L,
                           1L, false, XA_WINDOW, &real_type, &real_format, 
                           &items_read, &items_left, 
                           (unsigned char **) &data) == Success && 
        items_read) {
        mwin = *data;
        XFree(data);
        if (XGetWindowProperty(display, ww->id, waimea_net_wm_merged_type,
                               0L, 1L, false, XA_CARDINAL, &real_type,
                               &real_format, &items_read, &items_left, 
                               (unsigned char **) &data) == Success && 
            items_read) {
            mtype = *data;
            XFree(data);
        }
    }
    
    if (mwin) {
        WaWindow *master = (WaWindow *)
            ww->waimea->findWin(mwin, WindowType);
        if (master) {
            if (mtype != VertMergeType && mtype != HorizMergeType)
                master->merge(ww, CloneMergeType);
            else
                master->merge(ww, mtype);
        } else {
            if (! ww->waimea->eh) {
                if (mtype != VertMergeType && mtype != HorizMergeType)
                    ww->ws->mreqs.push_back(new MReq(mwin, ww,
                                                           CloneMergeType));
                else
                    ww->ws->mreqs.push_back(new MReq(mwin, ww, mtype));
            }
        }
    }
}

void NetHandler::setMergedState(WaWindow *ww) {
    CARD32 data[1];

    if (ww->master) {
        data[0] = ww->master->id;
        XChangeProperty(display, ww->id, waimea_net_wm_merged_to,
                        XA_WINDOW, 32, PropModeReplace,
                        (unsigned char *) data, 1);
        data[0] = ww->mergetype;
        XChangeProperty(display, ww->id, waimea_net_wm_merged_type,
                        XA_CARDINAL, 32, PropModeReplace,
                        (unsigned char *) data, 1);
    } else
        XDeleteProperty(display, ww->id, waimea_net_wm_merged_to);
}

void NetHandler::setMergeOrder(WaWindow *ww) {
    CARD32 *data;
    int i = 0;

    data = new CARD32[ww->merged.size()];

    list<WaWindow *>::iterator it = ww->merged.begin();
    for (; it != ww->merged.end(); it++)
        data[i++] = (*it)->id;
    
    if (i) {
        XChangeProperty(display, ww->id, waimea_net_wm_merge_order,
                        XA_WINDOW, 32, PropModeReplace,
                        (unsigned char *) data, i);
    } else
        XDeleteProperty(display, ww->id, waimea_net_wm_merge_order);
}

void NetHandler::getMergeOrder(WaWindow *ww) {
    CARD32 *data;
    int status = Success - 1;
    
    status = XGetWindowProperty(display, ww->id, waimea_net_wm_merge_order,
                                0L, 8192L, false, XA_WINDOW, &real_type,
                                &real_format, &items_read, &items_left,
                                (unsigned char **) &data);
    
    if (status == Success && items_read) {
        int i = items_read;
        for (i--; i >= 0; i--) {
            list<WaWindow *>::iterator it = ww->merged.begin();
            for (; it != ww->merged.end(); it++) {
                if (data[i] == (*it)->id) {
                    ww->merged.erase(it);
                    ww->merged.push_front(*it);
                    break;
                }
            }
        }
        ww->updateAllAttributes();
        XFree(data);
    }
}

void NetHandler::setMergeAtfront(WaWindow *ww, Window win) {
    CARD32 data[1];

    if (ww->ws->shutdown) return;
    
    data[0] = win;
    
    XChangeProperty(display, ww->id, waimea_net_wm_merge_atfront,
                    XA_WINDOW, 32, PropModeReplace,
                    (unsigned char *) data, 1);
}

void NetHandler::getMergeAtfront(WaWindow *ww) {
    CARD32 *data;
    int status = Success - 1;
        
    status = XGetWindowProperty(display, ww->id, waimea_net_wm_merge_atfront,
                                0L, 1L, false, XA_WINDOW, &real_type,
                                &real_format, &items_read, &items_left,
                                (unsigned char **) &data);
    
    if (status == Success && items_read) {
        if (*data == ww->id) ww->toFront();
        else {
            list<WaWindow *>::iterator it = ww->merged.begin();
            for (; it != ww->merged.end(); it++) {
                if ((*it)->id == *data)
                    (*it)->toFront();
            }
        }
        XFree(data);
    }
}

void NetHandler::setAllowedActions(WaWindow *ww) {
    CARD32 data[10];
    int i = 0;

    if (ww->functions & FunctionMoveMask)
        data[i++] = net_wm_action_move;

    if (ww->functions & FunctionResizeMask) {
        data[i++] = net_wm_action_resize;
        data[i++] = net_wm_action_maximize_horz;
        data[i++] = net_wm_action_maximize_vert;
        data[i++] = net_wm_action_fullscreen;
    }
    
    if (ww->functions & FunctionDesktopMask)
        data[i++] = net_wm_action_change_desktop;
    
    data[i++] = net_wm_action_close;
    data[i++] = net_wm_action_minimize;
    data[i++] = net_wm_action_shade;
    data[i++] = net_wm_action_stick;
    
    XChangeProperty(display, ww->id, net_wm_allowed_actions,
                    XA_ATOM, 32, PropModeReplace,
                    (unsigned char *) data, i);
}

void NetHandler::removeAllowedActions(WaWindow *ww) {
    XDeleteProperty(display, ww->id, net_wm_allowed_actions);
}

void NetHandler::sendNotify(AWindowObject *awo, long int event,
                            long int value1, long int value2) {
    if (waimea->running) {
        cme.message_type = waimea_net_event_notify;
        cme.window = awo->ws->id;
        cme.data.l[0] = awo->id;
        cme.data.l[1] = event;
        cme.data.l[2] = value1;
        cme.data.l[3] = value2;
        XSendEvent(display, awo->ws->id, false, StructureNotifyMask,
                   (XEvent *) &cme);
    }
}

#define BEST_ICON_MATCH_SIZE 256
#define MAX_ICON_MATCH_DIFF 5000

void NetHandler::getWmIconImage(WaWindow *ww) {
    CARD32 *data;
    int status = Success - 1;

    status = XGetWindowProperty(ww->display, ww->id, net_wm_icon_image, 0L,
                                1048576L, false, XA_CARDINAL, &real_type,
                                &real_format, &items_read, &items_left, 
                                (unsigned char **) &data);

    if (status != Success || items_read == 0) {
        status = XGetWindowProperty(ww->display, ww->id, net_wm_icon, 0L,
                                    1048576L, false, XA_CARDINAL, &real_type,
                                    &real_format, &items_read, &items_left, 
                                    (unsigned char **) &data);
    }
        
    if (status == Success && items_read >= 1048576L) {
        XFree(data);
        return;
    }
    
    if (status == Success && items_read) {
        unsigned int best_diff = INT_MAX;
        unsigned long *best = NULL;
        unsigned long *icon = (unsigned long *) data;
        unsigned int len;
        while (items_read > 3) {
            unsigned int diff;
            len = icon[0] * icon[1];
            if (len > BEST_ICON_MATCH_SIZE) diff = len - BEST_ICON_MATCH_SIZE;
            else diff = BEST_ICON_MATCH_SIZE - len;
            if (diff < best_diff && items_read >= (len + 2)) {
                best_diff = diff;
                best = icon;
            }
            items_read -= (len + 2);
            icon += (len + 2);
        }
        if (! best || best_diff > MAX_ICON_MATCH_DIFF) {
            XFree(data);
            return;
        }
        
        unsigned int w = best[0];
        unsigned int h = best[1];
        len = w * h;
        unsigned long *argb_data = &best[2];
        unsigned char *pix = new unsigned char[len * sizeof(WaPixel)];
      
        for (unsigned int i = 0; i < len * sizeof(WaPixel);
             i += sizeof(WaPixel)) {
            unsigned long *base = argb_data++;
            unsigned char blue = *base;
            unsigned char green = *base >> 8;
            unsigned char red = *base >> 16;
            unsigned char alpha = *base >> 24;
            WaPixel p;
            
            red = (unsigned) red * (unsigned) alpha / 255;
            green = (unsigned) green * (unsigned) alpha / 255;
            blue = (unsigned) blue * (unsigned) alpha / 255;
            p = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
            memcpy(&pix[i], &p, sizeof(WaPixel));
        }
        
        XFree(data);

        RenderGroup *icon_group = new RenderGroup(ww->ws, NULL);
        RenderOpImage *icon_image = new RenderOpImage();
        icon_image->image = ww->ws->rgbaToWaSurface(pix, w, h);
        icon_image->scale = ImageNormalScaleType;
        icon_group->operations.push_back(icon_image);
        icon_group->_w = w;
        icon_group->_h = h;
        icon_group->_x = icon_image->_y = 0;
        icon_group->_xu = icon_group->_yu = icon_group->_wu =
            icon_group->_hu = PXLenghtUnitType;

        if (ww->wm_icon_image) ww->wm_icon_image->unref();
        ww->wm_icon_image = icon_group;
        
        ww->frame->setDynamicGroup(DynamicGroupWmIconImageType,
                                   ww->wm_icon_image);
        
        ww->drawDecor();
    }
}

void NetHandler::getWmIconSvg(WaWindow *ww) {
    CARD32 *data;
    int status = Success - 1;

#ifdef    SVG
    status = XGetWindowProperty(ww->display, ww->id, net_wm_icon_svg, 0L,
                                1048576L, false,  utf8_string, &real_type,
                                &real_format, &items_read, &items_left, 
                                (unsigned char **) &data);
    
    if (status == Success && items_read >= 1048576L) {
        XFree(data);
        return;
    }
    
    if (status == Success && items_read) {
        svg_cairo_t *cairo_svg;
        svg_cairo_status status;
        status = svg_cairo_create(&cairo_svg);
        if (status == SVG_CAIRO_STATUS_SUCCESS) {
            status = svg_cairo_parse_buffer(cairo_svg, (const char *) data,
                                            items_read);
            if (status == SVG_CAIRO_STATUS_SUCCESS) {
                RenderGroup *icon_group = new RenderGroup(ww->ws, NULL);
                RenderOpSvg *icon_svg = new RenderOpSvg();
                icon_svg->cairo_svg = cairo_svg;
                icon_group->operations.push_back(icon_svg);
                icon_group->_w = 16;
                icon_group->_h = 16;
                icon_group->_x = icon_group->_y = 0;
                icon_group->_xu = icon_group->_yu = icon_group->_wu =
                    icon_group->_hu = PXLenghtUnitType;

                if (ww->wm_icon_svg) ww->wm_icon_svg->unref();
                ww->wm_icon_svg = icon_group;
                 
                ww->frame->setDynamicGroup(DynamicGroupWmIconSvgType,
                                           ww->wm_icon_svg);
                 
                ww->drawDecor();
            }
        }
        XFree(data);
    }
#endif // SVG
    
}


char *NetHandler::getDockappHandler(Dockapp *dockapp) {
    char *handlername = NULL;
    CARD32 *data;
    int status = Success - 1;

    status = XGetWindowProperty(display, dockapp->id,
                                waimea_net_dockapp_holder, 0L, 8192L,
                                false, utf8_string, &real_type,
                                &real_format, &items_read, &items_left, 
                                (unsigned char **) &data);
    
    if (status == Success && items_read) {
        handlername = WA_STRDUP((char *) data);
        XFree(data);
    }

    if (! handlername)
        return WA_STRDUP("default");
    else
        return handlername;
}

void NetHandler::setDockappHandler(Dockapp *dockapp) {
    XChangeProperty(display, dockapp->id, waimea_net_dockapp_holder,
                    utf8_string, 8, PropModeReplace,
                    (unsigned char *) dockapp->dh->name,
                    strlen(dockapp->dh->name));
}

void NetHandler::getDockappPrio(Dockapp *dockapp) {
    CARD32 *data;
    int prio = 0;

    if (XGetWindowProperty(display, dockapp->id, waimea_net_dockapp_prio,
                           0L, 1L, false, XA_CARDINAL, &real_type,
                           &real_format, &items_read, &items_left, 
                           (unsigned char **) &data) == Success && 
        items_read) {
        prio = *data;
        XFree(data);
    }
    
    dockapp->prio = prio;
}

void NetHandler::setDockappPrio(Dockapp *dockapp) {
    if (dockapp->prio_set)
        XChangeProperty(display, dockapp->id, waimea_net_dockapp_prio,
                        XA_CARDINAL, 32, PropModeReplace,
                        (unsigned char *) &dockapp->prio, 1);
}

void NetHandler::getConfig(WaScreen *ws, Window window, Atom atom,
                           unsigned int incsize) {
    CARD32 *data;
    Parser * parser = NULL;
    int status;
    bool failed = false;
    XEvent event;

    XSelectInput(display, window, StructureNotifyMask |
                 PropertyChangeMask);

    RENDER_GET;
    
    do {
        status = Success - 1;
        status = XGetWindowProperty(display, window, atom,
                                    0L, (incsize)? incsize: 819200L, true,
                                    utf8_string, &real_type, &real_format,
                                    &items_read, &items_left,
                                    (unsigned char **) &data);
        if (status == Success && items_read) {
            if (! parser) {
                parser = new Parser(ws);
                parser->pushElementHandler(new CfgElementHandler(parser));
                parser->setFilename("_WAIMEA_NET_CFG");
            }

            if (! failed) {
                if (! parser->parseChunk((char *) data, items_read))
                    failed = true;
            }
            
            XFree(data);
        } else
            break;

        if (incsize && items_read && items_read == incsize) {
            bool new_data = false;

            do {
                XWindowEvent(display, window, StructureNotifyMask |
                             PropertyChangeMask, &event);
                
                if (event.type == PropertyNotify) {
                    if (event.xproperty.state != PropertyDelete) {
                        new_data = true;
                    }
                } else
                    break;
                
            } while (! new_data);

            if (! new_data) break;
        }
    } while (items_read == incsize);
    
    XSelectInput(display, window, NoEventMask);
    
    if (parser) {
        if (! failed)
            parser->parseChunkEnd();
        
        delete parser;
    }

    RENDER_RELEASE;
    
}


syntax highlighted by Code2HTML, v. 0.9.1