/*

  Copyright (c) 2000 Troll Tech AS
  Copyright (c) 2003 Lubos Lunak <l.lunak@kde.org>

  Permission is hereby granted, free of charge, to any person obtaining a
  copy of this software and associated documentation files (the "Software"),
  to deal in the Software without restriction, including without limitation
  the rights to use, copy, modify, merge, publish, distribute, sublicense,
  and/or sell copies of the Software, and to permit persons to whom the
  Software is furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  DEALINGS IN THE SOFTWARE.

*/

//#define NETWMDEBUG

#include <tqwidget.h>
#include <tqapplication.h>
#ifdef Q_WS_X11 //FIXME

#include "netwm.h"

#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>

#include <X11/Xmd.h>

#include "netwm_p.h"

// UTF-8 string
static Atom UTF8_STRING = 0;

// root window properties
static Atom net_supported            = 0;
static Atom net_client_list          = 0;
static Atom net_client_list_stacking = 0;
static Atom net_desktop_geometry     = 0;
static Atom net_desktop_viewport     = 0;
static Atom net_current_desktop      = 0;
static Atom net_desktop_names        = 0;
static Atom net_number_of_desktops   = 0;
static Atom net_active_window        = 0;
static Atom net_workarea             = 0;
static Atom net_supporting_wm_check  = 0;
static Atom net_virtual_roots        = 0;
static Atom net_showing_desktop      = 0;
static Atom net_desktop_layout       = 0;

// root window messages
static Atom net_close_window         = 0;
static Atom net_restack_window       = 0;
static Atom net_wm_moveresize        = 0;
static Atom net_moveresize_window    = 0;

// application window properties
static Atom net_wm_name              = 0;
static Atom net_wm_visible_name      = 0;
static Atom net_wm_icon_name         = 0;
static Atom net_wm_visible_icon_name = 0;
static Atom net_wm_desktop           = 0;
static Atom net_wm_window_type       = 0;
static Atom net_wm_state             = 0;
static Atom net_wm_strut             = 0;
static Atom net_wm_extended_strut    = 0; // the atom is called _NET_WM_STRUT_PARTIAL
static Atom net_wm_icon_geometry     = 0;
static Atom net_wm_icon              = 0;
static Atom net_wm_pid               = 0;
static Atom net_wm_user_time         = 0;
static Atom net_wm_handled_icons     = 0;
static Atom net_startup_id           = 0;
static Atom net_wm_allowed_actions   = 0;
static Atom wm_window_role           = 0;
static Atom net_frame_extents        = 0;

// KDE extensions
static Atom kde_net_system_tray_windows       = 0;
static Atom kde_net_wm_system_tray_window_for = 0;
static Atom kde_net_wm_frame_strut            = 0;
static Atom kde_net_wm_window_type_override   = 0;
static Atom kde_net_wm_window_type_topmenu    = 0;
static Atom kde_net_wm_temporary_rules        = 0;

// application protocols
static Atom wm_protocols = 0;
static Atom net_wm_ping = 0;
static Atom net_wm_take_activity = 0;

// application window types
static Atom net_wm_window_type_normal  = 0;
static Atom net_wm_window_type_desktop = 0;
static Atom net_wm_window_type_dock    = 0;
static Atom net_wm_window_type_toolbar = 0;
static Atom net_wm_window_type_menu    = 0;
static Atom net_wm_window_type_dialog  = 0;
static Atom net_wm_window_type_utility = 0;
static Atom net_wm_window_type_splash  = 0;
static Atom net_wm_window_type_dropdown_menu = 0;
static Atom net_wm_window_type_popup_menu    = 0;
static Atom net_wm_window_type_tooltip       = 0;
static Atom net_wm_window_type_notification  = 0;
static Atom net_wm_window_type_combobox      = 0;
static Atom net_wm_window_type_dnd           = 0;

// application window state
static Atom net_wm_state_modal        = 0;
static Atom net_wm_state_sticky       = 0;
static Atom net_wm_state_max_vert     = 0;
static Atom net_wm_state_max_horiz    = 0;
static Atom net_wm_state_shaded       = 0;
static Atom net_wm_state_skip_taskbar = 0;
static Atom net_wm_state_skip_pager   = 0;
static Atom net_wm_state_hidden       = 0;
static Atom net_wm_state_fullscreen   = 0;
static Atom net_wm_state_above        = 0;
static Atom net_wm_state_below        = 0;
static Atom net_wm_state_demands_attention = 0;

// allowed actions
static Atom net_wm_action_move        = 0;
static Atom net_wm_action_resize      = 0;
static Atom net_wm_action_minimize    = 0;
static Atom net_wm_action_shade       = 0;
static Atom net_wm_action_stick       = 0;
static Atom net_wm_action_max_vert    = 0;
static Atom net_wm_action_max_horiz   = 0;
static Atom net_wm_action_fullscreen  = 0;
static Atom net_wm_action_change_desk = 0;
static Atom net_wm_action_close       = 0;

// KDE extension that's not in the specs - Replaced by state_above now?
static Atom net_wm_state_stays_on_top = 0;

// used to determine whether application window is managed or not
static Atom xa_wm_state = 0;

// ability flags
static Atom net_wm_full_placement = 0;

static Bool netwm_atoms_created      = False;
const unsigned long netwm_sendevent_mask = (SubstructureRedirectMask|
					     SubstructureNotifyMask);


const long MAX_PROP_SIZE = 100000;

static char *nstrdup(const char *s1) {
    if (! s1) return (char *) 0;

    int l = strlen(s1) + 1;
    char *s2 = new char[l];
    strncpy(s2, s1, l);
    return s2;
}


static char *nstrndup(const char *s1, int l) {
    if (! s1 || l == 0) return (char *) 0;

    char *s2 = new char[l+1];
    strncpy(s2, s1, l);
    s2[l] = '\0';
    return s2;
}


static Window *nwindup(Window *w1, int n) {
    if (! w1 || n == 0) return (Window *) 0;

    Window *w2 = new Window[n];
    while (n--)	w2[n] = w1[n];
    return w2;
}


static void refdec_nri(NETRootInfoPrivate *p) {

#ifdef    NETWMDEBUG
    fprintf(stderr, "NET: decrementing NETRootInfoPrivate::ref (%d)\n", p->ref - 1);
#endif

    if (! --p->ref) {

#ifdef    NETWMDEBUG
	fprintf(stderr, "NET: \tno more references, deleting\n");
#endif

	delete [] p->name;
	delete [] p->stacking;
	delete [] p->clients;
	delete [] p->virtual_roots;
	delete [] p->kde_system_tray_windows;

	int i;
	for (i = 0; i < p->desktop_names.size(); i++)
	    delete [] p->desktop_names[i];
    }
}


static void refdec_nwi(NETWinInfoPrivate *p) {

#ifdef    NETWMDEBUG
    fprintf(stderr, "NET: decrementing NETWinInfoPrivate::ref (%d)\n", p->ref - 1);
#endif

    if (! --p->ref) {

#ifdef    NETWMDEBUG
	fprintf(stderr, "NET: \tno more references, deleting\n");
#endif

	delete [] p->name;
	delete [] p->visible_name;
	delete [] p->icon_name;
	delete [] p->visible_icon_name;
	delete [] p->startup_id;

	int i;
	for (i = 0; i < p->icons.size(); i++)
	    delete [] p->icons[i].data;
    }
}


static int wcmp(const void *a, const void *b) {
    return *((Window *) a) - *((Window *) b);
}


static const int netAtomCount = 85;
static void create_atoms(Display *d) {
    static const char * const names[netAtomCount] =
    {
	"UTF8_STRING",
	    "_NET_SUPPORTED",
	    "_NET_SUPPORTING_WM_CHECK",
	    "_NET_CLIENT_LIST",
	    "_NET_CLIENT_LIST_STACKING",
	    "_NET_NUMBER_OF_DESKTOPS",
	    "_NET_DESKTOP_GEOMETRY",
	    "_NET_DESKTOP_VIEWPORT",
	    "_NET_CURRENT_DESKTOP",
	    "_NET_DESKTOP_NAMES",
	    "_NET_ACTIVE_WINDOW",
	    "_NET_WORKAREA",
	    "_NET_VIRTUAL_ROOTS",
            "_NET_DESKTOP_LAYOUT",
            "_NET_SHOWING_DESKTOP",
	    "_NET_CLOSE_WINDOW",
            "_NET_RESTACK_WINDOW",

	    "_NET_WM_MOVERESIZE",
            "_NET_MOVERESIZE_WINDOW",
	    "_NET_WM_NAME",
	    "_NET_WM_VISIBLE_NAME",
	    "_NET_WM_ICON_NAME",
	    "_NET_WM_VISIBLE_ICON_NAME",
	    "_NET_WM_DESKTOP",
	    "_NET_WM_WINDOW_TYPE",
	    "_NET_WM_STATE",
	    "_NET_WM_STRUT",
            "_NET_WM_STRUT_PARTIAL",
	    "_NET_WM_ICON_GEOMETRY",
	    "_NET_WM_ICON",
	    "_NET_WM_PID",
	    "_NET_WM_USER_TIME",
	    "_NET_WM_HANDLED_ICONS",
            "_NET_STARTUP_ID",
            "_NET_WM_ALLOWED_ACTIONS",
	    "_NET_WM_PING",
            "_NET_WM_TAKE_ACTIVITY",
            "WM_WINDOW_ROLE",
            "_NET_FRAME_EXTENTS",

	    "_NET_WM_WINDOW_TYPE_NORMAL",
	    "_NET_WM_WINDOW_TYPE_DESKTOP",
	    "_NET_WM_WINDOW_TYPE_DOCK",
	    "_NET_WM_WINDOW_TYPE_TOOLBAR",
	    "_NET_WM_WINDOW_TYPE_MENU",
	    "_NET_WM_WINDOW_TYPE_DIALOG",
	    "_NET_WM_WINDOW_TYPE_UTILITY",
	    "_NET_WM_WINDOW_TYPE_SPLASH",
	    "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU",
	    "_NET_WM_WINDOW_TYPE_POPUP_MENU",
	    "_NET_WM_WINDOW_TYPE_TOOLTIP",
	    "_NET_WM_WINDOW_TYPE_NOTIFICATION",
	    "_NET_WM_WINDOW_TYPE_COMBOBOX",
	    "_NET_WM_WINDOW_TYPE_DND",

	    "_NET_WM_STATE_MODAL",
	    "_NET_WM_STATE_STICKY",
	    "_NET_WM_STATE_MAXIMIZED_VERT",
	    "_NET_WM_STATE_MAXIMIZED_HORZ",
	    "_NET_WM_STATE_SHADED",
	    "_NET_WM_STATE_SKIP_TASKBAR",
	    "_NET_WM_STATE_SKIP_PAGER",
	    "_NET_WM_STATE_HIDDEN",
	    "_NET_WM_STATE_FULLSCREEN",
	    "_NET_WM_STATE_ABOVE",
	    "_NET_WM_STATE_BELOW",
	    "_NET_WM_STATE_DEMANDS_ATTENTION",

            "_NET_WM_ACTION_MOVE",
            "_NET_WM_ACTION_RESIZE",
            "_NET_WM_ACTION_MINIMIZE",
            "_NET_WM_ACTION_SHADE",
            "_NET_WM_ACTION_STICK",
            "_NET_WM_ACTION_MAXIMIZE_VERT",
            "_NET_WM_ACTION_MAXIMIZE_HORZ",
            "_NET_WM_ACTION_FULLSCREEN",
            "_NET_WM_ACTION_CHANGE_DESKTOP",
            "_NET_WM_ACTION_CLOSE",

	    "_NET_WM_STATE_STAYS_ON_TOP",

	    "_KDE_NET_SYSTEM_TRAY_WINDOWS",
	    "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR",
	    "_KDE_NET_WM_FRAME_STRUT",
	    "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE",
	    "_KDE_NET_WM_WINDOW_TYPE_TOPMENU",
            "_KDE_NET_WM_TEMPORARY_RULES",

	    "WM_STATE",
	    "WM_PROTOCOLS",
            
            "_NET_WM_FULL_PLACEMENT"
	    };

    Atom atoms[netAtomCount], *atomsp[netAtomCount] =
    {
	&UTF8_STRING,
	    &net_supported,
	    &net_supporting_wm_check,
	    &net_client_list,
	    &net_client_list_stacking,
	    &net_number_of_desktops,
	    &net_desktop_geometry,
	    &net_desktop_viewport,
	    &net_current_desktop,
	    &net_desktop_names,
	    &net_active_window,
	    &net_workarea,
	    &net_virtual_roots,
            &net_desktop_layout,
            &net_showing_desktop,
	    &net_close_window,
            &net_restack_window,

	    &net_wm_moveresize,
            &net_moveresize_window,
	    &net_wm_name,
	    &net_wm_visible_name,
	    &net_wm_icon_name,
	    &net_wm_visible_icon_name,
	    &net_wm_desktop,
	    &net_wm_window_type,
	    &net_wm_state,
	    &net_wm_strut,
            &net_wm_extended_strut,
	    &net_wm_icon_geometry,
	    &net_wm_icon,
	    &net_wm_pid,
	    &net_wm_user_time,
	    &net_wm_handled_icons,
            &net_startup_id,
            &net_wm_allowed_actions,
	    &net_wm_ping,
            &net_wm_take_activity,
            &wm_window_role,
            &net_frame_extents,

	    &net_wm_window_type_normal,
	    &net_wm_window_type_desktop,
	    &net_wm_window_type_dock,
	    &net_wm_window_type_toolbar,
	    &net_wm_window_type_menu,
	    &net_wm_window_type_dialog,
	    &net_wm_window_type_utility,
	    &net_wm_window_type_splash,
	    &net_wm_window_type_dropdown_menu,
	    &net_wm_window_type_popup_menu,
	    &net_wm_window_type_tooltip,
	    &net_wm_window_type_notification,
	    &net_wm_window_type_combobox,
	    &net_wm_window_type_dnd,

	    &net_wm_state_modal,
	    &net_wm_state_sticky,
	    &net_wm_state_max_vert,
	    &net_wm_state_max_horiz,
	    &net_wm_state_shaded,
	    &net_wm_state_skip_taskbar,
	    &net_wm_state_skip_pager,
	    &net_wm_state_hidden,
	    &net_wm_state_fullscreen,
	    &net_wm_state_above,
	    &net_wm_state_below,
	    &net_wm_state_demands_attention,

            &net_wm_action_move,
            &net_wm_action_resize,
            &net_wm_action_minimize,
            &net_wm_action_shade,
            &net_wm_action_stick,
            &net_wm_action_max_vert,
            &net_wm_action_max_horiz,
            &net_wm_action_fullscreen,
            &net_wm_action_change_desk,
            &net_wm_action_close,

	    &net_wm_state_stays_on_top,

	    &kde_net_system_tray_windows,
	    &kde_net_wm_system_tray_window_for,
	    &kde_net_wm_frame_strut,
	    &kde_net_wm_window_type_override,
	    &kde_net_wm_window_type_topmenu,
            &kde_net_wm_temporary_rules,

	    &xa_wm_state,
	    &wm_protocols,
            
            &net_wm_full_placement
	    };

    assert( !netwm_atoms_created );

    int i = netAtomCount;
    while (i--)
	atoms[i] = 0;

    XInternAtoms(d, (char **) names, netAtomCount, False, atoms);

    i = netAtomCount;
    while (i--)
	*atomsp[i] = atoms[i];

    netwm_atoms_created = True;
}


static void readIcon(Display* display, Window window, Atom property, NETRArray<NETIcon>& icons, int& icon_count) {

#ifdef    NETWMDEBUG
    fprintf(stderr, "NET: readIcon\n");
#endif

    Atom type_ret;
    int format_ret;
    unsigned long nitems_ret = 0, after_ret = 0;
    unsigned char *data_ret = 0;

    // reset
    for (int i = 0; i < icons.size(); i++)
        delete [] icons[i].data;
    icons.reset();
    icon_count = 0;

    // allocate buffers
    unsigned char *buffer = 0;
    unsigned long offset = 0;
    unsigned long buffer_offset = 0;
    unsigned long bufsize = 0;

    // read data
    do {
	if (XGetWindowProperty(display, window, property, offset,
			       MAX_PROP_SIZE, False, XA_CARDINAL, &type_ret,
			       &format_ret, &nitems_ret, &after_ret, &data_ret)
	    == Success) {
            if (!bufsize)
            {
               if (nitems_ret < 3 || type_ret != XA_CARDINAL ||
                  format_ret != 32) {
                  // either we didn't get the property, or the property has less than
                  // 3 elements in it
                  // NOTE: 3 is the ABSOLUTE minimum:
                  //     width = 1, height = 1, length(data) = 1 (width * height)
                  if ( data_ret )
                     XFree(data_ret);
                  return;
               }

               bufsize = nitems_ret * sizeof(long) + after_ret;
               buffer = (unsigned char *) malloc(bufsize);
            }
            else if (buffer_offset + nitems_ret*sizeof(long) > bufsize)
            {
fprintf(stderr, "NETWM: Warning readIcon() needs buffer adjustment!\n");
               bufsize = buffer_offset + nitems_ret * sizeof(long) + after_ret;
               buffer = (unsigned char *) realloc(buffer, bufsize);
            }
	    memcpy((buffer + buffer_offset), data_ret, nitems_ret * sizeof(long));
	    buffer_offset += nitems_ret * sizeof(long);
	    offset += nitems_ret;

	    if ( data_ret )
		XFree(data_ret);
	} else {
            if (buffer)
               free(buffer);
	    return; // Some error occurred cq. property didn't exist.
	}
    }
    while (after_ret > 0);

    CARD32 *data32;
    unsigned long i, j, k, sz, s;
    unsigned long *d = (unsigned long *) buffer;
    for (i = 0, j = 0; i < bufsize;) {
	icons[j].size.width = *d++;
	i += sizeof(long);
	icons[j].size.height = *d++;
	i += sizeof(long);

	sz = icons[j].size.width * icons[j].size.height;
	s = sz * sizeof(long);

	if ( i + s - 1 > bufsize || sz == 0 || sz > 1024 * 1024 ) {
	    break;
	}

	delete [] icons[j].data;
	data32 = new CARD32[sz];
	icons[j].data = (unsigned char *) data32;
	for (k = 0; k < sz; k++, i += sizeof(long)) {
	    *data32++ = (CARD32) *d++;
	}
	j++;
        icon_count++;
    }

#ifdef    NETWMDEBUG
    fprintf(stderr, "NET: readIcon got %d icons\n", icon_count);
#endif

    free(buffer);
}


template <class Z>
NETRArray<Z>::NETRArray()
  : sz(0),  capacity(2)
{
    d = (Z*) calloc(capacity, sizeof(Z)); // allocate 2 elts and set to zero
}


template <class Z>
NETRArray<Z>::~NETRArray() {
    free(d);
}


template <class Z>
void NETRArray<Z>::reset() {
    sz = 0;
    capacity = 2;
    d = (Z*) realloc(d, sizeof(Z)*capacity);
    memset( (void*) d, 0, sizeof(Z)*capacity );
}

template <class Z>
Z &NETRArray<Z>::operator[](int index) {
    if (index >= capacity) {
	// allocate space for the new data
	// open table has amortized O(1) access time
	// when N elements appended consecutively -- exa
        int newcapacity = 2*capacity > index+1 ? 2*capacity : index+1; // max
	// copy into new larger memory block using realloc
        d = (Z*) realloc(d, sizeof(Z)*newcapacity);
        memset( (void*) &d[capacity], 0, sizeof(Z)*(newcapacity-capacity) );
	capacity = newcapacity;
    }
    if (index >= sz)            // at this point capacity>index
        sz = index + 1;

    return d[index];
}


// Construct a new NETRootInfo object.

NETRootInfo::NETRootInfo(Display *display, Window supportWindow, const char *wmName,
			 const unsigned long properties[], int properties_size,
                         int screen, bool doActivate)
{

#ifdef    NETWMDEBUG
    fprintf(stderr, "NETRootInfo::NETRootInfo: using window manager constructor\n");
#endif

    p = new NETRootInfoPrivate;
    p->ref = 1;

    p->display = display;
    p->name = nstrdup(wmName);

    if (screen != -1) {
	p->screen = screen;
    } else {
	p->screen = DefaultScreen(p->display);
    }

    p->root = RootWindow(p->display, p->screen);
    p->supportwindow = supportWindow;
    p->number_of_desktops = p->current_desktop = 0;
    p->active = None;
    p->clients = p->stacking = p->virtual_roots = (Window *) 0;
    p->clients_count = p->stacking_count = p->virtual_roots_count = 0;
    p->kde_system_tray_windows = 0;
    p->kde_system_tray_windows_count = 0;
    p->showing_desktop = false;
    p->desktop_layout_orientation = OrientationHorizontal;
    p->desktop_layout_corner = DesktopLayoutCornerTopLeft;
    p->desktop_layout_columns = p->desktop_layout_rows = 0;
    setDefaultProperties();
    if( properties_size > PROPERTIES_SIZE ) {
        fprintf( stderr, "NETRootInfo::NETRootInfo(): properties array too large\n");
        properties_size = PROPERTIES_SIZE;
    }
    for( int i = 0; i < properties_size; ++i )
        p->properties[ i ] = properties[ i ];
    // force support for Supported and SupportingWMCheck for window managers
    p->properties[ PROTOCOLS ] |= ( Supported | SupportingWMCheck );
    p->client_properties[ PROTOCOLS ] = DesktopNames // the only thing that can be changed by clients
			                | WMPing; // or they can reply to this
    p->client_properties[ PROTOCOLS2 ] = WM2TakeActivity | WM2DesktopLayout;

    role = WindowManager;

    if (! netwm_atoms_created) create_atoms(p->display);

    if (doActivate) activate();
}

NETRootInfo::NETRootInfo(Display *display, Window supportWindow, const char *wmName,
			 unsigned long properties, int screen, bool doActivate)
{

#ifdef    NETWMDEBUG
    fprintf(stderr, "NETRootInfo::NETRootInfo: using window manager constructor\n");
#endif

    p = new NETRootInfoPrivate;
    p->ref = 1;

    p->display = display;
    p->name = nstrdup(wmName);

    if (screen != -1) {
	p->screen = screen;
    } else {
	p->screen = DefaultScreen(p->display);
    }

    p->root = RootWindow(p->display, p->screen);
    p->supportwindow = supportWindow;
    p->number_of_desktops = p->current_desktop = 0;
    p->active = None;
    p->clients = p->stacking = p->virtual_roots = (Window *) 0;
    p->clients_count = p->stacking_count = p->virtual_roots_count = 0;
    p->kde_system_tray_windows = 0;
    p->kde_system_tray_windows_count = 0;
    p->showing_desktop = false;
    setDefaultProperties();
    p->properties[ PROTOCOLS ] = properties;
    // force support for Supported and SupportingWMCheck for window managers
    p->properties[ PROTOCOLS ] |= ( Supported | SupportingWMCheck );
    p->client_properties[ PROTOCOLS ] = DesktopNames // the only thing that can be changed by clients
			                | WMPing; // or they can reply to this
    p->client_properties[ PROTOCOLS2 ] = WM2TakeActivity;

    role = WindowManager;

    if (! netwm_atoms_created) create_atoms(p->display);

    if (doActivate) activate();
}


NETRootInfo::NETRootInfo(Display *display, const unsigned long properties[], int properties_size,
                         int screen, bool doActivate)
{

#ifdef    NETWMDEBUG
    fprintf(stderr, "NETRootInfo::NETRootInfo: using Client constructor\n");
#endif

    p = new NETRootInfoPrivate;
    p->ref = 1;

    p->name = 0;

    p->display = display;

    if (screen != -1) {
	p->screen = screen;
    } else {
	p->screen = DefaultScreen(p->display);
    }

    p->root = RootWindow(p->display, p->screen);
    p->rootSize.width = WidthOfScreen(ScreenOfDisplay(p->display, p->screen));
    p->rootSize.height = HeightOfScreen(ScreenOfDisplay(p->display, p->screen));

    p->supportwindow = None;
    p->number_of_desktops = p->current_desktop = 0;
    p->active = None;
    p->clients = p->stacking = p->virtual_roots = (Window *) 0;
    p->clients_count = p->stacking_count = p->virtual_roots_count = 0;
    p->kde_system_tray_windows = 0;
    p->kde_system_tray_windows_count = 0;
    p->showing_desktop = false;
    p->desktop_layout_orientation = OrientationHorizontal;
    p->desktop_layout_corner = DesktopLayoutCornerTopLeft;
    p->desktop_layout_columns = p->desktop_layout_rows = 0;
    setDefaultProperties();
    if( properties_size > 2 ) {
        fprintf( stderr, "NETWinInfo::NETWinInfo(): properties array too large\n");
        properties_size = 2;
    }
    for( int i = 0; i < properties_size; ++i )
        // remap from [0]=NET::Property,[1]=NET::Property2
        switch( i ) {
            case 0:
                p->client_properties[ PROTOCOLS ] = properties[ i ];
                break;
            case 1:
                p->client_properties[ PROTOCOLS2 ] = properties[ i ];
                break;
        }
    for( int i = 0; i < PROPERTIES_SIZE; ++i )
        p->properties[ i ] = 0;

    role = Client;

    if (! netwm_atoms_created) create_atoms(p->display);

    if (doActivate) activate();
}

NETRootInfo::NETRootInfo(Display *display, unsigned long properties, int screen,
			 bool doActivate)
{

#ifdef    NETWMDEBUG
    fprintf(stderr, "NETRootInfo::NETRootInfo: using Client constructor\n");
#endif

    p = new NETRootInfoPrivate;
    p->ref = 1;

    p->name = 0;

    p->display = display;

    if (screen != -1) {
	p->screen = screen;
    } else {
	p->screen = DefaultScreen(p->display);
    }

    p->root = RootWindow(p->display, p->screen);
    p->rootSize.width = WidthOfScreen(ScreenOfDisplay(p->display, p->screen));
    p->rootSize.height = HeightOfScreen(ScreenOfDisplay(p->display, p->screen));

    p->supportwindow = None;
    p->number_of_desktops = p->current_desktop = 0;
    p->active = None;
    p->clients = p->stacking = p->virtual_roots = (Window *) 0;
    p->clients_count = p->stacking_count = p->virtual_roots_count = 0;
    p->kde_system_tray_windows = 0;
    p->kde_system_tray_windows_count = 0;
    p->showing_desktop = false;
    p->desktop_layout_orientation = OrientationHorizontal;
    p->desktop_layout_corner = DesktopLayoutCornerTopLeft;
    p->desktop_layout_columns = p->desktop_layout_rows = 0;
    setDefaultProperties();
    p->client_properties[ PROTOCOLS ] = properties;
    for( int i = 0; i < PROPERTIES_SIZE; ++i )
        p->properties[ i ] = 0;

    role = Client;

    if (! netwm_atoms_created) create_atoms(p->display);

    if (doActivate) activate();
}


NETRootInfo2::NETRootInfo2(Display *display, Window supportWindow, const char *wmName,
			 unsigned long properties[], int properties_size,
                         int screen, bool doActivate)
    : NETRootInfo( display, supportWindow, wmName, properties, properties_size,
	screen, doActivate )
{
}

NETRootInfo2::NETRootInfo2(Display *display, const unsigned long properties[], int properties_size,
                int screen, bool doActivate)
    : NETRootInfo( display, properties, properties_size, screen, doActivate )
{
}

NETRootInfo3::NETRootInfo3(Display *display, Window supportWindow, const char *wmName,
			 unsigned long properties[], int properties_size,
                         int screen, bool doActivate)
    : NETRootInfo2( display, supportWindow, wmName, properties, properties_size,
	screen, doActivate )
{
}

NETRootInfo3::NETRootInfo3(Display *display, const unsigned long properties[], int properties_size,
                int screen, bool doActivate)
    : NETRootInfo2( display, properties, properties_size, screen, doActivate )
{
}

NETRootInfo4::NETRootInfo4(Display *display, Window supportWindow, const char *wmName,
			 unsigned long properties[], int properties_size,
                         int screen, bool doActivate)
    : NETRootInfo3( display, supportWindow, wmName, properties, properties_size,
	screen, doActivate )
{
}

NETRootInfo4::NETRootInfo4(Display *display, const unsigned long properties[], int properties_size,
                int screen, bool doActivate)
    : NETRootInfo3( display, properties, properties_size, screen, doActivate )
{
}

// Copy an existing NETRootInfo object.

NETRootInfo::NETRootInfo(const NETRootInfo &rootinfo) {

#ifdef    NETWMDEBUG
    fprintf(stderr, "NETRootInfo::NETRootInfo: using copy constructor\n");
#endif

    p = rootinfo.p;
    role = rootinfo.role;

    p->ref++;
}


// Be gone with our NETRootInfo.

NETRootInfo::~NETRootInfo() {
    refdec_nri(p);

    if (! p->ref) delete p;
}


void NETRootInfo::setDefaultProperties()
{
    p->properties[ PROTOCOLS ] = Supported | SupportingWMCheck;
    p->properties[ WINDOW_TYPES ] = NormalMask | DesktopMask | DockMask
        | ToolbarMask | MenuMask | DialogMask;
    p->properties[ STATES ] = Modal | Sticky | MaxVert | MaxHoriz | Shaded
        | SkipTaskbar | StaysOnTop;
    p->properties[ PROTOCOLS2 ] = 0;
    p->properties[ ACTIONS ] = 0;
    p->client_properties[ PROTOCOLS ] = 0;
    p->client_properties[ WINDOW_TYPES ] = 0; // these two actually don't
    p->client_properties[ STATES ] = 0;       // make sense in client_properties
    p->client_properties[ PROTOCOLS2 ] = 0;
    p->client_properties[ ACTIONS ] = 0;
}

void NETRootInfo::activate() {
    if (role == WindowManager) {

#ifdef    NETWMDEBUG
	fprintf(stderr,
		"NETRootInfo::activate: setting supported properties on root\n");
#endif

	setSupported();
	update(p->client_properties);
    } else {

#ifdef    NETWMDEBUG
	fprintf(stderr, "NETRootInfo::activate: updating client information\n");
#endif

	update(p->client_properties);
    }
}


void NETRootInfo::setClientList(Window *windows, unsigned int count) {
    if (role != WindowManager) return;

    p->clients_count = count;

    delete [] p->clients;
    p->clients = nwindup(windows, count);

#ifdef    NETWMDEBUG
    fprintf(stderr, "NETRootInfo::setClientList: setting list with %ld windows\n",
	    p->clients_count);
#endif

    XChangeProperty(p->display, p->root, net_client_list, XA_WINDOW, 32,
		    PropModeReplace, (unsigned char *)p->clients,
		    p->clients_count);
}


void NETRootInfo::setClientListStacking(Window *windows, unsigned int count) {
    if (role != WindowManager) return;

    p->stacking_count = count;
    delete [] p->stacking;
    p->stacking = nwindup(windows, count);

#ifdef    NETWMDEBUG
    fprintf(stderr,
	    "NETRootInfo::setClientListStacking: setting list with %ld windows\n",
	    p->clients_count);
#endif

    XChangeProperty(p->display, p->root, net_client_list_stacking, XA_WINDOW, 32,
		    PropModeReplace, (unsigned char *) p->stacking,
		    p->stacking_count);
}


void NETRootInfo::setKDESystemTrayWindows(Window *windows, unsigned int count) {
    if (role != WindowManager) return;

    p->kde_system_tray_windows_count = count;
    delete [] p->kde_system_tray_windows;
    p->kde_system_tray_windows = nwindup(windows, count);

#ifdef    NETWMDEBUG
    fprintf(stderr,
	    "NETRootInfo::setKDESystemTrayWindows: setting list with %ld windows\n",
	    p->kde_system_tray_windows_count);
#endif

    XChangeProperty(p->display, p->root, kde_net_system_tray_windows, XA_WINDOW, 32,
		    PropModeReplace,
		    (unsigned char *) p->kde_system_tray_windows,
		    p->kde_system_tray_windows_count);
}


void NETRootInfo::setNumberOfDesktops(int numberOfDesktops) {

#ifdef    NETWMDEBUG
    fprintf(stderr,
	    "NETRootInfo::setNumberOfDesktops: setting desktop count to %d (%s)\n",
	    numberOfDesktops, (role == WindowManager) ? "WM" : "Client");
#endif

    if (role == WindowManager) {
	p->number_of_desktops = numberOfDesktops;
 	long d = numberOfDesktops;
	XChangeProperty(p->display, p->root, net_number_of_desktops, XA_CARDINAL, 32,
			PropModeReplace, (unsigned char *) &d, 1);
    } else {
	XEvent e;

	e.xclient.type = ClientMessage;
	e.xclient.message_type = net_number_of_desktops;
	e.xclient.display = p->display;
	e.xclient.window = p->root;
	e.xclient.format = 32;
	e.xclient.data.l[0] = numberOfDesktops;
	e.xclient.data.l[1] = 0l;
	e.xclient.data.l[2] = 0l;
	e.xclient.data.l[3] = 0l;
	e.xclient.data.l[4] = 0l;

	XSendEvent(p->display, p->root, False, netwm_sendevent_mask, &e);
    }
}


void NETRootInfo::setCurrentDesktop(int desktop) {

#ifdef    NETWMDEBUG
    fprintf(stderr,
	    "NETRootInfo::setCurrentDesktop: setting current desktop = %d (%s)\n",
	    desktop, (role == WindowManager) ? "WM" : "Client");
#endif

    if (role == WindowManager) {
	p->current_desktop = desktop;
	long d = p->current_desktop - 1;
	XChangeProperty(p->display, p->root, net_current_desktop, XA_CARDINAL, 32,
			PropModeReplace, (unsigned char *) &d, 1);
    } else {
	XEvent e;

	e.xclient.type = ClientMessage;
	e.xclient.message_type = net_current_desktop;
	e.xclient.display = p->display;
	e.xclient.window = p->root;
	e.xclient.format = 32;
	e.xclient.data.l[0] = desktop - 1;
	e.xclient.data.l[1] = 0l;
	e.xclient.data.l[2] = 0l;
	e.xclient.data.l[3] = 0l;
	e.xclient.data.l[4] = 0l;

	XSendEvent(p->display, p->root, False, netwm_sendevent_mask, &e);
    }
}


void NETRootInfo::setDesktopName(int desktop, const char *desktopName) {
    // allow setting desktop names even for non-existant desktops, see the spec, sect.3.7.
    if (desktop < 1) return;

    delete [] p->desktop_names[desktop - 1];
    p->desktop_names[desktop - 1] = nstrdup(desktopName);

    unsigned int i, proplen,
	num = ((p->number_of_desktops > p->desktop_names.size()) ?
	       p->number_of_desktops : p->desktop_names.size());
    for (i = 0, proplen = 0; i < num; i++)
	proplen += (p->desktop_names[i] != 0 ? strlen(p->desktop_names[i])+1 : 1 );

    char *prop = new char[proplen], *propp = prop;

    for (i = 0; i < num; i++)
	if (p->desktop_names[i]) {
	    strcpy(propp, p->desktop_names[i]);
	    propp += strlen(p->desktop_names[i]) + 1;
	} else
	    *propp++ = '\0';

#ifdef    NETWMDEBUG
    fprintf(stderr,
	    "NETRootInfo::setDesktopName(%d, '%s')\n"
	    "NETRootInfo::setDesktopName: total property length = %d",
	    desktop, desktopName, proplen);
#endif

    XChangeProperty(p->display, p->root, net_desktop_names, UTF8_STRING, 8,
		    PropModeReplace, (unsigned char *) prop, proplen);

    delete [] prop;
}


void NETRootInfo::setDesktopGeometry(int , const NETSize &geometry) {

#ifdef    NETWMDEBUG
    fprintf(stderr, "NETRootInfo::setDesktopGeometry( -- , { %d, %d }) (%s)\n",
	    geometry.width, geometry.height, (role == WindowManager) ? "WM" : "Client");
#endif

    if (role == WindowManager) {
	p->geometry = geometry;

	long data[2];
	data[0] = p->geometry.width;
	data[1] = p->geometry.height;

	XChangeProperty(p->display, p->root, net_desktop_geometry, XA_CARDINAL, 32,
			PropModeReplace, (unsigned char *) data, 2);
    } else {
	XEvent e;

	e.xclient.type = ClientMessage;
	e.xclient.message_type = net_desktop_geometry;
	e.xclient.display = p->display;
	e.xclient.window = p->root;
	e.xclient.format = 32;
	e.xclient.data.l[0] = geometry.width;
	e.xclient.data.l[1] = geometry.height;
	e.xclient.data.l[2] = 0l;
	e.xclient.data.l[3] = 0l;
	e.xclient.data.l[4] = 0l;

	XSendEvent(p->display, p->root, False, netwm_sendevent_mask, &e);
    }
}


void NETRootInfo::setDesktopViewport(int desktop, const NETPoint &viewport) {

#ifdef    NETWMDEBUG
    fprintf(stderr, "NETRootInfo::setDesktopViewport(%d, { %d, %d }) (%s)\n",
	    desktop, viewport.x, viewport.y, (role == WindowManager) ? "WM" : "Client");
#endif

    if (desktop < 1) return;

    if (role == WindowManager) {
	p->viewport[desktop - 1] = viewport;

	int d, i, l;
	l = p->number_of_desktops * 2;
	long *data = new long[l];
	for (d = 0, i = 0; d < p->number_of_desktops; d++) {
	    data[i++] = p->viewport[d].x;
	    data[i++] = p->viewport[d].y;
	}

	XChangeProperty(p->display, p->root, net_desktop_viewport, XA_CARDINAL, 32,
			PropModeReplace, (unsigned char *) data, l);

	delete [] data;
    } else {
	XEvent e;

	e.xclient.type = ClientMessage;
	e.xclient.message_type = net_desktop_viewport;
	e.xclient.display = p->display;
	e.xclient.window = p->root;
	e.xclient.format = 32;
	e.xclient.data.l[0] = viewport.x;
	e.xclient.data.l[1] = viewport.y;
	e.xclient.data.l[2] = 0l;
	e.xclient.data.l[3] = 0l;
	e.xclient.data.l[4] = 0l;

	XSendEvent(p->display, p->root, False, netwm_sendevent_mask, &e);
    }
}


void NETRootInfo::setSupported() {
    if (role != WindowManager) {
#ifdef    NETWMDEBUG
	fprintf(stderr, "NETRootInfo::setSupported - role != WindowManager\n");
#endif

	return;
    }

    Atom atoms[netAtomCount];
    int pnum = 2;

    // Root window properties/messages
    atoms[0] = net_supported;
    atoms[1] = net_supporting_wm_check;

    if (p->properties[ PROTOCOLS ] & ClientList)
	atoms[pnum++] = net_client_list;

    if (p->properties[ PROTOCOLS ] & ClientListStacking)
	atoms[pnum++] = net_client_list_stacking;

    if (p->properties[ PROTOCOLS ] & NumberOfDesktops)
	atoms[pnum++] = net_number_of_desktops;

    if (p->properties[ PROTOCOLS ] & DesktopGeometry)
	atoms[pnum++] = net_desktop_geometry;

    if (p->properties[ PROTOCOLS ] & DesktopViewport)
	atoms[pnum++] = net_desktop_viewport;

    if (p->properties[ PROTOCOLS ] & CurrentDesktop)
	atoms[pnum++] = net_current_desktop;

    if (p->properties[ PROTOCOLS ] & DesktopNames)
	atoms[pnum++] = net_desktop_names;

    if (p->properties[ PROTOCOLS ] & ActiveWindow)
	atoms[pnum++] = net_active_window;

    if (p->properties[ PROTOCOLS ] & WorkArea)
	atoms[pnum++] = net_workarea;

    if (p->properties[ PROTOCOLS ] & VirtualRoots)
	atoms[pnum++] = net_virtual_roots;

    if (p->properties[ PROTOCOLS2 ] & WM2DesktopLayout)
	atoms[pnum++] = net_desktop_layout;

    if (p->properties[ PROTOCOLS ] & CloseWindow)
	atoms[pnum++] = net_close_window;

    if (p->properties[ PROTOCOLS2 ] & WM2RestackWindow)
	atoms[pnum++] = net_restack_window;

    if (p->properties[ PROTOCOLS2 ] & WM2ShowingDesktop)
	atoms[pnum++] = net_showing_desktop;

    // Application window properties/messages
    if (p->properties[ PROTOCOLS ] & WMMoveResize)
	atoms[pnum++] = net_wm_moveresize;

    if (p->properties[ PROTOCOLS2 ] & WM2MoveResizeWindow)
	atoms[pnum++] = net_moveresize_window;

    if (p->properties[ PROTOCOLS ] & WMName)
	atoms[pnum++] = net_wm_name;

    if (p->properties[ PROTOCOLS ] & WMVisibleName)
	atoms[pnum++] = net_wm_visible_name;

    if (p->properties[ PROTOCOLS ] & WMIconName)
	atoms[pnum++] = net_wm_icon_name;

    if (p->properties[ PROTOCOLS ] & WMVisibleIconName)
	atoms[pnum++] = net_wm_visible_icon_name;

    if (p->properties[ PROTOCOLS ] & WMDesktop)
	atoms[pnum++] = net_wm_desktop;

    if (p->properties[ PROTOCOLS ] & WMWindowType) {
	atoms[pnum++] = net_wm_window_type;

	// Application window types
        if (p->properties[ WINDOW_TYPES ] & NormalMask)
	    atoms[pnum++] = net_wm_window_type_normal;
        if (p->properties[ WINDOW_TYPES ] & DesktopMask)
	    atoms[pnum++] = net_wm_window_type_desktop;
        if (p->properties[ WINDOW_TYPES ] & DockMask)
            atoms[pnum++] = net_wm_window_type_dock;
        if (p->properties[ WINDOW_TYPES ] & ToolbarMask)
	    atoms[pnum++] = net_wm_window_type_toolbar;
        if (p->properties[ WINDOW_TYPES ] & MenuMask)
	    atoms[pnum++] = net_wm_window_type_menu;
        if (p->properties[ WINDOW_TYPES ] & DialogMask)
	    atoms[pnum++] = net_wm_window_type_dialog;
        if (p->properties[ WINDOW_TYPES ] & UtilityMask)
	    atoms[pnum++] = net_wm_window_type_utility;
        if (p->properties[ WINDOW_TYPES ] & SplashMask)
	    atoms[pnum++] = net_wm_window_type_splash;
        if (p->properties[ WINDOW_TYPES ] & DropdownMenuMask)
	    atoms[pnum++] = net_wm_window_type_dropdown_menu;
        if (p->properties[ WINDOW_TYPES ] & PopupMenuMask)
	    atoms[pnum++] = net_wm_window_type_popup_menu;
        if (p->properties[ WINDOW_TYPES ] & TooltipMask)
	    atoms[pnum++] = net_wm_window_type_tooltip;
        if (p->properties[ WINDOW_TYPES ] & NotificationMask)
	    atoms[pnum++] = net_wm_window_type_notification;
        if (p->properties[ WINDOW_TYPES ] & ComboBoxMask)
	    atoms[pnum++] = net_wm_window_type_combobox;
        if (p->properties[ WINDOW_TYPES ] & DNDIconMask)
	    atoms[pnum++] = net_wm_window_type_dnd;
	// KDE extensions
        if (p->properties[ WINDOW_TYPES ] & OverrideMask)
	    atoms[pnum++] = kde_net_wm_window_type_override;
        if (p->properties[ WINDOW_TYPES ] & TopMenuMask)
	    atoms[pnum++] = kde_net_wm_window_type_topmenu;
    }

    if (p->properties[ PROTOCOLS ] & WMState) {
	atoms[pnum++] = net_wm_state;

	// Application window states
        if (p->properties[ STATES ] & Modal)
    	    atoms[pnum++] = net_wm_state_modal;
        if (p->properties[ STATES ] & Sticky)
	    atoms[pnum++] = net_wm_state_sticky;
        if (p->properties[ STATES ] & MaxVert)
	    atoms[pnum++] = net_wm_state_max_vert;
        if (p->properties[ STATES ] & MaxHoriz)
	    atoms[pnum++] = net_wm_state_max_horiz;
        if (p->properties[ STATES ] & Shaded)
	    atoms[pnum++] = net_wm_state_shaded;
        if (p->properties[ STATES ] & SkipTaskbar)
	    atoms[pnum++] = net_wm_state_skip_taskbar;
        if (p->properties[ STATES ] & SkipPager)
	    atoms[pnum++] = net_wm_state_skip_pager;
        if (p->properties[ STATES ] & Hidden)
	    atoms[pnum++] = net_wm_state_hidden;
        if (p->properties[ STATES ] & FullScreen)
	    atoms[pnum++] = net_wm_state_fullscreen;
        if (p->properties[ STATES ] & KeepAbove)
	    atoms[pnum++] = net_wm_state_above;
        if (p->properties[ STATES ] & KeepBelow)
	    atoms[pnum++] = net_wm_state_below;
        if (p->properties[ STATES ] & DemandsAttention)
	    atoms[pnum++] = net_wm_state_demands_attention;

        if (p->properties[ STATES ] & StaysOnTop)
	    atoms[pnum++] = net_wm_state_stays_on_top;
    }

    if (p->properties[ PROTOCOLS ] & WMStrut)
	atoms[pnum++] = net_wm_strut;

    if (p->properties[ PROTOCOLS2 ] & WM2ExtendedStrut)
	atoms[pnum++] = net_wm_extended_strut;

    if (p->properties[ PROTOCOLS ] & WMIconGeometry)
	atoms[pnum++] = net_wm_icon_geometry;

    if (p->properties[ PROTOCOLS ] & WMIcon)
	atoms[pnum++] = net_wm_icon;

    if (p->properties[ PROTOCOLS ] & WMPid)
	atoms[pnum++] = net_wm_pid;

    if (p->properties[ PROTOCOLS ] & WMHandledIcons)
	atoms[pnum++] = net_wm_handled_icons;

    if (p->properties[ PROTOCOLS ] & WMPing)
	atoms[pnum++] = net_wm_ping;

    if (p->properties[ PROTOCOLS2 ] & WM2TakeActivity)
	atoms[pnum++] = net_wm_take_activity;

    if (p->properties[ PROTOCOLS2 ] & WM2UserTime)
	atoms[pnum++] = net_wm_user_time;

    if (p->properties[ PROTOCOLS2 ] & WM2StartupId)
	atoms[pnum++] = net_startup_id;

    if (p->properties[ PROTOCOLS2 ] & WM2AllowedActions) {
        atoms[pnum++] = net_wm_allowed_actions;

	// Actions
        if (p->properties[ ACTIONS ] & ActionMove)
    	    atoms[pnum++] = net_wm_action_move;
        if (p->properties[ ACTIONS ] & ActionResize)
    	    atoms[pnum++] = net_wm_action_resize;
        if (p->properties[ ACTIONS ] & ActionMinimize)
    	    atoms[pnum++] = net_wm_action_minimize;
        if (p->properties[ ACTIONS ] & ActionShade)
    	    atoms[pnum++] = net_wm_action_shade;
        if (p->properties[ ACTIONS ] & ActionStick)
    	    atoms[pnum++] = net_wm_action_stick;
        if (p->properties[ ACTIONS ] & ActionMaxVert)
    	    atoms[pnum++] = net_wm_action_max_vert;
        if (p->properties[ ACTIONS ] & ActionMaxHoriz)
    	    atoms[pnum++] = net_wm_action_max_horiz;
        if (p->properties[ ACTIONS ] & ActionFullScreen)
    	    atoms[pnum++] = net_wm_action_fullscreen;
        if (p->properties[ ACTIONS ] & ActionChangeDesktop)
    	    atoms[pnum++] = net_wm_action_change_desk;
        if (p->properties[ ACTIONS ] & ActionClose)
    	    atoms[pnum++] = net_wm_action_close;
    }

    // KDE specific extensions
    if (p->properties[ PROTOCOLS ] & KDESystemTrayWindows)
	atoms[pnum++] = kde_net_system_tray_windows;

    if (p->properties[ PROTOCOLS ] & WMKDESystemTrayWinFor)
	atoms[pnum++] = kde_net_wm_system_tray_window_for;

    if (p->properties[ PROTOCOLS ] & WMFrameExtents) {
	atoms[pnum++] = net_frame_extents;
	atoms[pnum++] = kde_net_wm_frame_strut;
    }

    if (p->properties[ PROTOCOLS2 ] & WM2KDETemporaryRules)
	atoms[pnum++] = kde_net_wm_temporary_rules;
    if (p->properties[ PROTOCOLS2 ] & WM2FullPlacement)
	atoms[pnum++] = net_wm_full_placement;

    XChangeProperty(p->display, p->root, net_supported, XA_ATOM, 32,
		    PropModeReplace, (unsigned char *) atoms, pnum);
    XChangeProperty(p->display, p->root, net_supporting_wm_check, XA_WINDOW, 32,
	 	    PropModeReplace, (unsigned char *) &(p->supportwindow), 1);

#ifdef    NETWMDEBUG
    fprintf(stderr,
	    "NETRootInfo::setSupported: _NET_SUPPORTING_WM_CHECK = 0x%lx on 0x%lx\n"
	    "                         : _NET_WM_NAME = '%s' on 0x%lx\n",
	    p->supportwindow, p->supportwindow, p->name, p->supportwindow);
#endif

    XChangeProperty(p->display, p->supportwindow, net_supporting_wm_check,
		    XA_WINDOW, 32, PropModeReplace,
		    (unsigned char *) &(p->supportwindow), 1);
    XChangeProperty(p->display, p->supportwindow, net_wm_name, UTF8_STRING, 8,
		    PropModeReplace, (unsigned char *) p->name,
		    strlen(p->name));
}

void NETRootInfo::updateSupportedProperties( Atom atom )
{
    if( atom == net_supported )
        p->properties[ PROTOCOLS ] |= Supported;

    else if( atom == net_supporting_wm_check )
        p->properties[ PROTOCOLS ] |= SupportingWMCheck;

    else if( atom == net_client_list )
        p->properties[ PROTOCOLS ] |= ClientList;

    else if( atom == net_client_list_stacking )
        p->properties[ PROTOCOLS ] |= ClientListStacking;

    else if( atom == net_number_of_desktops )
        p->properties[ PROTOCOLS ] |= NumberOfDesktops;

    else if( atom == net_desktop_geometry )
        p->properties[ PROTOCOLS ] |= DesktopGeometry;

    else if( atom == net_desktop_viewport )
        p->properties[ PROTOCOLS ] |= DesktopViewport;

    else if( atom == net_current_desktop )
        p->properties[ PROTOCOLS ] |= CurrentDesktop;

    else if( atom == net_desktop_names )
        p->properties[ PROTOCOLS ] |= DesktopNames;

    else if( atom == net_active_window )
        p->properties[ PROTOCOLS ] |= ActiveWindow;

    else if( atom == net_workarea )
        p->properties[ PROTOCOLS ] |= WorkArea;

    else if( atom == net_virtual_roots )
        p->properties[ PROTOCOLS ] |= VirtualRoots;

    else if( atom == net_desktop_layout )
        p->properties[ PROTOCOLS2 ] |= WM2DesktopLayout;

    else if( atom == net_close_window )
        p->properties[ PROTOCOLS ] |= CloseWindow;

    else if( atom == net_restack_window )
        p->properties[ PROTOCOLS2 ] |= WM2RestackWindow;

    else if( atom == net_showing_desktop )
        p->properties[ PROTOCOLS2 ] |= WM2ShowingDesktop;

    // Application window properties/messages
    else if( atom == net_wm_moveresize )
        p->properties[ PROTOCOLS ] |= WMMoveResize;

    else if( atom == net_moveresize_window )
        p->properties[ PROTOCOLS2 ] |= WM2MoveResizeWindow;

    else if( atom == net_wm_name )
        p->properties[ PROTOCOLS ] |= WMName;

    else if( atom == net_wm_visible_name )
        p->properties[ PROTOCOLS ] |= WMVisibleName;

    else if( atom == net_wm_icon_name )
        p->properties[ PROTOCOLS ] |= WMIconName;

    else if( atom == net_wm_visible_icon_name )
        p->properties[ PROTOCOLS ] |= WMVisibleIconName;

    else if( atom == net_wm_desktop )
        p->properties[ PROTOCOLS ] |= WMDesktop;

    else if( atom == net_wm_window_type )
        p->properties[ PROTOCOLS ] |= WMWindowType;

	// Application window types
    else if( atom == net_wm_window_type_normal )
        p->properties[ WINDOW_TYPES ] |= NormalMask;
    else if( atom == net_wm_window_type_desktop )
        p->properties[ WINDOW_TYPES ] |= DesktopMask;
    else if( atom == net_wm_window_type_dock )
        p->properties[ WINDOW_TYPES ] |= DockMask;
    else if( atom == net_wm_window_type_toolbar )
        p->properties[ WINDOW_TYPES ] |= ToolbarMask;
    else if( atom == net_wm_window_type_menu )
        p->properties[ WINDOW_TYPES ] |= MenuMask;
    else if( atom == net_wm_window_type_dialog )
        p->properties[ WINDOW_TYPES ] |= DialogMask;
    else if( atom == net_wm_window_type_utility )
        p->properties[ WINDOW_TYPES ] |= UtilityMask;
    else if( atom == net_wm_window_type_splash )
        p->properties[ WINDOW_TYPES ] |= SplashMask;
    else if( atom == net_wm_window_type_dropdown_menu )
        p->properties[ WINDOW_TYPES ] |= DropdownMenuMask;
    else if( atom == net_wm_window_type_popup_menu )
        p->properties[ WINDOW_TYPES ] |= PopupMenuMask;
    else if( atom == net_wm_window_type_tooltip )
        p->properties[ WINDOW_TYPES ] |= TooltipMask;
    else if( atom == net_wm_window_type_notification )
        p->properties[ WINDOW_TYPES ] |= NotificationMask;
    else if( atom == net_wm_window_type_combobox )
        p->properties[ WINDOW_TYPES ] |= ComboBoxMask;
    else if( atom == net_wm_window_type_dnd )
        p->properties[ WINDOW_TYPES ] |= DNDIconMask;
	// KDE extensions
    else if( atom == kde_net_wm_window_type_override )
        p->properties[ WINDOW_TYPES ] |= OverrideMask;
    else if( atom == kde_net_wm_window_type_topmenu )
        p->properties[ WINDOW_TYPES ] |= TopMenuMask;

    else if( atom == net_wm_state )
        p->properties[ PROTOCOLS ] |= WMState;

	// Application window states
    else if( atom == net_wm_state_modal )
        p->properties[ STATES ] |= Modal;
    else if( atom == net_wm_state_sticky )
        p->properties[ STATES ] |= Sticky;
    else if( atom == net_wm_state_max_vert )
        p->properties[ STATES ] |= MaxVert;
    else if( atom == net_wm_state_max_horiz )
        p->properties[ STATES ] |= MaxHoriz;
    else if( atom == net_wm_state_shaded )
        p->properties[ STATES ] |= Shaded;
    else if( atom == net_wm_state_skip_taskbar )
        p->properties[ STATES ] |= SkipTaskbar;
    else if( atom == net_wm_state_skip_pager )
        p->properties[ STATES ] |= SkipPager;
    else if( atom == net_wm_state_hidden )
        p->properties[ STATES ] |= Hidden;
    else if( atom == net_wm_state_fullscreen )
        p->properties[ STATES ] |= FullScreen;
    else if( atom == net_wm_state_above )
        p->properties[ STATES ] |= KeepAbove;
    else if( atom == net_wm_state_below )
        p->properties[ STATES ] |= KeepBelow;
    else if( atom == net_wm_state_demands_attention )
        p->properties[ STATES ] |= DemandsAttention;

    else if( atom == net_wm_state_stays_on_top )
        p->properties[ STATES ] |= StaysOnTop;

    else if( atom == net_wm_strut )
        p->properties[ PROTOCOLS ] |= WMStrut;

    else if( atom == net_wm_extended_strut )
        p->properties[ PROTOCOLS2 ] |= WM2ExtendedStrut;

    else if( atom == net_wm_icon_geometry )
        p->properties[ PROTOCOLS ] |= WMIconGeometry;

    else if( atom == net_wm_icon )
        p->properties[ PROTOCOLS ] |= WMIcon;

    else if( atom == net_wm_pid )
        p->properties[ PROTOCOLS ] |= WMPid;

    else if( atom == net_wm_handled_icons )
        p->properties[ PROTOCOLS ] |= WMHandledIcons;

    else if( atom == net_wm_ping )
        p->properties[ PROTOCOLS ] |= WMPing;

    else if( atom == net_wm_take_activity )
        p->properties[ PROTOCOLS2 ] |= WM2TakeActivity;

    else if( atom == net_wm_user_time )
        p->properties[ PROTOCOLS2 ] |= WM2UserTime;

    else if( atom == net_startup_id )
        p->properties[ PROTOCOLS2 ] |= WM2StartupId;

    else if( atom == net_wm_allowed_actions )
        p->properties[ PROTOCOLS2 ] |= WM2AllowedActions;

        // Actions
    else if( atom == net_wm_action_move )
        p->properties[ ACTIONS ] |= ActionMove;
    else if( atom == net_wm_action_resize )
        p->properties[ ACTIONS ] |= ActionResize;
    else if( atom == net_wm_action_minimize )
        p->properties[ ACTIONS ] |= ActionMinimize;
    else if( atom == net_wm_action_shade )
        p->properties[ ACTIONS ] |= ActionShade;
    else if( atom == net_wm_action_stick )
        p->properties[ ACTIONS ] |= ActionStick;
    else if( atom == net_wm_action_max_vert )
        p->properties[ ACTIONS ] |= ActionMaxVert;
    else if( atom == net_wm_action_max_horiz )
        p->properties[ ACTIONS ] |= ActionMaxHoriz;
    else if( atom == net_wm_action_fullscreen )
        p->properties[ ACTIONS ] |= ActionFullScreen;
    else if( atom == net_wm_action_change_desk )
        p->properties[ ACTIONS ] |= ActionChangeDesktop;
    else if( atom == net_wm_action_close )
        p->properties[ ACTIONS ] |= ActionClose;

    // KDE specific extensions
    else if( atom == kde_net_system_tray_windows )
        p->properties[ PROTOCOLS ] |= KDESystemTrayWindows;

    else if( atom == kde_net_wm_system_tray_window_for )
        p->properties[ PROTOCOLS ] |= WMKDESystemTrayWinFor;

    else if( atom == net_frame_extents )
        p->properties[ PROTOCOLS ] |= WMFrameExtents;
    else if( atom == kde_net_wm_frame_strut )
        p->properties[ PROTOCOLS ] |= WMKDEFrameStrut;

    else if( atom == kde_net_wm_temporary_rules )
        p->properties[ PROTOCOLS2 ] |= WM2KDETemporaryRules;
    else if( atom == net_wm_full_placement )
        p->properties[ PROTOCOLS2 ] |= WM2FullPlacement;
}

void NETRootInfo::setActiveWindow(Window window) {
    setActiveWindow( window, FromUnknown, GET_QT_X_USER_TIME(), None );
}

void NETRootInfo::setActiveWindow(Window window, NET::RequestSource src,
    Time timestamp, Window active_window ) {

#ifdef    NETWMDEBUG
    fprintf(stderr, "NETRootInfo::setActiveWindow(0x%lx) (%s)\n",
            window, (role == WindowManager) ? "WM" : "Client");
#endif

    if (role == WindowManager) {
	p->active = window;
	XChangeProperty(p->display, p->root, net_active_window, XA_WINDOW, 32,
			PropModeReplace, (unsigned char *) &(p->active), 1);
    } else {
	XEvent e;

	e.xclient.type = ClientMessage;
	e.xclient.message_type = net_active_window;
	e.xclient.display = p->display;
	e.xclient.window = window;
	e.xclient.format = 32;
	e.xclient.data.l[0] = src;
	e.xclient.data.l[1] = timestamp;
	e.xclient.data.l[2] = active_window;
	e.xclient.data.l[3] = 0l;
	e.xclient.data.l[4] = 0l;

	XSendEvent(p->display, p->root, False, netwm_sendevent_mask, &e);
    }
}


void NETRootInfo::setWorkArea(int desktop, const NETRect &workarea) {

#ifdef    NETWMDEBUG
    fprintf(stderr, "NETRootInfo::setWorkArea(%d, { %d, %d, %d, %d }) (%s)\n",
	    desktop, workarea.pos.x, workarea.pos.y, workarea.size.width, workarea.size.height,
	    (role == WindowManager) ? "WM" : "Client");
#endif

    if (role != WindowManager || desktop < 1) return;

    p->workarea[desktop - 1] = workarea;

    long *wa = new long[p->number_of_desktops * 4];
    int i, o;
    for (i = 0, o = 0; i < p->number_of_desktops; i++) {
	wa[o++] = p->workarea[i].pos.x;
	wa[o++] = p->workarea[i].pos.y;
	wa[o++] = p->workarea[i].size.width;
	wa[o++] = p->workarea[i].size.height;
    }

    XChangeProperty(p->display, p->root, net_workarea, XA_CARDINAL, 32,
		    PropModeReplace, (unsigned char *) wa,
		    p->number_of_desktops * 4);

    delete [] wa;
}


void NETRootInfo::setVirtualRoots(Window *windows, unsigned int count) {
    if (role != WindowManager) return;

    p->virtual_roots_count = count;
    p->virtual_roots = windows;

#ifdef   NETWMDEBUG
    fprintf(stderr, "NETRootInfo::setVirtualRoots: setting list with %ld windows\n",
	    p->virtual_roots_count);
#endif

    XChangeProperty(p->display, p->root, net_virtual_roots, XA_WINDOW, 32,
		    PropModeReplace, (unsigned char *) p->virtual_roots,
		    p->virtual_roots_count);
}


void NETRootInfo::setDesktopLayout(NET::Orientation orientation, int columns, int rows,
    NET::DesktopLayoutCorner corner)
{
    p->desktop_layout_orientation = orientation;
    p->desktop_layout_columns = columns;
    p->desktop_layout_rows = rows;
    p->desktop_layout_corner = corner;

#ifdef   NETWMDEBUG
    fprintf(stderr, "NETRootInfo::setDesktopLayout: %d %d %d %d\n",
	    orientation, columns, rows, corner);
#endif

    long data[ 4 ];
    data[ 0 ] = orientation;
    data[ 1 ] = columns;
    data[ 2 ] = rows;
    data[ 3 ] = corner;
    XChangeProperty(p->display, p->root, net_desktop_layout, XA_CARDINAL, 32,
		    PropModeReplace, (unsigned char *) &data, 4);
}


void NETRootInfo::setShowingDesktop( bool showing ) {
    if (role == WindowManager) {
	long d = p->showing_desktop = showing;
	XChangeProperty(p->display, p->root, net_showing_desktop, XA_CARDINAL, 32,
			PropModeReplace, (unsigned char *) &d, 1);
    } else {
	XEvent e;

	e.xclient.type = ClientMessage;
	e.xclient.message_type = net_showing_desktop;
	e.xclient.display = p->display;
	e.xclient.window = 0;
	e.xclient.format = 32;
	e.xclient.data.l[0] = showing ? 1 : 0;
	e.xclient.data.l[1] = 0;
	e.xclient.data.l[2] = 0;
	e.xclient.data.l[3] = 0;
	e.xclient.data.l[4] = 0;

	XSendEvent(p->display, p->root, False, netwm_sendevent_mask, &e);
    }
}


bool NETRootInfo::showingDesktop() const {
    return p->showing_desktop;
}


void NETRootInfo::closeWindowRequest(Window window) {

#ifdef    NETWMDEBUG
    fprintf(stderr, "NETRootInfo::closeWindowRequest: requesting close for 0x%lx\n",
	    window);
#endif

    XEvent e;

    e.xclient.type = ClientMessage;
    e.xclient.message_type = net_close_window;
    e.xclient.display = p->display;
    e.xclient.window = window;
    e.xclient.format = 32;
    e.xclient.data.l[0] = 0l;
    e.xclient.data.l[1] = 0l;
    e.xclient.data.l[2] = 0l;
    e.xclient.data.l[3] = 0l;
    e.xclient.data.l[4] = 0l;

    XSendEvent(p->display, p->root, False, netwm_sendevent_mask, &e);
}


void NETRootInfo::moveResizeRequest(Window window, int x_root, int y_root,
				    Direction direction)
{

#ifdef    NETWMDEBUG
    fprintf(stderr,
	    "NETRootInfo::moveResizeRequest: requesting resize/move for 0x%lx (%d, %d, %d)\n",
	    window, x_root, y_root, direction);
#endif

    XEvent e;

    e.xclient.type = ClientMessage;
    e.xclient.message_type = net_wm_moveresize;
    e.xclient.display = p->display;
    e.xclient.window = window,
    e.xclient.format = 32;
    e.xclient.data.l[0] = x_root;
    e.xclient.data.l[1] = y_root;
    e.xclient.data.l[2] = direction;
    e.xclient.data.l[3] = 0l;
    e.xclient.data.l[4] = 0l;

    XSendEvent(p->display, p->root, False, netwm_sendevent_mask, &e);
}

void NETRootInfo::moveResizeWindowRequest(Window window, int flags, int x, int y, int width, int height )
{

#ifdef    NETWMDEBUG
    fprintf(stderr,
	    "NETRootInfo::moveResizeWindowRequest: resizing/moving 0x%lx (%d, %d, %d, %d, %d)\n",
	    window, flags, x, y, width, height);
#endif

    XEvent e;

    e.xclient.type = ClientMessage;
    e.xclient.message_type = net_moveresize_window;
    e.xclient.display = p->display;
    e.xclient.window = window,
    e.xclient.format = 32;
    e.xclient.data.l[0] = flags;
    e.xclient.data.l[1] = x;
    e.xclient.data.l[2] = y;
    e.xclient.data.l[3] = width;
    e.xclient.data.l[4] = height;

    XSendEvent(p->display, p->root, False, netwm_sendevent_mask, &e);
}

void NETRootInfo::restackRequest(Window window, Window above, int detail)
{
    restackRequest( window, FromTool, above, detail, GET_QT_X_USER_TIME() );
}

void NETRootInfo::restackRequest(Window window, RequestSource src, Window above, int detail, Time timestamp )
{
#ifdef    NETWMDEBUG
    fprintf(stderr,
	    "NETRootInfo::restackRequest: requesting restack for 0x%lx (%lx, %d)\n",
	    window, above, detail);
#endif

    XEvent e;

    e.xclient.type = ClientMessage;
    e.xclient.message_type = net_restack_window;
    e.xclient.display = p->display;
    e.xclient.window = window,
    e.xclient.format = 32;
    e.xclient.data.l[0] = src;
    e.xclient.data.l[1] = above;
    e.xclient.data.l[2] = detail;
    e.xclient.data.l[3] = timestamp;
    e.xclient.data.l[4] = 0l;

    XSendEvent(p->display, p->root, False, netwm_sendevent_mask, &e);
}

void NETRootInfo2::sendPing( Window window, Time timestamp )
{
    if (role != WindowManager) return;
#ifdef   NETWMDEBUG
    fprintf(stderr, "NETRootInfo2::setPing: window 0x%lx, timestamp %lu\n",
	window, timestamp );
#endif
    XEvent e;
    e.xclient.type = ClientMessage;
    e.xclient.message_type = wm_protocols;
    e.xclient.display = p->display;
    e.xclient.window = window,
    e.xclient.format = 32;
    e.xclient.data.l[0] = net_wm_ping;
    e.xclient.data.l[1] = timestamp;
    e.xclient.data.l[2] = window;
    e.xclient.data.l[3] = 0;
    e.xclient.data.l[4] = 0;

    XSendEvent(p->display, window, False, 0, &e);
}

void NETRootInfo3::takeActivity( Window window, Time timestamp, long flags )
{
    if (role != WindowManager) return;
#ifdef   NETWMDEBUG
    fprintf(stderr, "NETRootInfo2::takeActivity: window 0x%lx, timestamp %lu, flags 0x%lx\n",
	window, timestamp, flags );
#endif
    XEvent e;
    e.xclient.type = ClientMessage;
    e.xclient.message_type = wm_protocols;
    e.xclient.display = p->display;
    e.xclient.window = window,
    e.xclient.format = 32;
    e.xclient.data.l[0] = net_wm_take_activity;
    e.xclient.data.l[1] = timestamp;
    e.xclient.data.l[2] = window;
    e.xclient.data.l[3] = flags;
    e.xclient.data.l[4] = 0;

    XSendEvent(p->display, window, False, 0, &e);
}



// assignment operator

const NETRootInfo &NETRootInfo::operator=(const NETRootInfo &rootinfo) {

#ifdef   NETWMDEBUG
    fprintf(stderr, "NETRootInfo::operator=()\n");
#endif

    if (p != rootinfo.p) {
	refdec_nri(p);

	if (! p->ref) delete p;
    }

    p = rootinfo.p;
    role = rootinfo.role;
    p->ref++;

    return *this;
}

unsigned long NETRootInfo::event(XEvent *ev )
{
    unsigned long props[ 1 ];
    event( ev, props, 1 );
    return props[ 0 ];
}

void NETRootInfo::event(XEvent *event, unsigned long* properties, int properties_size )
{
    unsigned long props[ PROPERTIES_SIZE ] = { 0, 0, 0, 0, 0 };
    assert( PROPERTIES_SIZE == 5 ); // add elements above
    unsigned long& dirty = props[ PROTOCOLS ];
    unsigned long& dirty2 = props[ PROTOCOLS2 ];
    bool do_update = false;

    // the window manager will be interested in client messages... no other
    // client should get these messages
    if (role == WindowManager && event->type == ClientMessage &&
	event->xclient.format == 32) {
#ifdef    NETWMDEBUG
	fprintf(stderr, "NETRootInfo::event: handling ClientMessage event\n");
#endif

	if (event->xclient.message_type == net_number_of_desktops) {
	    dirty = NumberOfDesktops;

#ifdef   NETWMDEBUG
	    fprintf(stderr, "NETRootInfo::event: changeNumberOfDesktops(%ld)\n",
		    event->xclient.data.l[0]);
#endif

	    changeNumberOfDesktops(event->xclient.data.l[0]);
	} else if (event->xclient.message_type == net_desktop_geometry) {
	    dirty = DesktopGeometry;

	    NETSize sz;
	    sz.width = event->xclient.data.l[0];
	    sz.height = event->xclient.data.l[1];

#ifdef    NETWMDEBUG
	    fprintf(stderr, "NETRootInfo::event: changeDesktopGeometry( -- , { %d, %d })\n",
		    sz.width, sz.height);
#endif

	    changeDesktopGeometry(~0, sz);
	} else if (event->xclient.message_type == net_desktop_viewport) {
	    dirty = DesktopViewport;

	    NETPoint pt;
	    pt.x = event->xclient.data.l[0];
	    pt.y = event->xclient.data.l[1];

#ifdef   NETWMDEBUG
	    fprintf(stderr, "NETRootInfo::event: changeDesktopViewport(%d, { %d, %d })\n",
		    p->current_desktop, pt.x, pt.y);
#endif

	    changeDesktopViewport(p->current_desktop, pt);
	} else if (event->xclient.message_type == net_current_desktop) {
	    dirty = CurrentDesktop;

#ifdef   NETWMDEBUG
	    fprintf(stderr, "NETRootInfo::event: changeCurrentDesktop(%ld)\n",
		    event->xclient.data.l[0] + 1);
#endif

	    changeCurrentDesktop(event->xclient.data.l[0] + 1);
	} else if (event->xclient.message_type == net_active_window) {
	    dirty = ActiveWindow;

#ifdef    NETWMDEBUG
	    fprintf(stderr, "NETRootInfo::event: changeActiveWindow(0x%lx)\n",
		    event->xclient.window);
#endif

	    changeActiveWindow(event->xclient.window);
	    if( NETRootInfo2* this2 = dynamic_cast< NETRootInfo2* >( this ))
            {
                RequestSource src = FromUnknown;
                Time timestamp = CurrentTime;
                Window active_window = None;
                // make sure there aren't unknown values
                if( event->xclient.data.l[0] >= FromUnknown
                    && event->xclient.data.l[0] <= FromTool )
                    {
                    src = static_cast< RequestSource >( event->xclient.data.l[0] );
                    timestamp = event->xclient.data.l[1];
                    active_window = event->xclient.data.l[2];
                    }
		this2->changeActiveWindow( event->xclient.window, src, timestamp, active_window );
            }
	} else if (event->xclient.message_type == net_wm_moveresize) {

#ifdef    NETWMDEBUG
	    fprintf(stderr, "NETRootInfo::event: moveResize(%ld, %ld, %ld, %ld)\n",
		    event->xclient.window,
		    event->xclient.data.l[0],
		    event->xclient.data.l[1],
		    event->xclient.data.l[2]
		    );
#endif

	    moveResize(event->xclient.window,
		       event->xclient.data.l[0],
		       event->xclient.data.l[1],
		       event->xclient.data.l[2]);
	} else if (event->xclient.message_type == net_moveresize_window) {

#ifdef    NETWMDEBUG
	    fprintf(stderr, "NETRootInfo::event: moveResizeWindow(%ld, %ld, %ld, %ld, %ld, %ld)\n",
		    event->xclient.window,
		    event->xclient.data.l[0],
		    event->xclient.data.l[1],
		    event->xclient.data.l[2],
		    event->xclient.data.l[3],
		    event->xclient.data.l[4]
		    );
#endif

	    if( NETRootInfo2* this2 = dynamic_cast< NETRootInfo2* >( this ))
	        this2->moveResizeWindow(event->xclient.window,
		           event->xclient.data.l[0],
		           event->xclient.data.l[1],
		           event->xclient.data.l[2],
		           event->xclient.data.l[3],
		           event->xclient.data.l[4]);
	} else if (event->xclient.message_type == net_close_window) {

#ifdef   NETWMDEBUG
	    fprintf(stderr, "NETRootInfo::event: closeWindow(0x%lx)\n",
		    event->xclient.window);
#endif

	    closeWindow(event->xclient.window);
	} else if (event->xclient.message_type == net_restack_window) {

#ifdef   NETWMDEBUG
	    fprintf(stderr, "NETRootInfo::event: restackWindow(0x%lx)\n",
		    event->xclient.window);
#endif

	    if( NETRootInfo3* this3 = dynamic_cast< NETRootInfo3* >( this ))
            {
                RequestSource src = FromUnknown;
                Time timestamp = CurrentTime;
                // make sure there aren't unknown values
                if( event->xclient.data.l[0] >= FromUnknown
                    && event->xclient.data.l[0] <= FromTool )
                    {
                    src = static_cast< RequestSource >( event->xclient.data.l[0] );
                    timestamp = event->xclient.data.l[3];
                    }
	        this3->restackWindow(event->xclient.window, src,
                    event->xclient.data.l[1], event->xclient.data.l[2], timestamp);
            }
	    else if( NETRootInfo2* this2 = dynamic_cast< NETRootInfo2* >( this ))
	        this2->restackWindow(event->xclient.window,
                    event->xclient.data.l[1], event->xclient.data.l[2]);
	} else if (event->xclient.message_type == wm_protocols
	    && (Atom)event->xclient.data.l[ 0 ] == net_wm_ping) {
	    dirty = WMPing;

#ifdef   NETWMDEBUG
	    fprintf(stderr, "NETRootInfo2::event: gotPing(0x%lx,%lu)\n",
		event->xclient.window, event->xclient.data.l[1]);
#endif
	    if( NETRootInfo2* this2 = dynamic_cast< NETRootInfo2* >( this ))
		this2->gotPing( event->xclient.data.l[2], event->xclient.data.l[1]);
	} else if (event->xclient.message_type == wm_protocols
	    && (Atom)event->xclient.data.l[ 0 ] == net_wm_take_activity) {
	    dirty2 = WM2TakeActivity;

#ifdef   NETWMDEBUG
	    fprintf(stderr, "NETRootInfo2::event: gotTakeActivity(0x%lx,%lu,0x%lx)\n",
		event->xclient.window, event->xclient.data.l[1], event->xclient.data.l[3]);
#endif
	    if( NETRootInfo3* this3 = dynamic_cast< NETRootInfo3* >( this ))
		this3->gotTakeActivity( event->xclient.data.l[2], event->xclient.data.l[1],
                    event->xclient.data.l[3]);
	} else if (event->xclient.message_type == net_showing_desktop) {
	    dirty2 = WM2ShowingDesktop;

#ifdef   NETWMDEBUG
	    fprintf(stderr, "NETRootInfo::event: changeShowingDesktop(%ld)\n",
		    event->xclient.data.l[0]);
#endif

	    if( NETRootInfo4* this4 = dynamic_cast< NETRootInfo4* >( this ))
	        this4->changeShowingDesktop(event->xclient.data.l[0]);
	}
    }

    if (event->type == PropertyNotify) {

#ifdef    NETWMDEBUG
	fprintf(stderr, "NETRootInfo::event: handling PropertyNotify event\n");
#endif

	XEvent pe = *event;

	Bool done = False;
	Bool compaction = False;
	while (! done) {

#ifdef   NETWMDEBUG
	    fprintf(stderr, "NETRootInfo::event: loop fire\n");
#endif

	    if (pe.xproperty.atom == net_client_list)
		dirty |= ClientList;
	    else if (pe.xproperty.atom == net_client_list_stacking)
		dirty |= ClientListStacking;
	    else if (pe.xproperty.atom == kde_net_system_tray_windows)
		dirty |= KDESystemTrayWindows;
	    else if (pe.xproperty.atom == net_desktop_names)
		dirty |= DesktopNames;
	    else if (pe.xproperty.atom == net_workarea)
		dirty |= WorkArea;
	    else if (pe.xproperty.atom == net_number_of_desktops)
		dirty |= NumberOfDesktops;
	    else if (pe.xproperty.atom == net_desktop_geometry)
		dirty |= DesktopGeometry;
	    else if (pe.xproperty.atom == net_desktop_viewport)
		dirty |= DesktopViewport;
	    else if (pe.xproperty.atom == net_current_desktop)
		dirty |= CurrentDesktop;
	    else if (pe.xproperty.atom == net_active_window)
		dirty |= ActiveWindow;
	    else if (pe.xproperty.atom == net_showing_desktop)
		dirty2 |= WM2ShowingDesktop;
//            else if (pe.xproperty.atom == net_supported )
//                dirty |= Supported; // update here?
            else if (pe.xproperty.atom == net_supporting_wm_check )
                dirty |= SupportingWMCheck;
            else if (pe.xproperty.atom == net_virtual_roots )
                dirty |= VirtualRoots;
            else if (pe.xproperty.atom == net_desktop_layout )
                dirty2 |= WM2DesktopLayout;
	    else {

#ifdef    NETWMDEBUG
		fprintf(stderr, "NETRootInfo::event: putting back event and breaking\n");
#endif

		if ( compaction )
		    XPutBackEvent(p->display, &pe);
		break;
	    }

	    if (XCheckTypedWindowEvent(p->display, p->root, PropertyNotify, &pe) )
		compaction = True;
	    else
		break;
	}

	do_update = true;
    }

    if( do_update )
        update( props );

#ifdef   NETWMDEBUG
     fprintf(stderr, "NETRootInfo::event: handled events, returning dirty = 0x%lx, 0x%lx\n",
 	    dirty, dirty2);
#endif

    if( properties_size > PROPERTIES_SIZE )
        properties_size = PROPERTIES_SIZE;
    for( int i = 0;
         i < properties_size;
         ++i )
        properties[ i ] = props[ i ];
}


// private functions to update the data we keep

void NETRootInfo::update( const unsigned long dirty_props[] )
{
    Atom type_ret;
    int format_ret;
    unsigned char *data_ret;
    unsigned long nitems_ret, unused;
    unsigned long props[ PROPERTIES_SIZE ];
    for( int i = 0;
         i < PROPERTIES_SIZE;
         ++i )
        props[ i ] = dirty_props[ i ] & p->client_properties[ i ];
    const unsigned long& dirty = props[ PROTOCOLS ];
    const unsigned long& dirty2 = props[ PROTOCOLS2 ];

    if (dirty & Supported ) {
        // only in Client mode
        for( int i = 0; i < PROPERTIES_SIZE; ++i )
            p->properties[ i ] = 0;
        if( XGetWindowProperty(p->display, p->root, net_supported,
                               0l, MAX_PROP_SIZE, False, XA_ATOM, &type_ret,
                               &format_ret, &nitems_ret, &unused, &data_ret)
            == Success ) {
            if( type_ret == XA_ATOM && format_ret == 32 ) {
                Atom* atoms = (Atom*) data_ret;
                for( unsigned int i = 0;
                     i < nitems_ret;
                     ++i )
                    updateSupportedProperties( atoms[ i ] );
            }
	    if ( data_ret )
		XFree(data_ret);
        }
    }

    if (dirty & ClientList) {
        bool read_ok = false;
	if (XGetWindowProperty(p->display, p->root, net_client_list,
			       0l, MAX_PROP_SIZE, False, XA_WINDOW, &type_ret,
			       &format_ret, &nitems_ret, &unused, &data_ret)
	    == Success) {
	    if (type_ret == XA_WINDOW && format_ret == 32) {
		Window *wins = (Window *) data_ret;

		qsort(wins, nitems_ret, sizeof(Window), wcmp);

		if (p->clients) {
		    if (role == Client) {
			unsigned long new_index = 0, old_index = 0;
			unsigned long new_count = nitems_ret,
				      old_count = p->clients_count;

			while (old_index < old_count || new_index < new_count) {
			    if (old_index == old_count) {
				addClient(wins[new_index++]);
			    } else if (new_index == new_count) {
				removeClient(p->clients[old_index++]);
			    } else {
				if (p->clients[old_index] <
				    wins[new_index]) {
				    removeClient(p->clients[old_index++]);
				} else if (wins[new_index] <
					   p->clients[old_index]) {
				    addClient(wins[new_index++]);
				} else {
				    new_index++;
				    old_index++;
				}
			    }
			}
		    }

		    delete [] p->clients;
		} else {
#ifdef    NETWMDEBUG
		    fprintf(stderr, "NETRootInfo::update: client list null, creating\n");
#endif

		    unsigned long n;
		    for (n = 0; n < nitems_ret; n++) {
			addClient(wins[n]);
		    }
		}

		p->clients_count = nitems_ret;
		p->clients = nwindup(wins, p->clients_count);
                read_ok = true;
	    }

	    if ( data_ret )
		XFree(data_ret);
	}
        if( !read_ok ) {
            for( unsigned int i = 0; i < p->clients_count; ++ i )
	        removeClient(p->clients[i]);
            p->clients_count = 0;
            delete[] p->clients;
            p->clients = NULL;
        }

#ifdef    NETWMDEBUG
	fprintf(stderr, "NETRootInfo::update: client list updated (%ld clients)\n",
		p->clients_count);
#endif
    }

    if (dirty & KDESystemTrayWindows) {
        bool read_ok = false;
	if (XGetWindowProperty(p->display, p->root, kde_net_system_tray_windows,
			       0l, MAX_PROP_SIZE, False, XA_WINDOW, &type_ret,
			       &format_ret, &nitems_ret, &unused, &data_ret)
	    == Success) {
	    if (type_ret == XA_WINDOW && format_ret == 32) {
		Window *wins = (Window *) data_ret;

		qsort(wins, nitems_ret, sizeof(Window), wcmp);

		if (p->kde_system_tray_windows) {
		    if (role == Client) {
			unsigned long new_index = 0, new_count = nitems_ret;
			unsigned long old_index = 0,
				      old_count = p->kde_system_tray_windows_count;

			while(old_index < old_count || new_index < new_count) {
			    if (old_index == old_count) {
				addSystemTrayWin(wins[new_index++]);
			    } else if (new_index == new_count) {
				removeSystemTrayWin(p->kde_system_tray_windows[old_index++]);
			    } else {
				if (p->kde_system_tray_windows[old_index] <
				    wins[new_index]) {
				    removeSystemTrayWin(p->kde_system_tray_windows[old_index++]);
				} else if (wins[new_index] <
					   p->kde_system_tray_windows[old_index]) {
				    addSystemTrayWin(wins[new_index++]);
				} else {
				    new_index++;
				    old_index++;
				}
			    }
			}
		    }

		} else {
		    unsigned long n;
		    for (n = 0; n < nitems_ret; n++) {
			addSystemTrayWin(wins[n]);
		    }
		}

		p->kde_system_tray_windows_count = nitems_ret;
		delete [] p->kde_system_tray_windows;
		p->kde_system_tray_windows =
		    nwindup(wins, p->kde_system_tray_windows_count);
                read_ok = true;
	    }

	    if ( data_ret )
		XFree(data_ret);
	}
        if( !read_ok ) {
            for( unsigned int i = 0; i < p->kde_system_tray_windows_count; ++i )
                removeSystemTrayWin(p->kde_system_tray_windows[i]);
            p->kde_system_tray_windows_count = 0;
	    delete [] p->kde_system_tray_windows;
            p->kde_system_tray_windows = NULL;
        }
    }

    if (dirty & ClientListStacking) {
        p->stacking_count = 0;
        delete[] p->stacking;
        p->stacking = NULL;
	if (XGetWindowProperty(p->display, p->root, net_client_list_stacking,
			       0, MAX_PROP_SIZE, False, XA_WINDOW, &type_ret,
			       &format_ret, &nitems_ret, &unused, &data_ret)
	    == Success) {
	    if (type_ret == XA_WINDOW && format_ret == 32) {
		Window *wins = (Window *) data_ret;

		p->stacking_count = nitems_ret;
		p->stacking = nwindup(wins, p->stacking_count);
	    }

#ifdef    NETWMDEBUG
	    fprintf(stderr,"NETRootInfo::update: client stacking updated (%ld clients)\n",
		    p->stacking_count);
#endif

	    if ( data_ret )
		XFree(data_ret);
	}
    }

    if (dirty & NumberOfDesktops) {
	p->number_of_desktops = 0;

	if (XGetWindowProperty(p->display, p->root, net_number_of_desktops,
			       0l, 1l, False, XA_CARDINAL, &type_ret, &format_ret,
			       &nitems_ret, &unused, &data_ret)
	    == Success) {
	    if (type_ret == XA_CARDINAL && format_ret == 32 && nitems_ret == 1) {
		p->number_of_desktops = *((long *) data_ret);
	    }

#ifdef    NETWMDEBUG
	    fprintf(stderr, "NETRootInfo::update: number of desktops = %d\n",
		    p->number_of_desktops);
#endif
	    if ( data_ret )
		XFree(data_ret);
	}
    }

    if (dirty & DesktopGeometry) {
        p->geometry = p->rootSize;
	if (XGetWindowProperty(p->display, p->root, net_desktop_geometry,
			       0l, 2l, False, XA_CARDINAL, &type_ret, &format_ret,
			       &nitems_ret, &unused, &data_ret)
	    == Success) {
	    if (type_ret == XA_CARDINAL && format_ret == 32 &&
		nitems_ret == 2) {
		long *data = (long *) data_ret;

		p->geometry.width  = data[0];
		p->geometry.height = data[1];

#ifdef    NETWMDEBUG
		fprintf(stderr, "NETRootInfo::update: desktop geometry updated\n");
#endif
	    }
	    if ( data_ret )
		XFree(data_ret);
	}
    }

    if (dirty & DesktopViewport) {
	for (int i = 0; i < p->viewport.size(); i++)
	    p->viewport[i].x = p->viewport[i].y = 0;
	if (XGetWindowProperty(p->display, p->root, net_desktop_viewport,
			       0l, 2l, False, XA_CARDINAL, &type_ret, &format_ret,
			       &nitems_ret, &unused, &data_ret)
	    == Success) {
	    if (type_ret == XA_CARDINAL && format_ret == 32 &&
		nitems_ret == 2) {
		long *data = (long *) data_ret;

		int d, i, n;
		n = nitems_ret / 2;
		for (d = 0, i = 0; d < n; d++) {
		    p->viewport[d].x = data[i++];
		    p->viewport[d].y = data[i++];
		}

#ifdef    NETWMDEBUG
		fprintf(stderr,
			"NETRootInfo::update: desktop viewport array updated (%d entries)\n",
			p->viewport.size());

		if (nitems_ret % 2 != 0) {
		    fprintf(stderr,
			    "NETRootInfo::update(): desktop viewport array "
			    "size not a multiple of 2\n");
		}
#endif
	    }
	    if ( data_ret )
		XFree(data_ret);
	}
    }

    if (dirty & CurrentDesktop) {
	p->current_desktop = 0;
	if (XGetWindowProperty(p->display, p->root, net_current_desktop,
			       0l, 1l, False, XA_CARDINAL, &type_ret, &format_ret,
			       &nitems_ret, &unused, &data_ret)
	    == Success) {
	    if (type_ret == XA_CARDINAL && format_ret == 32 && nitems_ret == 1) {
		p->current_desktop = *((long *) data_ret) + 1;
	    }

#ifdef    NETWMDEBUG
	    fprintf(stderr, "NETRootInfo::update: current desktop = %d\n",
		    p->current_desktop);
#endif
	    if ( data_ret )
		XFree(data_ret);
	}
    }

    if (dirty & DesktopNames) {
        for( int i = 0; i < p->desktop_names.size(); ++i )
            delete[] p->desktop_names[ i ];
        p->desktop_names.reset();
	if (XGetWindowProperty(p->display, p->root, net_desktop_names,
			       0l, MAX_PROP_SIZE, False, UTF8_STRING, &type_ret,
			       &format_ret, &nitems_ret, &unused, &data_ret)
	    == Success) {
	    if (type_ret == UTF8_STRING && format_ret == 8) {
		const char *d = (const char *) data_ret;
		unsigned int s, n, index;

		for (s = 0, n = 0, index = 0; n < nitems_ret; n++) {
		    if (d[n] == '\0') {
			delete [] p->desktop_names[index];
			p->desktop_names[index++] = nstrndup((d + s), n - s + 1);
			s = n + 1;
		    }
		}
	    }

#ifdef    NETWMDEBUG
	    fprintf(stderr, "NETRootInfo::update: desktop names array updated (%d entries)\n",
		    p->desktop_names.size());
#endif
	    if ( data_ret )
		XFree(data_ret);
	}
    }

    if (dirty & ActiveWindow) {
        p->active = None;
	if (XGetWindowProperty(p->display, p->root, net_active_window, 0l, 1l,
			       False, XA_WINDOW, &type_ret, &format_ret,
			       &nitems_ret, &unused, &data_ret)
	    == Success) {
	    if (type_ret == XA_WINDOW && format_ret == 32 && nitems_ret == 1) {
		p->active = *((Window *) data_ret);
	    }

#ifdef    NETWMDEBUG
	    fprintf(stderr, "NETRootInfo::update: active window = 0x%lx\n",
		    p->active);
#endif
	    if ( data_ret )
		XFree(data_ret);
	}
    }

    if (dirty & WorkArea) {
        p->workarea.reset();
	if (XGetWindowProperty(p->display, p->root, net_workarea, 0l,
			       (p->number_of_desktops * 4), False, XA_CARDINAL,
			       &type_ret, &format_ret, &nitems_ret, &unused,
			       &data_ret)
	    == Success) {
	    if (type_ret == XA_CARDINAL && format_ret == 32 &&
		nitems_ret == (unsigned) (p->number_of_desktops * 4)) {
		long *d = (long *) data_ret;
		int i, j;
		for (i = 0, j = 0; i < p->number_of_desktops; i++) {
		    p->workarea[i].pos.x       = d[j++];
		    p->workarea[i].pos.y       = d[j++];
		    p->workarea[i].size.width  = d[j++];
		    p->workarea[i].size.height = d[j++];
		}
	    }

#ifdef    NETWMDEBUG
	    fprintf(stderr, "NETRootInfo::update: work area array updated (%d entries)\n",
		    p->workarea.size());
#endif
	    if ( data_ret )
		XFree(data_ret);
	}
    }


    if (dirty & SupportingWMCheck) {
        p->supportwindow = None;
        delete[] p->name;
        p->name = NULL;
	if (XGetWindowProperty(p->display, p->root, net_supporting_wm_check,
			       0l, 1l, False, XA_WINDOW, &type_ret, &format_ret,
			       &nitems_ret, &unused, &data_ret)
	    == Success) {
	    if (type_ret == XA_WINDOW && format_ret == 32 && nitems_ret == 1) {
		p->supportwindow = *((Window *) data_ret);

		unsigned char *name_ret;
		if (XGetWindowProperty(p->display, p->supportwindow,
				       net_wm_name, 0l, MAX_PROP_SIZE, False,
				       UTF8_STRING, &type_ret, &format_ret,
				       &nitems_ret, &unused, &name_ret)
		    == Success) {
		    if (type_ret == UTF8_STRING && format_ret == 8)
			p->name = nstrndup((const char *) name_ret, nitems_ret);

		    if ( name_ret )
			XFree(name_ret);
		}
	    }

#ifdef    NETWMDEBUG
	    fprintf(stderr,
		    "NETRootInfo::update: supporting window manager = '%s'\n",
		    p->name);
#endif
	    if ( data_ret )
		XFree(data_ret);
	}
    }

    if (dirty & VirtualRoots) {
        p->virtual_roots_count = 0;
        delete[] p->virtual_roots;
        p->virtual_roots = NULL;
	if (XGetWindowProperty(p->display, p->root, net_virtual_roots,
			       0, MAX_PROP_SIZE, False, XA_WINDOW, &type_ret,
			       &format_ret, &nitems_ret, &unused, &data_ret)
	    == Success) {
	    if (type_ret == XA_WINDOW && format_ret == 32) {
		Window *wins = (Window *) data_ret;

		p->virtual_roots_count = nitems_ret;
		p->virtual_roots = nwindup(wins, p->virtual_roots_count);
	    }

#ifdef    NETWMDEBUG
	    fprintf(stderr, "NETRootInfo::updated: virtual roots updated (%ld windows)\n",
		    p->virtual_roots_count);
#endif
	    if ( data_ret )
		XFree(data_ret);
	}
    }

    if (dirty2 & WM2DesktopLayout) {
        p->desktop_layout_orientation = OrientationHorizontal;
        p->desktop_layout_corner = DesktopLayoutCornerTopLeft;
        p->desktop_layout_columns = p->desktop_layout_rows = 0;
	if (XGetWindowProperty(p->display, p->root, net_desktop_layout,
			       0, MAX_PROP_SIZE, False, XA_CARDINAL, &type_ret,
			       &format_ret, &nitems_ret, &unused, &data_ret)
	    == Success) {
	    if (type_ret == XA_CARDINAL && format_ret == 32) {
                long* data = (long*) data_ret;
                if( nitems_ret >= 4 && data[ 3 ] >= 0 && data[ 3 ] <= 3 )
                    p->desktop_layout_corner = (NET::DesktopLayoutCorner)data[ 3 ];
                if( nitems_ret >= 3 ) {
                    if( data[ 0 ] >= 0 && data[ 0 ] <= 1 )
                        p->desktop_layout_orientation = (NET::Orientation)data[ 0 ];
                    p->desktop_layout_columns = data[ 1 ];
                    p->desktop_layout_rows = data[ 2 ];
                }
	    }

#ifdef    NETWMDEBUG
	    fprintf(stderr, "NETRootInfo::updated: desktop layout updated (%d %d %d %d)\n",
                p->desktop_layout_orientation, p->desktop_layout_columns,
                p->desktop_layout_rows, p->desktop_layout_corner );
#endif
	    if ( data_ret )
		XFree(data_ret);
	}
    }

    if (dirty2 & WM2ShowingDesktop) {
        p->showing_desktop = false;
	if (XGetWindowProperty(p->display, p->root, net_showing_desktop,
			       0, MAX_PROP_SIZE, False, XA_CARDINAL, &type_ret,
			       &format_ret, &nitems_ret, &unused, &data_ret)
	    == Success) {
	    if (type_ret == XA_CARDINAL && format_ret == 32 && nitems_ret == 1) {
		p->showing_desktop = *((long *) data_ret);
	    }

#ifdef    NETWMDEBUG
	    fprintf(stderr, "NETRootInfo::update: showing desktop = %d\n",
		    p->showing_desktop);
#endif
	    if ( data_ret )
		XFree(data_ret);
	}
    }
}


Display *NETRootInfo::x11Display() const {
    return p->display;
}


Window NETRootInfo::rootWindow() const {
    return p->root;
}


Window NETRootInfo::supportWindow() const {
    return p->supportwindow;
}


const char *NETRootInfo::wmName() const {
    return p->name; }


int NETRootInfo::screenNumber() const {
    return p->screen;
}


unsigned long NETRootInfo::supported() const {
    return role == WindowManager
        ? p->properties[ PROTOCOLS ]
        : p->client_properties[ PROTOCOLS ];
}

const unsigned long* NETRootInfo::supportedProperties() const {
    return p->properties;
}

const unsigned long* NETRootInfo::passedProperties() const {
    return role == WindowManager
        ? p->properties
        : p->client_properties;
}

bool NETRootInfo::isSupported( NET::Property property ) const {
    return p->properties[ PROTOCOLS ] & property;
}

bool NETRootInfo::isSupported( NET::Property2 property ) const {
    return p->properties[ PROTOCOLS2 ] & property;
}

bool NETRootInfo::isSupported( NET::WindowType type ) const {
    return p->properties[ WINDOW_TYPES ] & type;
}

bool NETRootInfo::isSupported( NET::State state ) const {
    return p->properties[ STATES ] & state;
}

bool NETRootInfo::isSupported( NET::Action action ) const {
    return p->properties[ ACTIONS ] & action;
}

const Window *NETRootInfo::clientList() const {
    return p->clients;
}


int NETRootInfo::clientListCount() const {
    return p->clients_count;
}


const Window *NETRootInfo::clientListStacking() const {
    return p->stacking;
}


int NETRootInfo::clientListStackingCount() const {
    return p->stacking_count;
}


const Window *NETRootInfo::kdeSystemTrayWindows() const {
    return p->kde_system_tray_windows;
}


int NETRootInfo::kdeSystemTrayWindowsCount() const {
    return p->kde_system_tray_windows_count;
}


NETSize NETRootInfo::desktopGeometry(int) const {
    return p->geometry.width != 0 ? p->geometry : p->rootSize;
}


NETPoint NETRootInfo::desktopViewport(int desktop) const {
    if (desktop < 1) {
	NETPoint pt; // set to (0,0)
	return pt;
    }

    return p->viewport[desktop - 1];
}


NETRect NETRootInfo::workArea(int desktop) const {
    if (desktop < 1) {
	NETRect rt;
	return rt;
    }

    return p->workarea[desktop - 1];
}


const char *NETRootInfo::desktopName(int desktop) const {
    if (desktop < 1) {
	return 0;
    }

    return p->desktop_names[desktop - 1];
}


const Window *NETRootInfo::virtualRoots( ) const {
    return p->virtual_roots;
}


int NETRootInfo::virtualRootsCount() const {
    return p->virtual_roots_count;
}


NET::Orientation NETRootInfo::desktopLayoutOrientation() const {
    return p->desktop_layout_orientation;
}


TQSize NETRootInfo::desktopLayoutColumnsRows() const {
    return TQSize( p->desktop_layout_columns, p->desktop_layout_rows );
}


NET::DesktopLayoutCorner NETRootInfo::desktopLayoutCorner() const {
    return p->desktop_layout_corner;
}


int NETRootInfo::numberOfDesktops() const {
    return p->number_of_desktops == 0 ? 1 : p->number_of_desktops;
}


int NETRootInfo::currentDesktop() const {
    return p->current_desktop == 0 ? 1 : p->current_desktop;
}


Window NETRootInfo::activeWindow() const {
    return p->active;
}


// NETWinInfo stuffs

const int NETWinInfo::OnAllDesktops = NET::OnAllDesktops;

NETWinInfo::NETWinInfo(Display *display, Window window, Window rootWindow,
		       const unsigned long properties[], int properties_size,
                       Role role)
{

#ifdef   NETWMDEBUG
    fprintf(stderr, "NETWinInfo::NETWinInfo: constructing object with role '%s'\n",
	    (role == WindowManager) ? "WindowManager" : "Client");
#endif

    p = new NETWinInfoPrivate;
    p->ref = 1;

    p->display = display;
    p->window = window;
    p->root = rootWindow;
    p->mapping_state = Withdrawn;
    p->mapping_state_dirty = True;
    p->state = 0;
    p->types[ 0 ] = Unknown;
    p->name = (char *) 0;
    p->visible_name = (char *) 0;
    p->icon_name = (char *) 0;
    p->visible_icon_name = (char *) 0;
    p->desktop = p->pid = p->handled_icons = 0;
    p->user_time = -1U;
    p->startup_id = NULL;
    p->transient_for = None;
    p->window_group = None;
    p->allowed_actions = 0;
    p->has_net_support = false;
    p->class_class = (char*) 0;
    p->class_name = (char*) 0;
    p->role = (char*) 0;
    p->client_machine = (char*) 0;

    // p->strut.left = p->strut.right = p->strut.top = p->strut.bottom = 0;
    // p->frame_strut.left = p->frame_strut.right = p->frame_strut.top =
    // p->frame_strut.bottom = 0;

    p->kde_system_tray_win_for = 0;

    for( int i = 0;
         i < PROPERTIES_SIZE;
         ++i )
        p->properties[ i ] = 0;
    if( properties_size > PROPERTIES_SIZE )
        properties_size = PROPERTIES_SIZE;
    for( int i = 0;
         i < properties_size;
         ++i )
        p->properties[ i ] = properties[ i ];

    p->icon_count = 0;

    this->role = role;

    if (! netwm_atoms_created) create_atoms(p->display);

    update(p->properties);
}


NETWinInfo::NETWinInfo(Display *display, Window window, Window rootWindow,
		       unsigned long properties, Role role)
{

#ifdef   NETWMDEBUG
    fprintf(stderr, "NETWinInfo::NETWinInfo: constructing object with role '%s'\n",
	    (role == WindowManager) ? "WindowManager" : "Client");
#endif

    p = new NETWinInfoPrivate;
    p->ref = 1;

    p->display = display;
    p->window = window;
    p->root = rootWindow;
    p->mapping_state = Withdrawn;
    p->mapping_state_dirty = True;
    p->state = 0;
    p->types[ 0 ] = Unknown;
    p->name = (char *) 0;
    p->visible_name = (char *) 0;
    p->icon_name = (char *) 0;
    p->visible_icon_name = (char *) 0;
    p->desktop = p->pid = p->handled_icons = 0;
    p->user_time = -1U;
    p->startup_id = NULL;
    p->transient_for = None;
    p->window_group = None;
    p->allowed_actions = 0;
    p->has_net_support = false;
    p->class_class = (char*) 0;
    p->class_name = (char*) 0;
    p->role = (char*) 0;
    p->client_machine = (char*) 0;

    // p->strut.left = p->strut.right = p->strut.top = p->strut.bottom = 0;
    // p->frame_strut.left = p->frame_strut.right = p->frame_strut.top =
    // p->frame_strut.bottom = 0;

    p->kde_system_tray_win_for = 0;

    for( int i = 0;
         i < PROPERTIES_SIZE;
         ++i )
        p->properties[ i ] = 0;
    p->properties[ PROTOCOLS ] = properties;

    p->icon_count = 0;

    this->role = role;

    if (! netwm_atoms_created) create_atoms(p->display);

    update(p->properties);
}


NETWinInfo::NETWinInfo(const NETWinInfo &wininfo) {
    p = wininfo.p;
    p->ref++;
}


NETWinInfo::~NETWinInfo() {
    refdec_nwi(p);

    if (! p->ref) delete p;
}


// assignment operator

const NETWinInfo &NETWinInfo::operator=(const NETWinInfo &wininfo) {

#ifdef   NETWMDEBUG
    fprintf(stderr, "NETWinInfo::operator=()\n");
#endif

    if (p != wininfo.p) {
	refdec_nwi(p);

	if (! p->ref) delete p;
    }

    p = wininfo.p;
    role = wininfo.role;
    p->ref++;

    return *this;
}


void NETWinInfo::setIcon(NETIcon icon, Bool replace) {
    setIconInternal( p->icons, p->icon_count, net_wm_icon, icon, replace );
}

void NETWinInfo::setIconInternal(NETRArray<NETIcon>& icons, int& icon_count, Atom property, NETIcon icon, Bool replace) {
    if (role != Client) return;

    int proplen, i, sz, j;

    if (replace) {

	for (i = 0; i < icons.size(); i++) {
	    delete [] icons[i].data;
	    icons[i].data = 0;
	    icons[i].size.width = 0;
	    icons[i].size.height = 0;
	}

	icon_count = 0;
    }

    // assign icon
    icons[icon_count] = icon;
    icon_count++;

    // do a deep copy, we want to own the data
    NETIcon &ni = icons[icon_count - 1];
    sz = ni.size.width * ni.size.height;
    CARD32 *d = new CARD32[sz];
    ni.data = (unsigned char *) d;
    memcpy(d, icon.data, sz * sizeof(CARD32));

    // compute property length
    for (i = 0, proplen = 0; i < icon_count; i++) {
	proplen += 2 + (icons[i].size.width *
			icons[i].size.height);
    }

    CARD32 *d32;
    long *prop = new long[proplen], *pprop = prop;
    for (i = 0; i < icon_count; i++) {
	// copy size into property
       	*pprop++ = icons[i].size.width;
	*pprop++ = icons[i].size.height;

	// copy data into property
	sz = (icons[i].size.width * icons[i].size.height);
	d32 = (CARD32 *) icons[i].data;
	for (j = 0; j < sz; j++) *pprop++ = *d32++;
    }

    XChangeProperty(p->display, p->window, property, XA_CARDINAL, 32,
		    PropModeReplace, (unsigned char *) prop, proplen);

    delete [] prop;
}


void NETWinInfo::setIconGeometry(NETRect geometry) {
    if (role != Client) return;

    p->icon_geom = geometry;

    if( geometry.size.width == 0 ) // empty
        XDeleteProperty(p->display, p->window, net_wm_icon_geometry);
    else {
        long data[4];
        data[0] = geometry.pos.x;
        data[1] = geometry.pos.y;
        data[2] = geometry.size.width;
        data[3] = geometry.size.height;

        XChangeProperty(p->display, p->window, net_wm_icon_geometry, XA_CARDINAL,
		    32, PropModeReplace, (unsigned char *) data, 4);
    }
}


void NETWinInfo::setExtendedStrut(const NETExtendedStrut& extended_strut ) {
    if (role != Client) return;

    p->extended_strut = extended_strut;

    long data[12];
    data[0] = extended_strut.left_width;
    data[1] = extended_strut.right_width;
    data[2] = extended_strut.top_width;
    data[3] = extended_strut.bottom_width;
    data[4] = extended_strut.left_start;
    data[5] = extended_strut.left_end;
    data[6] = extended_strut.right_start;
    data[7] = extended_strut.right_end;
    data[8] = extended_strut.top_start;
    data[9] = extended_strut.top_end;
    data[10] = extended_strut.bottom_start;
    data[11] = extended_strut.bottom_end;

    XChangeProperty(p->display, p->window, net_wm_extended_strut, XA_CARDINAL, 32,
		    PropModeReplace, (unsigned char *) data, 12);
}


void NETWinInfo::setStrut(NETStrut strut) {
    if (role != Client) return;

    p->strut = strut;

    long data[4];
    data[0] = strut.left;
    data[1] = strut.right;
    data[2] = strut.top;
    data[3] = strut.bottom;

    XChangeProperty(p->display, p->window, net_wm_strut, XA_CARDINAL, 32,
		    PropModeReplace, (unsigned char *) data, 4);
}


void NETWinInfo::setState(unsigned long state, unsigned long mask) {
    if (p->mapping_state_dirty)
	updateWMState();

    // setState() needs to know the current state, so read it even if not requested
    if( ( p->properties[ PROTOCOLS ] & WMState ) == 0 ) {
        p->properties[ PROTOCOLS ] |= WMState;
        unsigned long props[ PROPERTIES_SIZE ] = { WMState, 0 };
        assert( PROPERTIES_SIZE == 2 ); // add elements above
        update( props );
        p->properties[ PROTOCOLS ] &= ~WMState;
    }

    if (role == Client && p->mapping_state != Withdrawn) {

#ifdef NETWMDEBUG
        fprintf(stderr, "NETWinInfo::setState (0x%lx, 0x%lx) (Client)\n",
                state, mask);
#endif // NETWMDEBUG

	XEvent e;
	e.xclient.type = ClientMessage;
	e.xclient.message_type = net_wm_state;
	e.xclient.display = p->display;
	e.xclient.window = p->window;
	e.xclient.format = 32;
	e.xclient.data.l[3] = 0l;
	e.xclient.data.l[4] = 0l;

	if ((mask & Modal) && ((p->state & Modal) != (state & Modal))) {
	    e.xclient.data.l[0] = (state & Modal) ? 1 : 0;
	    e.xclient.data.l[1] = net_wm_state_modal;
	    e.xclient.data.l[2] = 0l;

	    XSendEvent(p->display, p->root, False, netwm_sendevent_mask, &e);
	}

	if ((mask & Sticky) && ((p->state & Sticky) != (state & Sticky))) {
	    e.xclient.data.l[0] = (state & Sticky) ? 1 : 0;
	    e.xclient.data.l[1] = net_wm_state_sticky;
	    e.xclient.data.l[2] = 0l;

	    XSendEvent(p->display, p->root, False, netwm_sendevent_mask, &e);
	}

	if ((mask & Max) && (( (p->state&mask) & Max) != (state & Max))) {

	    unsigned long wishstate = (p->state & ~mask) | (state & mask);
	    if ( ( (wishstate & MaxHoriz) != (p->state & MaxHoriz) )
		 && ( (wishstate & MaxVert) != (p->state & MaxVert) ) ) {
		if ( (wishstate & Max) == Max ) {
		    e.xclient.data.l[0] = 1;
		    e.xclient.data.l[1] = net_wm_state_max_horiz;
		    e.xclient.data.l[2] = net_wm_state_max_vert;
		    XSendEvent(p->display, p->root, False, netwm_sendevent_mask, &e);
		} else if ( (wishstate & Max) == 0 ) {
		    e.xclient.data.l[0] = 0;
		    e.xclient.data.l[1] = net_wm_state_max_horiz;
		    e.xclient.data.l[2] = net_wm_state_max_vert;
		    XSendEvent(p->display, p->root, False, netwm_sendevent_mask, &e);
		} else {
		    e.xclient.data.l[0] = ( wishstate & MaxHoriz ) ? 1 : 0;
		    e.xclient.data.l[1] = net_wm_state_max_horiz;
		    e.xclient.data.l[2] = 0;
		    XSendEvent(p->display, p->root, False, netwm_sendevent_mask, &e);
		    e.xclient.data.l[0] = ( wishstate & MaxVert ) ? 1 : 0;
		    e.xclient.data.l[1] = net_wm_state_max_vert;
		    e.xclient.data.l[2] = 0;
		    XSendEvent(p->display, p->root, False, netwm_sendevent_mask, &e);
		}
	    } else	if ( (wishstate & MaxVert) != (p->state & MaxVert) ) {
		e.xclient.data.l[0] = ( wishstate & MaxVert ) ? 1 : 0;
		e.xclient.data.l[1] = net_wm_state_max_vert;
		e.xclient.data.l[2] = 0;
		XSendEvent(p->display, p->root, False, netwm_sendevent_mask, &e);
	    } else if ( (wishstate & MaxHoriz) != (p->state & MaxHoriz) ) {
		e.xclient.data.l[0] = ( wishstate & MaxHoriz ) ? 1 : 0;
		e.xclient.data.l[1] = net_wm_state_max_horiz;
		e.xclient.data.l[2] = 0;
		XSendEvent(p->display, p->root, False, netwm_sendevent_mask, &e);
	    }
	}

	if ((mask & Shaded) && ((p->state & Shaded) != (state & Shaded))) {
	    e.xclient.data.l[0] = (state & Shaded) ? 1 : 0;
	    e.xclient.data.l[1] = net_wm_state_shaded;
	    e.xclient.data.l[2] = 0l;

	    XSendEvent(p->display, p->root, False, netwm_sendevent_mask, &e);
	}

	if ((mask & SkipTaskbar) &&
	    ((p->state & SkipTaskbar) != (state & SkipTaskbar))) {
	    e.xclient.data.l[0] = (state & SkipTaskbar) ? 1 : 0;
	    e.xclient.data.l[1] = net_wm_state_skip_taskbar;
	    e.xclient.data.l[2] = 0l;

	    XSendEvent(p->display, p->root, False, netwm_sendevent_mask, &e);
	}

        if ((mask & SkipPager) &&
	    ((p->state & SkipPager) != (state & SkipPager))) {
            e.xclient.data.l[0] = (state & SkipPager) ? 1 : 0;
            e.xclient.data.l[1] = net_wm_state_skip_pager;
            e.xclient.data.l[2] = 0l;

            XSendEvent(p->display, p->root, False, netwm_sendevent_mask, &e);
        }

        if ((mask & Hidden) &&
	    ((p->state & Hidden) != (state & Hidden))) {
            e.xclient.data.l[0] = (state & Hidden) ? 1 : 0;
            e.xclient.data.l[1] = net_wm_state_hidden;
            e.xclient.data.l[2] = 0l;

            XSendEvent(p->display, p->root, False, netwm_sendevent_mask, &e);
        }

        if ((mask & FullScreen) &&
	    ((p->state & FullScreen) != (state & FullScreen))) {
            e.xclient.data.l[0] = (state & FullScreen) ? 1 : 0;
            e.xclient.data.l[1] = net_wm_state_fullscreen;
            e.xclient.data.l[2] = 0l;

            XSendEvent(p->display, p->root, False, netwm_sendevent_mask, &e);
        }

        if ((mask & KeepAbove) &&
	    ((p->state & KeepAbove) != (state & KeepAbove))) {
            e.xclient.data.l[0] = (state & KeepAbove) ? 1 : 0;
            e.xclient.data.l[1] = net_wm_state_above;
            e.xclient.data.l[2] = 0l;

            XSendEvent(p->display, p->root, False, netwm_sendevent_mask, &e);
        }

        if ((mask & KeepBelow) &&
	    ((p->state & KeepBelow) != (state & KeepBelow))) {
            e.xclient.data.l[0] = (state & KeepBelow) ? 1 : 0;
            e.xclient.data.l[1] = net_wm_state_below;
            e.xclient.data.l[2] = 0l;

            XSendEvent(p->display, p->root, False, netwm_sendevent_mask, &e);
        }

	if ((mask & StaysOnTop) && ((p->state & StaysOnTop) != (state & StaysOnTop))) {
	    e.xclient.data.l[0] = (state & StaysOnTop) ? 1 : 0;
	    e.xclient.data.l[1] = net_wm_state_stays_on_top;
	    e.xclient.data.l[2] = 0l;

	    XSendEvent(p->display, p->root, False, netwm_sendevent_mask, &e);
	}

        if ((mask & DemandsAttention) &&
	    ((p->state & DemandsAttention) != (state & DemandsAttention))) {
            e.xclient.data.l[0] = (state & DemandsAttention) ? 1 : 0;
            e.xclient.data.l[1] = net_wm_state_demands_attention;
            e.xclient.data.l[2] = 0l;

            XSendEvent(p->display, p->root, False, netwm_sendevent_mask, &e);
        }

    } else {
	p->state &= ~mask;
	p->state |= state;

	long data[50];
	int count = 0;

	// hints
	if (p->state & Modal) data[count++] = net_wm_state_modal;
	if (p->state & MaxVert) data[count++] = net_wm_state_max_vert;
	if (p->state & MaxHoriz) data[count++] = net_wm_state_max_horiz;
	if (p->state & Shaded) data[count++] = net_wm_state_shaded;
	if (p->state & Hidden) data[count++] = net_wm_state_hidden;
	if (p->state & FullScreen) data[count++] = net_wm_state_fullscreen;
	if (p->state & DemandsAttention) data[count++] = net_wm_state_demands_attention;

	// policy
	if (p->state & KeepAbove) data[count++] = net_wm_state_above;
	if (p->state & KeepBelow) data[count++] = net_wm_state_below;
	if (p->state & StaysOnTop) data[count++] = net_wm_state_stays_on_top;
	if (p->state & Sticky) data[count++] = net_wm_state_sticky;
	if (p->state & SkipTaskbar) data[count++] = net_wm_state_skip_taskbar;
	if (p->state & SkipPager) data[count++] = net_wm_state_skip_pager;

#ifdef NETWMDEBUG
	fprintf(stderr, "NETWinInfo::setState: setting state property (%d)\n", count);
	for (int i = 0; i < count; i++) {
            char* data_ret = XGetAtomName(p->display, (Atom) data[i]);
	    fprintf(stderr, "NETWinInfo::setState:   state %ld '%s'\n",
		    data[i], data_ret);
            if ( data_ret )
                XFree( data_ret );
        }

#endif

	XChangeProperty(p->display, p->window, net_wm_state, XA_ATOM, 32,
			PropModeReplace, (unsigned char *) data, count);
    }
}


void NETWinInfo::setWindowType(WindowType type) {
    if (role != Client) return;

    int len;
    long data[2];

    switch (type) {
    case Override:
	// spec extension: override window type.  we must comply with the spec
	// and provide a fall back (normal seems best)
	data[0] = kde_net_wm_window_type_override;
	data[1] = net_wm_window_type_normal;
	len = 2;
	break;

    case  Dialog:
	data[0] = net_wm_window_type_dialog;
	data[1] = None;
	len = 1;
	break;

    case Menu:
	data[0] = net_wm_window_type_menu;
	data[1] = None;
	len = 1;
	break;

    case TopMenu:
	// spec extension: override window type.  we must comply with the spec
	// and provide a fall back (dock seems best)
	data[0] = kde_net_wm_window_type_topmenu;
	data[1] = net_wm_window_type_dock;
	len = 2;
	break;

    case Tool:
	data[0] = net_wm_window_type_toolbar;
	data[1] = None;
	len = 1;
	break;

    case Dock:
	data[0] = net_wm_window_type_dock;
	data[1] = None;
	len = 1;
	break;

    case Desktop:
	data[0] = net_wm_window_type_desktop;
	data[1] = None;
	len = 1;
	break;

    case Utility:
	data[0] = net_wm_window_type_utility;
	data[1] = net_wm_window_type_dialog; // fallback for old netwm version
	len = 2;
	break;

    case Splash:
	data[0] = net_wm_window_type_splash;
	data[1] = net_wm_window_type_dock; // fallback (dock seems best)
	len = 2;
	break;

    case DropdownMenu:
	data[0] = net_wm_window_type_dropdown_menu;
	data[1] = None;
	len = 1;
	break;

    case PopupMenu:
	data[0] = net_wm_window_type_popup_menu;
	data[1] = None;
	len = 1;
	break;

    case Tooltip:
	data[0] = net_wm_window_type_tooltip;
	data[1] = None;
	len = 1;
	break;

    case Notification:
	data[0] = net_wm_window_type_notification;
	data[1] = None;
	len = 1;
	break;

    case ComboBox:
	data[0] = net_wm_window_type_combobox;
	data[1] = None;
	len = 1;
	break;

    case DNDIcon:
	data[0] = net_wm_window_type_dnd;
	data[1] = None;
	len = 1;
	break;

    default:
    case Normal:
	data[0] = net_wm_window_type_normal;
	data[1] = None;
	len = 1;
	break;
    }

    XChangeProperty(p->display, p->window, net_wm_window_type, XA_ATOM, 32,
		    PropModeReplace, (unsigned char *) &data, len);
}


void NETWinInfo::setName(const char *name) {
    if (role != Client) return;

    delete [] p->name;
    p->name = nstrdup(name);
    if( p->name[ 0 ] != '\0' )
        XChangeProperty(p->display, p->window, net_wm_name, UTF8_STRING, 8,
		    PropModeReplace, (unsigned char *) p->name,
		    strlen(p->name));
    else
        XDeleteProperty(p->display, p->window, net_wm_name);
}


void NETWinInfo::setVisibleName(const char *visibleName) {
    if (role != WindowManager) return;

    delete [] p->visible_name;
    p->visible_name = nstrdup(visibleName);
    if( p->visible_name[ 0 ] != '\0' )
        XChangeProperty(p->display, p->window, net_wm_visible_name, UTF8_STRING, 8,
		    PropModeReplace, (unsigned char *) p->visible_name,
		    strlen(p->visible_name));
    else
        XDeleteProperty(p->display, p->window, net_wm_visible_name);
}


void NETWinInfo::setIconName(const char *iconName) {
    if (role != Client) return;

    delete [] p->icon_name;
    p->icon_name = nstrdup(iconName);
    if( p->icon_name[ 0 ] != '\0' )
        XChangeProperty(p->display, p->window, net_wm_icon_name, UTF8_STRING, 8,
		    PropModeReplace, (unsigned char *) p->icon_name,
		    strlen(p->icon_name));
    else
        XDeleteProperty(p->display, p->window, net_wm_icon_name);
}


void NETWinInfo::setVisibleIconName(const char *visibleIconName) {
    if (role != WindowManager) return;

    delete [] p->visible_icon_name;
    p->visible_icon_name = nstrdup(visibleIconName);
    if( p->visible_icon_name[ 0 ] != '\0' )
        XChangeProperty(p->display, p->window, net_wm_visible_icon_name, UTF8_STRING, 8,
		    PropModeReplace, (unsigned char *) p->visible_icon_name,
		    strlen(p->visible_icon_name));
    else
        XDeleteProperty(p->display, p->window, net_wm_visible_icon_name);
}


void NETWinInfo::setDesktop(int desktop) {
    if (p->mapping_state_dirty)
	updateWMState();

    if (role == Client && p->mapping_state != Withdrawn) {
	// we only send a ClientMessage if we are 1) a client and 2) managed

	if ( desktop == 0 )
	    return; // we can't do that while being managed

	XEvent e;

	e.xclient.type = ClientMessage;
	e.xclient.message_type = net_wm_desktop;
	e.xclient.display = p->display;
	e.xclient.window = p->window;
	e.xclient.format = 32;
	e.xclient.data.l[0] = desktop == OnAllDesktops ? OnAllDesktops : desktop - 1;
	e.xclient.data.l[1] = 0l;
	e.xclient.data.l[2] = 0l;
	e.xclient.data.l[3] = 0l;
	e.xclient.data.l[4] = 0l;

	XSendEvent(p->display, p->root, False, netwm_sendevent_mask, &e);
    } else {
	// otherwise we just set or remove the property directly
	p->desktop = desktop;
	long d = desktop;

	if ( d != OnAllDesktops ) {
	    if ( d == 0 ) {
		XDeleteProperty( p->display, p->window, net_wm_desktop );
		return;
	    }

	    d -= 1;
	}

	XChangeProperty(p->display, p->window, net_wm_desktop, XA_CARDINAL, 32,
			PropModeReplace, (unsigned char *) &d, 1);
    }
}


void NETWinInfo::setPid(int pid) {
    if (role != Client) return;

    p->pid = pid;
    long d = pid;
    XChangeProperty(p->display, p->window, net_wm_pid, XA_CARDINAL, 32,
		    PropModeReplace, (unsigned char *) &d, 1);
}


void NETWinInfo::setHandledIcons(Bool handled) {
    if (role != Client) return;

    p->handled_icons = handled;
    long d = handled;
    XChangeProperty(p->display, p->window, net_wm_handled_icons, XA_CARDINAL, 32,
		    PropModeReplace, (unsigned char *) &d, 1);
}

void NETWinInfo::setStartupId(const char* id) {
    if (role != Client) return;

    delete[] p->startup_id;
    p->startup_id = nstrdup(id);
    XChangeProperty(p->display, p->window, net_startup_id, UTF8_STRING, 8,
        PropModeReplace, reinterpret_cast< unsigned char* >( p->startup_id ),
        strlen( p->startup_id ));
}

void NETWinInfo::setAllowedActions( unsigned long actions ) {
    if( role != WindowManager )
        return;
    long data[50];
    int count = 0;

    p->allowed_actions = actions;
    if (p->allowed_actions & ActionMove) data[count++] = net_wm_action_move;
    if (p->allowed_actions & ActionResize) data[count++] = net_wm_action_resize;
    if (p->allowed_actions & ActionMinimize) data[count++] = net_wm_action_minimize;
    if (p->allowed_actions & ActionShade) data[count++] = net_wm_action_shade;
    if (p->allowed_actions & ActionStick) data[count++] = net_wm_action_stick;
    if (p->allowed_actions & ActionMaxVert) data[count++] = net_wm_action_max_vert;
    if (p->allowed_actions & ActionMaxHoriz) data[count++] = net_wm_action_max_horiz;
    if (p->allowed_actions & ActionFullScreen) data[count++] = net_wm_action_fullscreen;
    if (p->allowed_actions & ActionChangeDesktop) data[count++] = net_wm_action_change_desk;
    if (p->allowed_actions & ActionClose) data[count++] = net_wm_action_close;

#ifdef NETWMDEBUG
    fprintf(stderr, "NETWinInfo::setAllowedActions: setting property (%d)\n", count);
    for (int i = 0; i < count; i++) {
        char* data_ret = XGetAtomName(p->display, (Atom) data[i]);
        fprintf(stderr, "NETWinInfo::setAllowedActions:   action %ld '%s'\n",
	    data[i], data_ret);
        if ( data_ret )
            XFree(data_ret);
    }
#endif

    XChangeProperty(p->display, p->window, net_wm_allowed_actions, XA_ATOM, 32,
		    PropModeReplace, (unsigned char *) data, count);
}

void NETWinInfo::setKDESystemTrayWinFor(Window window) {
    if (role != Client) return;

    p->kde_system_tray_win_for = window;
    XChangeProperty(p->display, p->window, kde_net_wm_system_tray_window_for,
		    XA_WINDOW, 32, PropModeReplace,
		    (unsigned char *) &(p->kde_system_tray_win_for), 1);
}


void NETWinInfo::setKDEFrameStrut(NETStrut strut) {
    setFrameExtents( strut );
}

void NETWinInfo::setFrameExtents(NETStrut strut) {
    if (role != WindowManager) return;

    p->frame_strut = strut;

    long d[4];
    d[0] = strut.left;
    d[1] = strut.right;
    d[2] = strut.top;
    d[3] = strut.bottom;

    XChangeProperty(p->display, p->window, net_frame_extents, XA_CARDINAL, 32,
		    PropModeReplace, (unsigned char *) d, 4);
    XChangeProperty(p->display, p->window, kde_net_wm_frame_strut, XA_CARDINAL, 32,
		    PropModeReplace, (unsigned char *) d, 4);
}


void NETWinInfo::kdeGeometry(NETRect& frame, NETRect& window) {
    if (p->win_geom.size.width == 0 || p->win_geom.size.height == 0) {
	Window unused;
	int x, y;
	unsigned int w, h, junk;
	XGetGeometry(p->display, p->window, &unused, &x, &y, &w, &h, &junk, &junk);
	XTranslateCoordinates(p->display, p->window, p->root, 0, 0, &x, &y, &unused
			      );

	p->win_geom.pos.x = x;
	p->win_geom.pos.y = y;

	p->win_geom.size.width = w;
	p->win_geom.size.height = h;
    }
// TODO try to work also without _KDE_NET_WM_FRAME_STRUT
    window = p->win_geom;

    frame.pos.x = window.pos.x - p->frame_strut.left;
    frame.pos.y = window.pos.y - p->frame_strut.top;
    frame.size.width = window.size.width + p->frame_strut.left + p->frame_strut.right;
    frame.size.height = window.size.height + p->frame_strut.top + p->frame_strut.bottom;
}


NETIcon NETWinInfo::icon(int width, int height) const {
    return iconInternal( p->icons, p->icon_count, width, height );
}

NETIcon NETWinInfo::iconInternal(NETRArray<NETIcon>& icons, int icon_count, int width, int height) const {
    NETIcon result;

    if ( !icon_count ) {
	result.size.width = 0;
	result.size.height = 0;
	result.data = 0;
	return result;
    }

    // find the largest icon
    result = icons[0];
    for (int i = 1; i < icons.size(); i++) {
	if( icons[i].size.width >= result.size.width &&
	     icons[i].size.height >= result.size.height )
	    result = icons[i];
    }

    // return the largest icon if w and h are -1
    if (width == -1 && height == -1) return result;

    // find the icon that's closest in size to w x h...
    for (int i = 0; i < icons.size(); i++) {
	if ((icons[i].size.width >= width &&
	     icons[i].size.width < result.size.width) &&
	    (icons[i].size.height >= height &&
	     icons[i].size.height < result.size.height))
	    result = icons[i];
    }

    return result;
}

void NETWinInfo::setUserTime( Time time ) {
    if (role != Client) return;

    p->user_time = time;
    long d = time;
    XChangeProperty(p->display, p->window, net_wm_user_time, XA_CARDINAL, 32,
		    PropModeReplace, (unsigned char *) &d, 1);
}


unsigned long NETWinInfo::event(XEvent *ev )
{
    unsigned long props[ 1 ];
    event( ev, props, 1 );
    return props[ 0 ];
}

void NETWinInfo::event(XEvent *event, unsigned long* properties, int properties_size ) {
    unsigned long props[ PROPERTIES_SIZE ] = { 0, 0 };
    assert( PROPERTIES_SIZE == 2 ); // add elements above
    unsigned long& dirty = props[ PROTOCOLS ];
    unsigned long& dirty2 = props[ PROTOCOLS2 ];
    bool do_update = false;

    if (role == WindowManager && event->type == ClientMessage &&
	event->xclient.format == 32) {

#ifdef NETWMDEBUG
        fprintf(stderr, "NETWinInfo::event: handling ClientMessage event\n");
#endif // NETWMDEBUG

	if (event->xclient.message_type == net_wm_state) {
	    dirty = WMState;

	    // we need to generate a change mask

#ifdef NETWMDEBUG
	    fprintf(stderr,
		    "NETWinInfo::event: state client message, getting new state/mask\n");
#endif

	    int i;
	    long state = 0, mask = 0;

	    for (i = 1; i < 3; i++) {
#ifdef NETWMDEBUG
                char* debug_txt = XGetAtomName(p->display, (Atom) event->xclient.data.l[i]);
		fprintf(stderr, "NETWinInfo::event:  message %ld '%s'\n",
			event->xclient.data.l[i], debug_txt );
                if ( debug_txt )
                    XFree( debug_txt );
#endif

		if ((Atom) event->xclient.data.l[i] == net_wm_state_modal)
		    mask |= Modal;
		else if ((Atom) event->xclient.data.l[i] == net_wm_state_sticky)
		    mask |= Sticky;
		else if ((Atom) event->xclient.data.l[i] == net_wm_state_max_vert)
		    mask |= MaxVert;
		else if ((Atom) event->xclient.data.l[i] == net_wm_state_max_horiz)
		    mask |= MaxHoriz;
		else if ((Atom) event->xclient.data.l[i] == net_wm_state_shaded)
		    mask |= Shaded;
		else if ((Atom) event->xclient.data.l[i] == net_wm_state_skip_taskbar)
		    mask |= SkipTaskbar;
                else if ((Atom) event->xclient.data.l[i] == net_wm_state_skip_pager)
		    mask |= SkipPager;
                else if ((Atom) event->xclient.data.l[i] == net_wm_state_hidden)
		    mask |= Hidden;
                else if ((Atom) event->xclient.data.l[i] == net_wm_state_fullscreen)
		    mask |= FullScreen;
                else if ((Atom) event->xclient.data.l[i] == net_wm_state_above)
		    mask |= KeepAbove;
                else if ((Atom) event->xclient.data.l[i] == net_wm_state_below)
		    mask |= KeepBelow;
                else if ((Atom) event->xclient.data.l[i] == net_wm_state_demands_attention)
		    mask |= DemandsAttention;
		else if ((Atom) event->xclient.data.l[i] == net_wm_state_stays_on_top)
		    mask |= StaysOnTop;
	    }

	    // when removing, we just leave newstate == 0
	    switch (event->xclient.data.l[0]) {
	    case 1: // set
		// to set... the change state should be the same as the mask
		state = mask;
		break;

	    case 2: // toggle
		// to toggle, we need to xor the current state with the new state
		state = (p->state & mask) ^ mask;
		break;

	    default:
		// to clear state, the new state should stay zero
		;
	    }

#ifdef NETWMDEBUG
	    fprintf(stderr, "NETWinInfo::event: calling changeState(%lx, %lx)\n",
		    state, mask);
#endif

	    changeState(state, mask);
	} else if (event->xclient.message_type == net_wm_desktop) {
	    dirty = WMDesktop;

	    if( event->xclient.data.l[0] == OnAllDesktops )
		changeDesktop( OnAllDesktops );
	    else
    		changeDesktop(event->xclient.data.l[0] + 1);
	}
    }

    if (event->type == PropertyNotify) {

#ifdef    NETWMDEBUG
	fprintf(stderr, "NETWinInfo::event: handling PropertyNotify event\n");
#endif

	XEvent pe = *event;

	Bool done = False;
	Bool compaction = False;
	while (! done) {

#ifdef    NETWMDEBUG
	    fprintf(stderr, "NETWinInfo::event: loop fire\n");
#endif

	    if (pe.xproperty.atom == net_wm_name)
		dirty |= WMName;
	    else if (pe.xproperty.atom == net_wm_visible_name)
		dirty |= WMVisibleName;
	    else if (pe.xproperty.atom == net_wm_desktop)
		dirty |= WMDesktop;
	    else if (pe.xproperty.atom == net_wm_window_type)
		dirty |=WMWindowType;
	    else if (pe.xproperty.atom == net_wm_state)
		dirty |= WMState;
	    else if (pe.xproperty.atom == net_wm_strut)
		dirty |= WMStrut;
	    else if (pe.xproperty.atom == net_wm_extended_strut)
		dirty2 |= WM2ExtendedStrut;
	    else if (pe.xproperty.atom == net_wm_icon_geometry)
		dirty |= WMIconGeometry;
	    else if (pe.xproperty.atom == net_wm_icon)
		dirty |= WMIcon;
	    else if (pe.xproperty.atom == net_wm_pid)
		dirty |= WMPid;
	    else if (pe.xproperty.atom == net_wm_handled_icons)
		dirty |= WMHandledIcons;
	    else if (pe.xproperty.atom == net_startup_id)
		dirty2 |= WM2StartupId;
	    else if (pe.xproperty.atom == net_wm_allowed_actions)
		dirty2 |= WM2AllowedActions;
	    else if (pe.xproperty.atom == kde_net_wm_system_tray_window_for)
		dirty |= WMKDESystemTrayWinFor;
	    else if (pe.xproperty.atom == xa_wm_state)
		dirty |= XAWMState;
	    else if (pe.xproperty.atom == net_frame_extents)
		dirty |= WMFrameExtents;
	    else if (pe.xproperty.atom == kde_net_wm_frame_strut)
		dirty |= WMKDEFrameStrut;
	    else if (pe.xproperty.atom == net_wm_icon_name)
		dirty |= WMIconName;
	    else if (pe.xproperty.atom == net_wm_visible_icon_name)
		dirty |= WMVisibleIconName;
	    else if (pe.xproperty.atom == net_wm_user_time)
		dirty2 |= WM2UserTime;
            else if (pe.xproperty.atom == XA_WM_HINTS)
                dirty2 |= WM2GroupLeader;
            else if (pe.xproperty.atom == XA_WM_TRANSIENT_FOR)
                dirty2 |= WM2TransientFor;
            else if (pe.xproperty.atom == XA_WM_CLASS)
                dirty2 |= WM2WindowClass;
            else if (pe.xproperty.atom == wm_window_role)
                dirty2 |= WM2WindowRole;
            else if (pe.xproperty.atom == XA_WM_CLIENT_MACHINE)
                dirty2 |= WM2ClientMachine;
	    else {

#ifdef    NETWMDEBUG
		fprintf(stderr, "NETWinInfo::event: putting back event and breaking\n");
#endif

		if ( compaction )
		    XPutBackEvent(p->display, &pe);
		break;
	    }

	    if (XCheckTypedWindowEvent(p->display, p->window, PropertyNotify, &pe) )
		compaction = True;
	    else
		break;
	}

	do_update = true;
    } else if (event->type == ConfigureNotify) {

#ifdef NETWMDEBUG
	fprintf(stderr, "NETWinInfo::event: handling ConfigureNotify event\n");
#endif

	dirty |= WMGeometry;

	// update window geometry
	p->win_geom.pos.x = event->xconfigure.x;
	p->win_geom.pos.y = event->xconfigure.y;
	p->win_geom.size.width = event->xconfigure.width;
	p->win_geom.size.height = event->xconfigure.height;
    }

    if( do_update )
        update( props );

    if( properties_size > PROPERTIES_SIZE )
        properties_size = PROPERTIES_SIZE;
    for( int i = 0;
         i < properties_size;
         ++i )
        properties[ i ] = props[ i ];
}

void NETWinInfo::updateWMState() {
    unsigned long props[ PROPERTIES_SIZE ] = { XAWMState, 0 };
    assert( PROPERTIES_SIZE == 2 ); // add elements above
    update( props );
}

void NETWinInfo::update(const unsigned long dirty_props[]) {
    Atom type_ret;
    int format_ret;
    unsigned long nitems_ret, unused;
    unsigned char *data_ret;
    unsigned long props[ PROPERTIES_SIZE ];
    for( int i = 0;
         i < PROPERTIES_SIZE;
         ++i )
        props[ i ] = dirty_props[ i ] & p->properties[ i ];
    const unsigned long& dirty = props[ PROTOCOLS ];
    const unsigned long& dirty2 = props[ PROTOCOLS2 ];

    // we *always* want to update WM_STATE if set in dirty_props
    if( dirty_props[ PROTOCOLS ] & XAWMState )
        props[ PROTOCOLS ] |= XAWMState;

    if (dirty & XAWMState) {
        p->mapping_state = Withdrawn;
	if (XGetWindowProperty(p->display, p->window, xa_wm_state, 0l, 1l,
			       False, xa_wm_state, &type_ret, &format_ret,
			       &nitems_ret, &unused, &data_ret)
	    == Success) {
	    if (type_ret == xa_wm_state && format_ret == 32 &&
		nitems_ret == 1) {
		long *state = (long *) data_ret;

		switch(*state) {
		    case IconicState:
			p->mapping_state = Iconic;
			break;
		    case NormalState:
			p->mapping_state = Visible;
                        break;
		    case WithdrawnState:
		    default:
			p->mapping_state = Withdrawn;
			break;
		}

		p->mapping_state_dirty = False;
	    }
	    if ( data_ret )
		XFree(data_ret);
	}
    }

    if (dirty & WMState) {
	p->state = 0;
	if (XGetWindowProperty(p->display, p->window, net_wm_state, 0l, 2048l,
			       False, XA_ATOM, &type_ret, &format_ret,
			       &nitems_ret, &unused, &data_ret)
	    == Success) {
	    if (type_ret == XA_ATOM && format_ret == 32 && nitems_ret > 0) {
		// determine window state
#ifdef NETWMDEBUG
		fprintf(stderr, "NETWinInfo::update: updating window state (%ld)\n",
			nitems_ret);
#endif

		long *states = (long *) data_ret;
		unsigned long count;

		for (count = 0; count < nitems_ret; count++) {
#ifdef NETWMDEBUG
                    char* data_ret = XGetAtomName(p->display, (Atom) states[count]);
		    fprintf(stderr,
			    "NETWinInfo::update:   adding window state %ld '%s'\n",
			    states[count], data_ret );
                    if ( data_ret )
                        XFree( data_ret );
#endif

		    if ((Atom) states[count] == net_wm_state_modal)
			p->state |= Modal;
		    else if ((Atom) states[count] == net_wm_state_sticky)
			p->state |= Sticky;
		    else if ((Atom) states[count] == net_wm_state_max_vert)
			p->state |= MaxVert;
		    else if ((Atom) states[count] == net_wm_state_max_horiz)
			p->state |= MaxHoriz;
		    else if ((Atom) states[count] == net_wm_state_shaded)
			p->state |= Shaded;
		    else if ((Atom) states[count] == net_wm_state_skip_taskbar)
			p->state |= SkipTaskbar;
		    else if ((Atom) states[count] == net_wm_state_skip_pager)
			p->state |= SkipPager;
		    else if ((Atom) states[count] == net_wm_state_hidden)
			p->state |= Hidden;
		    else if ((Atom) states[count] == net_wm_state_fullscreen)
			p->state |= FullScreen;
		    else if ((Atom) states[count] == net_wm_state_above)
			p->state |= KeepAbove;
		    else if ((Atom) states[count] == net_wm_state_below)
			p->state |= KeepBelow;
		    else if ((Atom) states[count] == net_wm_state_demands_attention)
			p->state |= DemandsAttention;
		    else if ((Atom) states[count] == net_wm_state_stays_on_top)
			p->state |= StaysOnTop;
		}
	    }
	    if ( data_ret )
		XFree(data_ret);
	}
    }

    if (dirty & WMDesktop) {
	p->desktop = 0;
	if (XGetWindowProperty(p->display, p->window, net_wm_desktop, 0l, 1l,
			       False, XA_CARDINAL, &type_ret,
			       &format_ret, &nitems_ret,
			       &unused, &data_ret)
	    == Success) {
	    if (type_ret == XA_CARDINAL && format_ret == 32 &&
		nitems_ret == 1) {
		p->desktop = *((long *) data_ret);
		if ((signed) p->desktop != OnAllDesktops)
		    p->desktop++;

		if ( p->desktop == 0 )
		    p->desktop = OnAllDesktops;
	    }
	    if ( data_ret )
		XFree(data_ret);
	}
    }

    if (dirty & WMName) {
        delete[] p->name;
        p->name = NULL;
	if (XGetWindowProperty(p->display, p->window, net_wm_name, 0l,
			       MAX_PROP_SIZE, False, UTF8_STRING, &type_ret,
			       &format_ret, &nitems_ret, &unused, &data_ret)
	    == Success) {
	    if (type_ret == UTF8_STRING && format_ret == 8 && nitems_ret > 0) {
		p->name = nstrndup((const char *) data_ret, nitems_ret);
	    }

	    if( data_ret )
		XFree(data_ret);
	}
    }

    if (dirty & WMVisibleName) {
        delete[] p->visible_name;
        p->visible_name = NULL;
	if (XGetWindowProperty(p->display, p->window, net_wm_visible_name, 0l,
			       MAX_PROP_SIZE, False, UTF8_STRING, &type_ret,
			       &format_ret, &nitems_ret, &unused, &data_ret)
	    == Success) {
	    if (type_ret == UTF8_STRING && format_ret == 8 && nitems_ret > 0) {
		p->visible_name = nstrndup((const char *) data_ret, nitems_ret);
	    }

	    if( data_ret )
		XFree(data_ret);
	}
    }

    if (dirty & WMIconName) {
        delete[] p->icon_name;
        p->icon_name = NULL;
	if (XGetWindowProperty(p->display, p->window, net_wm_icon_name, 0l,
			       MAX_PROP_SIZE, False, UTF8_STRING, &type_ret,
			       &format_ret, &nitems_ret, &unused, &data_ret)
	    == Success) {
	    if (type_ret == UTF8_STRING && format_ret == 8 && nitems_ret > 0) {
		p->icon_name = nstrndup((const char *) data_ret, nitems_ret);
	    }

	    if( data_ret )
		XFree(data_ret);
	}
    }

    if (dirty & WMVisibleIconName)
    {
        delete[] p->visible_icon_name;
        p->visible_icon_name = NULL;
	if (XGetWindowProperty(p->display, p->window, net_wm_visible_icon_name, 0l,
			       MAX_PROP_SIZE, False, UTF8_STRING, &type_ret,
			       &format_ret, &nitems_ret, &unused, &data_ret)
	    == Success) {
	    if (type_ret == UTF8_STRING && format_ret == 8 && nitems_ret > 0) {
		p->visible_icon_name = nstrndup((const char *) data_ret, nitems_ret);
	    }

	    if( data_ret )
		XFree(data_ret);
	}
    }

    if (dirty & WMWindowType) {
	p->types.reset();
	p->types[ 0 ] = Unknown;
        p->has_net_support = false;
	if (XGetWindowProperty(p->display, p->window, net_wm_window_type, 0l, 2048l,
			       False, XA_ATOM, &type_ret, &format_ret,
			       &nitems_ret, &unused, &data_ret)
	    == Success) {
	    if (type_ret == XA_ATOM && format_ret == 32 && nitems_ret > 0) {
		// determine the window type
#ifdef NETWMDEBUG
		fprintf(stderr, "NETWinInfo::update: getting window type (%ld)\n",
			nitems_ret);
#endif

                p->has_net_support = true;

		unsigned long count = 0;
		long *types = (long *) data_ret;
		int pos = 0;

		while (count < nitems_ret) {
		    // remember all window types we know
#ifdef NETWMDEBUG
                    char* debug_type = XGetAtomName(p->display, (Atom) types[count]);
		    fprintf(stderr,
			    "NETWinInfo::update:   examining window type %ld %s\n",
			    types[count], debug_type );
                    if ( debug_type )
                        XFree( debug_type );
#endif

		    if ((Atom) types[count] == net_wm_window_type_normal)
			p->types[ pos++ ] = Normal;
		    else if ((Atom) types[count] == net_wm_window_type_desktop)
			p->types[ pos++ ] = Desktop;
		    else if ((Atom) types[count] == net_wm_window_type_dock)
			p->types[ pos++ ] = Dock;
		    else if ((Atom) types[count] == net_wm_window_type_toolbar)
			p->types[ pos++ ] = Tool;
		    else if ((Atom) types[count] == net_wm_window_type_menu)
			p->types[ pos++ ] = Menu;
		    else if ((Atom) types[count] == net_wm_window_type_dialog)
			p->types[ pos++ ] = Dialog;
		    else if ((Atom) types[count] == net_wm_window_type_utility)
			p->types[ pos++ ] = Utility;
		    else if ((Atom) types[count] == net_wm_window_type_splash)
			p->types[ pos++ ] = Splash;
		    else if ((Atom) types[count] == net_wm_window_type_dropdown_menu)
			p->types[ pos++ ] = DropdownMenu;
		    else if ((Atom) types[count] == net_wm_window_type_popup_menu)
			p->types[ pos++ ] = PopupMenu;
		    else if ((Atom) types[count] == net_wm_window_type_tooltip)
			p->types[ pos++ ] = Tooltip;
		    else if ((Atom) types[count] == net_wm_window_type_notification)
			p->types[ pos++ ] = Notification;
		    else if ((Atom) types[count] == net_wm_window_type_combobox)
			p->types[ pos++ ] = ComboBox;
		    else if ((Atom) types[count] == net_wm_window_type_dnd)
			p->types[ pos++ ] = DNDIcon;
		    else if ((Atom) types[count] == kde_net_wm_window_type_override)
			p->types[ pos++ ] = Override;
		    else if ((Atom) types[count] == kde_net_wm_window_type_topmenu)
			p->types[ pos++ ] = TopMenu;

		    count++;
		}
	    }

	    if ( data_ret )
		XFree(data_ret);
	}
    }

    if (dirty & WMStrut) {
        p->strut = NETStrut();
	if (XGetWindowProperty(p->display, p->window, net_wm_strut, 0l, 4l,
			       False, XA_CARDINAL, &type_ret, &format_ret,
			       &nitems_ret, &unused, &data_ret)
	    == Success) {
	    if (type_ret == XA_CARDINAL && format_ret == 32 &&
		nitems_ret == 4) {
		long *d = (long *) data_ret;
		p->strut.left   = d[0];
		p->strut.right  = d[1];
		p->strut.top    = d[2];
		p->strut.bottom = d[3];
	    }
	    if ( data_ret )
		XFree(data_ret);
	}
    }

    if (dirty2 & WM2ExtendedStrut) {
        p->extended_strut = NETExtendedStrut();
	if (XGetWindowProperty(p->display, p->window, net_wm_extended_strut, 0l, 12l,
			       False, XA_CARDINAL, &type_ret, &format_ret,
			       &nitems_ret, &unused, &data_ret)
	    == Success) {
	    if (type_ret == XA_CARDINAL && format_ret == 32 &&
		nitems_ret == 12) {
		long *d = (long *) data_ret;
		p->extended_strut.left_width = d[0];
		p->extended_strut.right_width = d[1];
		p->extended_strut.top_width = d[2];
		p->extended_strut.bottom_width = d[3];
                p->extended_strut.left_start = d[4];
                p->extended_strut.left_end = d[5];
                p->extended_strut.right_start = d[6];
                p->extended_strut.right_end = d[7];
                p->extended_strut.top_start = d[8];
                p->extended_strut.top_end = d[9];
                p->extended_strut.bottom_start = d[10];
                p->extended_strut.bottom_end = d[11];
	    }
	    if ( data_ret )
		XFree(data_ret);
	}
    }

    if (dirty & WMIconGeometry) {
        p->icon_geom = NETRect();
	if (XGetWindowProperty(p->display, p->window, net_wm_icon_geometry, 0l, 4l,
			       False, XA_CARDINAL, &type_ret, &format_ret,
			       &nitems_ret, &unused, &data_ret)
	    == Success) {
	    if (type_ret == XA_CARDINAL && format_ret == 32 &&
		nitems_ret == 4) {
		long *d = (long *) data_ret;
		p->icon_geom.pos.x       = d[0];
		p->icon_geom.pos.y       = d[1];
		p->icon_geom.size.width  = d[2];
		p->icon_geom.size.height = d[3];
	    }
	    if ( data_ret )
		XFree(data_ret);
	}
    }

    if (dirty & WMIcon) {
	readIcon(p->display,p->window,net_wm_icon,p->icons,p->icon_count);
    }

    if (dirty & WMKDESystemTrayWinFor) {
	p->kde_system_tray_win_for = 0;
	if (XGetWindowProperty(p->display, p->window, kde_net_wm_system_tray_window_for,
			       0l, 1l, False, XA_WINDOW, &type_ret, &format_ret,
			       &nitems_ret, &unused, &data_ret)
	    == Success) {
	    if (type_ret == XA_WINDOW && format_ret == 32 &&
		nitems_ret == 1) {
		p->kde_system_tray_win_for = *((Window *) data_ret);
		if ( p->kde_system_tray_win_for == 0 )
		    p->kde_system_tray_win_for = p->root;
	    }
	    if ( data_ret )
		XFree(data_ret);
        }
    }

    if (dirty & WMFrameExtents) {
        p->frame_strut = NETStrut();
        bool ok = false;
	if (XGetWindowProperty(p->display, p->window, net_frame_extents,
			       0l, 4l, False, XA_CARDINAL, &type_ret, &format_ret,
			       &nitems_ret, &unused, &data_ret) == Success) {
	    if (type_ret == XA_CARDINAL && format_ret == 32 && nitems_ret == 4) {
                ok = true;
		long *d = (long *) data_ret;

		p->frame_strut.left   = d[0];
		p->frame_strut.right  = d[1];
		p->frame_strut.top    = d[2];
		p->frame_strut.bottom = d[3];
	    }
	    if ( data_ret )
		XFree(data_ret);
        }
	if (!ok && XGetWindowProperty(p->display, p->window, kde_net_wm_frame_strut,
			       0l, 4l, False, XA_CARDINAL, &type_ret, &format_ret,
			       &nitems_ret, &unused, &data_ret) == Success) {
	    if (type_ret == XA_CARDINAL && format_ret == 32 && nitems_ret == 4) {
                ok = true;
		long *d = (long *) data_ret;

		p->frame_strut.left   = d[0];
		p->frame_strut.right  = d[1];
		p->frame_strut.top    = d[2];
		p->frame_strut.bottom = d[3];
	    }
	    if ( data_ret )
		XFree(data_ret);
	}
    }

    if (dirty & WMPid) {
	p->pid = 0;
	if (XGetWindowProperty(p->display, p->window, net_wm_pid, 0l, 1l,
			       False, XA_CARDINAL, &type_ret, &format_ret,
			       &nitems_ret, &unused, &data_ret) == Success) {
	    if (type_ret == XA_CARDINAL && format_ret == 32 && nitems_ret == 1) {
		p->pid = *((long *) data_ret);
	    }
	    if ( data_ret )
		XFree(data_ret);
	}
    }

    if (dirty2 & WM2StartupId)
    {
        delete[] p->startup_id;
        p->startup_id = NULL;
	if (XGetWindowProperty(p->display, p->window, net_startup_id, 0l,
			       MAX_PROP_SIZE, False, UTF8_STRING, &type_ret,
			       &format_ret, &nitems_ret, &unused, &data_ret)
	    == Success) {
	    if (type_ret == UTF8_STRING && format_ret == 8 && nitems_ret > 0) {
		p->startup_id = nstrndup((const char *) data_ret, nitems_ret);
	    }

	    if( data_ret )
		XFree(data_ret);
	}
    }

    if( dirty2 & WM2AllowedActions ) {
        p->allowed_actions = 0;
	if (XGetWindowProperty(p->display, p->window, net_wm_allowed_actions, 0l, 2048l,
			       False, XA_ATOM, &type_ret, &format_ret,
			       &nitems_ret, &unused, &data_ret)
	    == Success) {
	    if (type_ret == XA_ATOM && format_ret == 32 && nitems_ret > 0) {
		// determine actions
#ifdef NETWMDEBUG
		fprintf(stderr, "NETWinInfo::update: updating allowed actions (%ld)\n",
			nitems_ret);
#endif

		long *actions = (long *) data_ret;
		unsigned long count;

		for (count = 0; count < nitems_ret; count++) {
#ifdef NETWMDEBUG
		    fprintf(stderr,
			    "NETWinInfo::update:   adding allowed action %ld '%s'\n",
			    actions[count],
			    XGetAtomName(p->display, (Atom) actions[count]));
#endif

		    if ((Atom) actions[count] == net_wm_action_move)
			p->allowed_actions |= ActionMove;
		    if ((Atom) actions[count] == net_wm_action_resize)
			p->allowed_actions |= ActionResize;
		    if ((Atom) actions[count] == net_wm_action_minimize)
			p->allowed_actions |= ActionMinimize;
		    if ((Atom) actions[count] == net_wm_action_shade)
			p->allowed_actions |= ActionShade;
		    if ((Atom) actions[count] == net_wm_action_stick)
			p->allowed_actions |= ActionStick;
		    if ((Atom) actions[count] == net_wm_action_max_vert)
			p->allowed_actions |= ActionMaxVert;
		    if ((Atom) actions[count] == net_wm_action_max_horiz)
			p->allowed_actions |= ActionMaxHoriz;
		    if ((Atom) actions[count] == net_wm_action_fullscreen)
			p->allowed_actions |= ActionFullScreen;
		    if ((Atom) actions[count] == net_wm_action_change_desk)
			p->allowed_actions |= ActionChangeDesktop;
		    if ((Atom) actions[count] == net_wm_action_close)
			p->allowed_actions |= ActionClose;
		}
	    }
	    if ( data_ret )
		XFree(data_ret);
	}
    }

    if (dirty2 & WM2UserTime) {
	p->user_time = -1U;
	if (XGetWindowProperty(p->display, p->window, net_wm_user_time, 0l, 1l,
			       False, XA_CARDINAL, &type_ret, &format_ret,
			       &nitems_ret, &unused, &data_ret) == Success) {
            // don't do nitems_ret check - Qt does PropModeAppend to avoid API call for it
	    if (type_ret == XA_CARDINAL && format_ret == 32 /*&& nitems_ret == 1*/) {
		p->user_time = *((long *) data_ret);
	    }
	    if ( data_ret )
		XFree(data_ret);
	}
    }

    if (dirty2 & WM2TransientFor) {
	p->transient_for = None;
        XGetTransientForHint(p->display, p->window, &p->transient_for);
    }

    if (dirty2 & WM2GroupLeader) {
        XWMHints *hints = XGetWMHints(p->display, p->window);
        p->window_group = None;
        if ( hints )
        {
            if( hints->flags & WindowGroupHint )
                p->window_group = hints->window_group;
            XFree( reinterpret_cast< char* >( hints ));
        }
    }
    
    if( dirty2 & WM2WindowClass ) {
        delete[] p->class_class;
        delete[] p->class_name;
        p->class_class = NULL;
        p->class_name = NULL;
        XClassHint hint;
        if( XGetClassHint( p->display, p->window, &hint )) {
            p->class_class = strdup( hint.res_class );
            p->class_name = strdup( hint.res_name );
            XFree( hint.res_class );
            XFree( hint.res_name );
        }
    }

    if( dirty2 & WM2WindowRole ) {
        delete[] p->role;
        p->role = NULL;
	if (XGetWindowProperty(p->display, p->window, wm_window_role, 0l,
			       MAX_PROP_SIZE, False, XA_STRING, &type_ret,
			       &format_ret, &nitems_ret, &unused, &data_ret)
	    == Success) {
	    if (type_ret == XA_STRING && format_ret == 8 && nitems_ret > 0) {
		p->role = nstrndup((const char *) data_ret, nitems_ret);
	    }
	    if( data_ret )
		XFree(data_ret);
	}
    }

    if( dirty2 & WM2ClientMachine ) {
        delete[] p->client_machine;
        p->client_machine = NULL;
	if (XGetWindowProperty(p->display, p->window, XA_WM_CLIENT_MACHINE, 0l,
			       MAX_PROP_SIZE, False, XA_STRING, &type_ret,
			       &format_ret, &nitems_ret, &unused, &data_ret)
	    == Success) {
	    if (type_ret == XA_STRING && format_ret == 8 && nitems_ret > 0) {
		p->client_machine = nstrndup((const char *) data_ret, nitems_ret);
	    }
	    if( data_ret )
		XFree(data_ret);
	}
    }
}


NETRect NETWinInfo::iconGeometry() const {
    return p->icon_geom;
}


unsigned long NETWinInfo::state() const {
    return p->state;
}


NETStrut NETWinInfo::strut() const {
    return p->strut;
}

NETExtendedStrut NETWinInfo::extendedStrut() const {
    return p->extended_strut;
}

bool NET::typeMatchesMask( WindowType type, unsigned long mask ) {
    switch( type ) {
#define CHECK_TYPE_MASK( type ) \
        case type: \
    	    if( mask & type##Mask ) \
	        return true; \
	    break;
        CHECK_TYPE_MASK( Normal )
        CHECK_TYPE_MASK( Desktop )
        CHECK_TYPE_MASK( Dock )
        CHECK_TYPE_MASK( Toolbar )
        CHECK_TYPE_MASK( Menu )
        CHECK_TYPE_MASK( Dialog )
        CHECK_TYPE_MASK( Override )
        CHECK_TYPE_MASK( TopMenu )
        CHECK_TYPE_MASK( Utility )
        CHECK_TYPE_MASK( Splash )
        CHECK_TYPE_MASK( DropdownMenu )
        CHECK_TYPE_MASK( PopupMenu )
        CHECK_TYPE_MASK( Tooltip )
        CHECK_TYPE_MASK( Notification )
        CHECK_TYPE_MASK( ComboBox )
        CHECK_TYPE_MASK( DNDIcon )
#undef CHECK_TYPE_MASK
        default:
            break;
    }
    return false;
}

NET::WindowType NETWinInfo::windowType( unsigned long supported_types ) const {
    for( int i = 0;
	 i < p->types.size();
	 ++i ) {
	// return the type only if the application supports it
        if( typeMatchesMask( p->types[ i ], supported_types ))
            return p->types[ i ];
	}
    return Unknown;
}

NET::WindowType NETWinInfo::windowType() const {
    return p->types[ 0 ];
}


const char *NETWinInfo::name() const {
    return p->name;
}


const char *NETWinInfo::visibleName() const {
    return p->visible_name;
}


const char *NETWinInfo::iconName() const {
    return p->icon_name;
}


const char *NETWinInfo::visibleIconName() const {
    return p->visible_icon_name;
}


int NETWinInfo::desktop() const {
    return p->desktop;
}

int NETWinInfo::pid() const {
    return p->pid;
}

Time NETWinInfo::userTime() const {
    return p->user_time;
}

const char* NETWinInfo::startupId() const {
    return p->startup_id;
}

unsigned long NETWinInfo::allowedActions() const {
    return p->allowed_actions;
}

bool NETWinInfo::hasNETSupport() const {
    return p->has_net_support;
}

Window NETWinInfo::transientFor() const {
    return p->transient_for;
}

Window NETWinInfo::groupLeader() const {
    return p->window_group;
}

const char* NETWinInfo::windowClassClass() const {
    return p->class_class;
}

const char* NETWinInfo::windowClassName() const {
    return p->class_name;
}

const char* NETWinInfo::windowRole() const {
    return p->role;
}

const char* NETWinInfo::clientMachine() const {
    return p->client_machine;
}

Bool NETWinInfo::handledIcons() const {
    return p->handled_icons;
}


Window NETWinInfo::kdeSystemTrayWinFor() const {
    return p->kde_system_tray_win_for;
}

const unsigned long* NETWinInfo::passedProperties() const {
    return p->properties;
}

unsigned long NETWinInfo::properties() const {
    return p->properties[ PROTOCOLS ];
}


NET::MappingState NETWinInfo::mappingState() const {
    return p->mapping_state;
}

void NETRootInfo::virtual_hook( int, void* )
{ /*BASE::virtual_hook( id, data );*/ }

void NETWinInfo::virtual_hook( int, void* )
{ /*BASE::virtual_hook( id, data );*/ }

// Functions for X timestamp comparing. For Time being 32bit they're fairly simple
// (the #if 0 part), but on 64bit architectures Time is 64bit unsigned long,
// so there special care needs to be taken to always use only the lower 32bits.
#if 0
int NET::timestampCompare( Time time1, Time time2 ) // like strcmp()
    {
    if( time1 == time2 )
        return 0;
    return ( time1 - time2 ) < 0x7fffffffU ? 1 : -1; // time1 > time2 -> 1, handle wrapping
    }

Time NET::timestampDiff( Time time1, Time time2 ) // returns time2 - time1
    { // no need to handle wrapping?
    return time2 - time1;
    }
#else
int NET::timestampCompare( unsigned long time1_, unsigned long time2_ ) // like strcmp()
    {
    TQ_UINT32 time1 = time1_;
    TQ_UINT32 time2 = time2_;
    if( time1 == time2 )
        return 0;
    return TQ_UINT32( time1 - time2 ) < 0x7fffffffU ? 1 : -1; // time1 > time2 -> 1, handle wrapping
    }

int NET::timestampDiff( unsigned long time1_, unsigned long time2_ ) // returns time2 - time1
    { // no need to handle wrapping?
    TQ_UINT32 time1 = time1_;
    TQ_UINT32 time2 = time2_;
    return TQ_UINT32( time2 - time1 );
    }
#endif


#endif