/*-
 * Copyright (c) 2001 Jordan DeLong
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the author nor the names of contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#include "plugutil.h"

/* per-screen structure */
static struct netwmscr
{
	Window		wm_check; /* check window, also used as button proxy */
} *netwmscr = NULL;


static desktop_t **desktop_list;
static int desktop_count;


/* win_state bitmask values */
#define WIN_STATE_STICKY		    (1L << 0)
#define WIN_STATE_MINIMIZED		 (1L << 1) /* ignored */
#define WIN_STATE_MAXIMIZED_VERT	 (1L << 2) /* ignored */
#define WIN_STATE_MAXIMIZED_HORIZ (1L << 3) /* ignored */
#define WIN_STATE_HIDDEN		    (1L << 4) /* ignored */
#define WIN_STATE_SHADED		    (1L << 5) /* ignored */
#define WIN_STATE_HID_WORKSPACE	 (1L << 6) /* ignored */
#define WIN_STATE_HID_TRANSIENT	 (1L << 7) /* ignored */
#define WIN_STATE_FIXED_POSITION	 (1L << 8)
#define WIN_STATE_ARRANGE_IGNORE	 (1L << 9) /* ignored */

/* win_hints bitmask values */
#define WIN_HINTS_SKIP_FOCUS		(1L << 0)
#define WIN_HINTS_SKIP_WINLIST		(1L << 1) /* ignored */
#define WIN_HINTS_SKIP_TASKBAR		(1L << 2) /* ignored */
#define WIN_HINTS_GROUP_TRANSIENT	(1L << 3) /* ignored */
#define WIN_HINTS_FOCUS_ON_CLICK	(1L << 4) /* ignored */
#define WIN_HINTS_DO_NOT_COVER		(1L << 5) /* ignored */

/* win_layer values */
#define WIN_LAYER_DESKTOP		0
#define WIN_LAYER_BELOW			2
#define WIN_LAYER_NORMAL		4
#define WIN_LAYER_ONTOP			6
#define WIN_LAYER_DOCK			8
#define WIN_LAYER_ABOVE_DOCK    10
#define WIN_LAYER_MENU			12

/* atoms */
static Atom	win_supporting_wm_check;
static Atom	win_state;
static Atom	win_hints;
static Atom	win_layer;

// netwm
static Atom net_wm_desktop;
static Atom net_wm_window_type;
static Atom net_wm_window_type_dock;
static Atom net_wm_window_type_desktop;
static Atom net_wm_state;
static Atom net_wm_state_skip_pager;
static Atom net_wm_state_sticky;
static Atom net_wm_desktop_viewport;

#define MAX_DESK_NAMES 15
#define MAX_NAME_LEN   40
char desk_names[MAX_DESK_NAMES][MAX_NAME_LEN];

/* macros to get protocol atoms */
#define NUM_PROTOCOLS				13
#define WIN_CLIENT_LIST			   win_protocols_list[0]
#define WIN_DESKTOP_BUTTON_PROXY	win_protocols_list[1]
#define WIN_WORKSPACE			   win_protocols_list[2]
#define WIN_WORKSPACE_COUNT		win_protocols_list[3]
#define WIN_AREA_COUNT				win_protocols_list[4]
#define WIN_AREA			         win_protocols_list[5]

#define NET_CLIENT_LIST	      	win_protocols_list[6]
#define NET_NUMBER_OF_DESKTOPS	win_protocols_list[7]
#define NET_CURRENT_DESKTOP	   win_protocols_list[8]
#define NET_ACTIVE_WINDOW        win_protocols_list[9]
#define NET_DESKTOP_NAMES        win_protocols_list[10]
#define NET_DESKTOP_VIEWPORT     win_protocols_list[11]
#define NET_CLIENT_LIST_STACKING win_protocols_list[12]

/* _WIN_PROTOCOLS stuff */
static Atom win_protocols;
static Atom win_protocols_list[NUM_PROTOCOLS];
static char *win_protocols_names[NUM_PROTOCOLS] = {
	"_WIN_CLIENT_LIST",
	"_WIN_DESKTOP_BUTTON_PROXY",
	"_WIN_WORKSPACE",
	"_WIN_WORKSPACE_COUNT",
	"_WIN_AREA_COUNT",
	"_WIN_AREA",
   "_NET_CLIENT_LIST",
   "_NET_NUMBER_OF_DESKTOPS",
	"_NET_CURRENT_DESKTOP",
   "_NET_ACTIVE_WINDOW",
   "_NET_DESKTOP_NAMES",
   "_NET_DESKTOP_VIEWPORT",
   "_NET_CLIENT_LIST_STACKING"
};

/* set the list of clients for screen */
static int set_client_list( screen_t *screen,
                            client_t *skip_client )
{
	Window *list, *tmp;
	client_t *client;
	int size, count;
   CARD32 val[2];

	count = 0;
	size = 10;
	list = malloc(sizeof(Window) * size);
	if (!list)
		return -1;

   /* log( 0, "----------------------------------------------"); */
   /*    log( 0, "--------------- set_client_list"); */
   /*    log( 0, "----------------------------------------------"); */

	/* recreate the list of clients */
	LIST_FOREACH(client, &client_list, c_list)
       if ( !client->flags.internal && client->screen == screen )
       {
          /* log( 4,"processing a client: %x skip is: %x\n", client, skip_client ); */
          if( client != skip_client )
          {
             if( client->workspace == NULL )
             {
                //log(4,"hmm, no workspace, must be a new arrival ... take the current ?");
             }
             else
             {
                /* log( 4,"client desktop: %d, current: %d",  */
/*                      client->workspace->desktop->num,  */
/*                      screen->desktop->num ); */

                if( client->workspace->desktop->num == screen->desktop->num )
                {
//                   log( 4, "client on current workspace");

                   if (++count > size)
                   {
                      tmp = realloc(list, sizeof(Window) * (size + size));
                      if (!tmp)
                      {
                         free(list);
                         return -1;
                      }
                      list = tmp;
                      size += size;
                   }
                   list[count - 1] = client->window;

                   // bva. set the netwm hint that will tell any pagers, etc, what
                   // workspace this client is on ...
                   val[0] = (CARD32) client->workspace->desktop->num;
                   XChangeProperty( display, client->window, net_wm_desktop, XA_CARDINAL, 32,
                                    PropModeReplace,  (u_char *) val, 1 );

/*                    log( 4, "setting a viewport attr to: %d, %d", */
/*                         client->workspace->desktop->viewx, */
/*                         client->workspace->desktop->viewy ); */

                   // this is resetting the viewx !
                   val[0] = (CARD32) client->workspace->desktop->viewx;
                   val[1] = (CARD32) client->workspace->desktop->viewy;
                   XChangeProperty( display, client->window,
                                    net_wm_desktop_viewport,
                                    XA_CARDINAL, 32,
                                    PropModeReplace,  (u_char *) val, 2 );
                }
             }
          }
          else
          {
//             log( 4, "skipping a client ....\n");
          }
       }

	/* put it on the root window */
	XChangeProperty( display, screen->root, WIN_CLIENT_LIST, XA_CARDINAL, 32,
                    PropModeReplace, (u_char *) list, count );

   // bva
   // IS the client on the current desktop .....
   //screen->desktop->num;
   // log( 2, "setting %d clients on this space", count );
   XChangeProperty( display, screen->root, NET_CLIENT_LIST, XA_WINDOW, 32,
                    PropModeReplace, (u_char *) list, count );
   XChangeProperty( display, screen->root, NET_CLIENT_LIST_STACKING, XA_WINDOW, 32,
                    PropModeReplace, (u_char *) list, count );
   // end bva

	free(list);
	return 0;
}

/* handle hints for newly arriving clients */
static int init_hints(int pcall, client_t *client)
{
	CARD32 *card;
   Atom *prop_atom;
	Atom rettype;
	u_long items, bytes;
	int fmt;
   CARD32 val[2];
   int *value;
   int index;

   //  log( 0, "init_hints" );

	/* _WIN_STATE hint */
	if ( XGetWindowProperty( display, client->window, win_state, 0, 1, 0,
                            XA_CARDINAL, &rettype, &fmt, &items, &bytes,
                            (u_char **) &card) == Success && card )
   {
      if (*card & WIN_STATE_STICKY  )
      {
         client->flags.sticky = 1;
      }
      if (*card & WIN_STATE_FIXED_POSITION)
      {
         client->flags.noresize = 1;
         client->flags.nomove = 1;
      }
      XFree(card);
   }

	/* _WIN_HINTS hint */
	if (XGetWindowProperty(display, client->window, win_hints, 0, 1, 0,
                          XA_CARDINAL, &rettype, &fmt, &items, &bytes,
                          (u_char **) &card) == Success && card)
   {
		if (*card & WIN_HINTS_SKIP_FOCUS)
      {
			client->flags.nofocus = 1;
      }
		XFree(card);
	}

	/* _WIN_LAYER hint */
	if (XGetWindowProperty( display, client->window, win_layer, 0, 1, 0,
                           XA_CARDINAL, &rettype, &fmt, &items, &bytes,
                           (u_char **) &card) == Success && card )
   {
		if (*card >= WIN_LAYER_DESKTOP && *card < WIN_LAYER_BELOW)
			client->stacklayer = STACKLAYER_BOTTOM;
		else if (*card < WIN_LAYER_NORMAL)
			client->stacklayer = STACKLAYER_BELOW;
		else if (*card < WIN_LAYER_ONTOP)
			client->stacklayer = STACKLAYER_NORMAL;
		else if (*card < WIN_LAYER_ABOVE_DOCK)
			client->stacklayer = STACKLAYER_ABOVE;
		else if (*card <= WIN_LAYER_MENU)
			client->stacklayer = STACKLAYER_TIPTOP;
		XFree(card);
	}


   // client->workspace

   // bva. TODO: There is a whole list of properties that we should check and add to
   //            the list below .....

   if ( XGetWindowProperty( display,
                            client->window,
                            net_wm_desktop,
                            0,
                            1,
                            0,
                            XA_CARDINAL,
                            &rettype,
                            &fmt,
                            &items,
                            &bytes,
                            (u_char **) &value) == Success && value )
   {
      log( 2,"window has a hint");

      if( rettype != XA_CARDINAL )
      {
         log(2,"retfmt: %d value: %d\n", fmt, *value );
         log(2,"rettype: %d\n", rettype );
         log(2,"net_wm_desktop: %d\n", net_wm_desktop );
      }
      else
      {
         if( *value == 0xFFFFFFFF )
         {
            client->flags.sticky = 1;
         }
         else
         {
            // since we have 2-d grid of desktops, we must be fancy.
            //   we have "x" desktops that are
            //      "y" wide (these are the workspaces)
            //      "z" high (these are the workspaces)

            // I think there is another attribute for workspaces, so right
            // now we are going to assume that everything is "1" wide and
            // "1" high. When I figure out what that attribute is for workspace
            // add it in here.

            //vx = (client->x + desktop->viewx * client->screen->width)	/ client->screen->width;
            //vy = (client->y + desktop->viewy * client->screen->height)	/ client->screen->height;
            //client->x = (client->screen->width * *value ) + client->x;
            //client->y = (client->screen->height * *value ) + client->y;
            //vy = (int) (client->screen->height * *value) + client->y;

            if( *value >= desktop_count )
            {
               index = desktop_count - 1;
            }
            else
            {
               index = *value;
            }
            log( 2, "client should be on desk: %d which is: %x",
                 index, desktop_list[index] );

            client->initial_attr.desktop = desktop_list[index];
         }
      }
      XFree(value);

      if ( XGetWindowProperty( display,
                               client->window,
                               net_wm_desktop_viewport,
                               0,
                               2,
                               0,
                               XA_CARDINAL,
                               &rettype,
                               &fmt,
                               &items,
                               &bytes,
                               (u_char **) &value) == Success && value )
      {
         log( 2, "viewport value found: %d %d\n", *value, *(value+1) );

         client->initial_attr.viewport_x = *value;
         client->initial_attr.viewport_y = *(value+1);
      }
      XFree(value);

   }
   else
   {
      // bva. Looks like we are supposed to set the desktop for each window
      // as a property. This allows applications to query the workspace a particular
      // application is on. Since this is window birth .. take the current !
      //atom__NET_WM_DESKTOP
      log( 2, "adding a new window to destop: %d", client->screen->desktop->num );

      val[0] = (CARD32) client->screen->desktop->num;
      XChangeProperty( display, client->window, net_wm_desktop, XA_CARDINAL, 32,
                       PropModeReplace,  (u_char *) val, 1 );

      val[0] = (CARD32) client->screen->desktop->viewx;
      val[1] = (CARD32) client->screen->desktop->viewy;
      XChangeProperty( display, client->window, net_wm_desktop_viewport,
                       XA_CARDINAL, 32,
                       PropModeReplace,  (u_char *) val, 2 );
   }

   // bva. should create a new flag for this ..: client->flags.internal
   if( XGetWindowProperty( display, client->window, net_wm_state, 0, 1, 0,
                           XA_ATOM, &rettype, &fmt, &items, &bytes,
                           (u_char **) &prop_atom ) == Success && prop_atom )
   {
      if( *prop_atom && net_wm_state_skip_pager )
      {
         client->flags.internal = 1;
      }
      if( *prop_atom && net_wm_state_sticky )
      {
         client->flags.sticky = 1;
      }
      XFree(prop_atom);
   }

   if( XGetWindowProperty( display, client->window, net_wm_window_type, 0, 1, 0,
                           XA_ATOM, &rettype, &fmt, &items, &bytes,
                           (u_char **) & prop_atom ) == Success && prop_atom )
   {
      if( *prop_atom == net_wm_window_type_dock )
      {
         client->flags.noresize = 1;
         client->flags.nomove = 1;
         /* let's keep it from being decorated */
         client->dgroup = &dgroup_empty;
      }
      if( *prop_atom == net_wm_window_type_desktop )
      {
         client->stacklayer = STACKLAYER_BOTTOM;
      }
      XFree(prop_atom);
   }

	return PLUGIN_OK;
}

/* when new windows arrive */
static int window_life(int pcall, client_t *client)
{
   CARD32 *card;
	Atom type;
	u_long items, bytes;
	int fmt;

	if( set_client_list(client->screen, NULL ) == -1 )
   {
      return PLUGIN_UNLOAD;
   }

   if( XGetWindowProperty( display, client->window, net_wm_desktop, 0, 1, 0,
                           XA_CARDINAL, &type, &fmt, &items, &bytes,
                           (u_char **) &card ) == Success && card )
   {
      if( *card == 0xFFFFFFFF )
      {
         client->flags.sticky = 1;
      }
      else
      {
         log( 2, "got a client whose atom says: %d\n", *card );
      }
      XFree(card);
   }

	return PLUGIN_OK;
}

/* when new windows leave */
static int window_death( int pcall,
                         client_t *client )
{

   log( 0, "----[ netwm. window death: %x win: %x ]------\n",
        client, client->window );
	if( set_client_list( client->screen,
                        client ) == -1 )
   {
      return PLUGIN_UNLOAD;
   }

	return PLUGIN_OK;
}


/* workspace changes */
static int workspace_change(int pcall, screen_t *screen, desktop_t *desktop)
{
	CARD32 val[2];

	if (desktop == screen->desktop)
   {
      val[0] = (CARD32) screen->desktop->viewx;
      val[1] = (CARD32) screen->desktop->viewy;
      XChangeProperty( display, screen->root,
                       WIN_AREA, XA_CARDINAL,
                       32, PropModeReplace, (u_char *) val, 2 );

      XChangeProperty( display, screen->root,
                       NET_DESKTOP_VIEWPORT,
                       XA_CARDINAL, 32,
                       PropModeReplace,  (u_char *) val, 2 );

      //bva
      if( set_client_list( screen, NULL ) == -1 )
      {
         return PLUGIN_UNLOAD;
      }
      // end bva
	}

	return PLUGIN_OK;
}

/* when the user switches desktops */
static int desktop_change(int pcall, screen_t *screen, desktop_t *olddesk)
{
	CARD32 val[2];

   log( 0, "desktop_change to %d",
          (CARD32) screen->desktop->num );

	/* set the workspace area properties */
	val[0] = (CARD32) screen->desktop->width;
	val[1] = (CARD32) screen->desktop->height;
	XChangeProperty(display, screen->root, WIN_AREA_COUNT, XA_CARDINAL,
                    32, PropModeReplace, (u_char *) val, 2);

	/* now set the current desktop */
	val[0] = (CARD32) screen->desktop->num;
	XChangeProperty(display, screen->root, WIN_WORKSPACE, XA_CARDINAL,
                    32, PropModeReplace, (u_char *) val, 1);

   // bva
   XChangeProperty(display, screen->root, NET_CURRENT_DESKTOP, XA_CARDINAL,
                   32, PropModeReplace, (u_char *) val, 1);

   val[0] = (CARD32) screen->desktop->viewx;
   val[1] = (CARD32) screen->desktop->viewy;
   XChangeProperty( display, screen->root, NET_DESKTOP_VIEWPORT,
                    XA_CARDINAL, 32,
                    PropModeReplace,  (u_char *) val, 2 );
   // end bva

	/* desktop change also changes workspace */
	return workspace_change(pcall, screen, screen->desktop);
}

/* button press on the root window */
static int root_button(int pcall, screen_t *screen, XButtonEvent *e)
{
	if (e->type == ButtonPress)
		XUngrabPointer(display, CurrentTime);

	//bva. We don't need this right now ...
    //
    //  XSendEvent( display, netwmscr[screen->num].wm_check, 0,
    //              SubstructureNotifyMask, (XEvent *) e );

	return PLUGIN_OK;
}

/* plugin death */
void netwm_shutdown() {
	screen_t *screen;

	/* kill our per-screen structure */
	if (!netwmscr)
		return;

	/* for each managed screen */
	TAILQ_FOREACH(screen, &screen_list, s_list) {
		XDeleteProperty(display, RootWindow(display, screen->num),
			win_supporting_wm_check);
		XDeleteProperty(display, RootWindow(display, screen->num),
			win_protocols);
		XDeleteProperty(display, RootWindow(display, screen->num),
			WIN_DESKTOP_BUTTON_PROXY);
		XDeleteProperty(display, RootWindow(display, screen->num),
                        WIN_CLIENT_LIST);
		if (netwmscr[screen->num].wm_check)
           XDestroyWindow(display, netwmscr[screen->num].wm_check);
	}
	free(netwmscr);
}

// bva. not used ... just testing ...
static int map_request (int pcall, screen_t *screen, XMapRequestEvent *e)
{
   return  PLUGIN_OK;
}

static int focus_change(int pcall, client_t *client)
{
   Window val[2];

   // log( 2, "focus_change");

//atom__NET_ACTIVE_WINDOW

   // set_client_list(client->screen);

   // bva. set the netwm hint that will tell any pagers, etc, what
   // workspace this client is on ...

   if( client )
   {
      // log( 4,"setting active window .....");

      val[0] = client->window;
      XChangeProperty( display, client->screen->root, NET_ACTIVE_WINDOW, XA_WINDOW, 32,
                       PropModeReplace, (u_char *) val, 1 );
   }

   return PLUGIN_OK;
}

static int external_client_message( int pcall, screen_t *screen, XClientMessageEvent *e)
{
   log( 0, "external_client_message");
   log( 4, "data: %d", (int) e->data.l[0] );

   if( screen->root == e->window &&
       e->message_type == NET_CURRENT_DESKTOP )
   {
      //log(2, "desktop change!!!!!!!!!!!!!!!!!");
      //  xev.format = 32;
      // xev.data.l[0] = new_desk;
      desktop_switch( screen, e->data.l[0] );
   }
   return PLUGIN_OK;
}

static int geometry_change(int pcall, client_t *client)
{
   CARD32 val[2];

   //log( 0, "===== geometry_change =======");

   /* some nasty clients that aren't even moveable spam us with many
      geometry requests. so let's just ignore them */
   if( client->flags.noresize || client->flags.nomove )
   {
      return PLUGIN_OK; /* ---------> */
   }

   // bva. set the netwm hint that will tell any pagers, etc, what
   // workspace this client is on ...
   val[0] = (CARD32) client->workspace->desktop->num;
   XChangeProperty( display, client->window, net_wm_desktop, XA_CARDINAL, 32,
                    PropModeReplace,  (u_char *) val, 1 );

   /* log( 2, "geometry_change: setting a viewport attr to: %d, %d\n", */
   /*         client->workspace->desktop->viewx, */
   /*         client->workspace->desktop->viewy ); */

   // this is resetting the viewx !
   val[0] = (CARD32) client->workspace->desktop->viewx;
   val[1] = (CARD32) client->workspace->desktop->viewy;
   XChangeProperty( display, client->window,
                    net_wm_desktop_viewport,
                    XA_CARDINAL, 32,
                    PropModeReplace,  (u_char *) val, 2 );

   // update our list if necessary, since the geometry change might have
   // move the client into the current workspace.
   set_client_list( client->screen, NULL );

   return PLUGIN_OK;
}

static int configure_notify(int pcall, client_t *client, XPropertyEvent *e)
{
   return PLUGIN_OK;
}

static int property_notify(int pcall, client_t *client, XPropertyEvent *e)
{
   return PLUGIN_OK;
}


void updateWorkspaceNamesHint( screen_t *screen,
                               int wspace_count )
{
    char *wsNames[20];
    XTextProperty textProp;
    int i;

    for (i = 0; i < wspace_count; i++)
    {
       //temp = (char *) malloc( 20 );
       //sprintf( temp, "%d", i + 1 );
       //wsNames[i] = temp;
       wsNames[i] = desk_names[i];
    }

    if (XStringListToTextProperty(wsNames, wspace_count, &textProp))
    {
       XSetTextProperty( display, screen->root, &textProp,
                         NET_DESKTOP_NAMES );
       XFree(textProp.value);
    }

    for (i = 0; i < wspace_count; i++)
    {
       // free( wsNames[i] );
    }
}


/* plugin intialization */
int netwm_init()
{
	char *names;
   char *temp;
   int i, j;

	/* get parameters */
	OPTIONAL_PARAM(&plugin_this->params, "desktop_names", string, names, NULL );

   i = 0;
   if( names )
   {

      temp = strtok( names, "," );

      while( temp != NULL )
      {
         if( temp )
         {
            strcpy( desk_names[i], temp );
         }
         i++;
         temp = strtok( NULL, "," );
      }

      for( j = i; j < MAX_DESK_NAMES; j++ )
      {
         sprintf( desk_names[j], "%d", j + 1);
      }

   }

   return PLUGIN_OK;
}


/* alloc stuff; create the gnome window check window */
int netwm_start()
{
	XSetWindowAttributes attr;
	screen_t *screen;
	CARD32 val[2];
   desktop_t *desktop;


	/* register callbacks */
	plugin_callback_add(plugin_this, PCALL_INIT_HINTS,       init_hints);
	plugin_callback_add(plugin_this, PCALL_WINDOW_BIRTH,     window_life);
	plugin_callback_add(plugin_this, PCALL_WINDOW_DEATH,     window_death);
	plugin_callback_add(plugin_this, PCALL_WORKSPACE_CHANGE, workspace_change);
	plugin_callback_add(plugin_this, PCALL_DESKTOP_CHANGE,   desktop_change);
	plugin_callback_add(plugin_this, PCALL_ROOT_BUTTON,      root_button);

   // bva

   // bva. I need a plugin that knows a window has been dragged off the current workspace.
   //   this will allow us to change the client list and update any external pagers ...
   // ***I think we need a new callback that is called when a client is added or removed
   //    from a workspace. See workspace.c workspace_add_client() and workspace_rm_client()
   //    for triggers ...*** oorrrrrrrr figure out where the window_birth callback is triggered
   //    from and call that when a client changes a workspace, since that is a type of "birth"
   //    as well and wouldn't add another cheesy callback :)

   // two new angles:
   //  a) what about using:  plugin_setcontext() to get all xevents ?
   //  b) what about adding a configureNotify callback, since once the pager moves a window
   //     it sends a synthetic ICCM event that is a configureNotify ... we'd catch this and
   //     update the root window properties ... buuuuuuuuuuuut we seem to be getting this
   //     constantly when a window is being dragged ... we'll be flooded .... we need the
   //     pager to call: plugin_geometry_change(client), just like action.c window placment
   //     code.

   plugin_callback_add(plugin_this, PCALL_FOCUS_CHANGE, focus_change );
   plugin_callback_add(plugin_this, PCALL_EXT_CLIENT_MESSAGE, external_client_message );
   plugin_callback_add(plugin_this, PCALL_MAP_REQUEST, map_request );
   plugin_callback_add(plugin_this, PCALL_PROPERTY_NOTIFY, property_notify );
   plugin_callback_add(plugin_this, PCALL_CONFIGURE_NOTIFY, configure_notify );
   plugin_callback_add(plugin_this, PCALL_GEOMETRY_CHANGE, geometry_change );
   // end bva


	/* get memory for the per-screen structure */
	netwmscr = calloc(screen_count, sizeof(struct netwmscr));
	if (!netwmscr)
		return PLUGIN_UNLOAD;

	/* get atoms */
	win_supporting_wm_check = XInternAtom(display, "_WIN_SUPPORTING_WM_CHECK", 0);
	win_state = XInternAtom(display, "_WIN_STATE", 0);
	win_hints = XInternAtom(display, "_WIN_HINTS", 0);
	win_layer = XInternAtom(display, "_WIN_LAYER", 0);

   net_wm_desktop = XInternAtom(display, "_NET_WM_DESKTOP", 0);
   net_wm_window_type = XInternAtom(display, "_NET_WM_WINDOW_TYPE", 0);
   net_wm_window_type_dock = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DOCK", 0);
   net_wm_window_type_desktop = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DESKTOP", 0);
   net_wm_state = XInternAtom(display, "_NET_WM_STATE", False);
   net_wm_state_skip_pager = XInternAtom(display, "_NET_WM_STATE_SKIP_PAGER", 0);
   net_wm_state_sticky = XInternAtom(display, "_NET_WM_STATE_STICKY", 0 );
   net_wm_desktop_viewport = XInternAtom(display, "_NET_DESKTOP_VIEWPORT", 0);

	/* get win protocols atoms */
	win_protocols = XInternAtom(display, "_WIN_PROTOCOLS", 0);
	XInternAtoms(display, win_protocols_names, NUM_PROTOCOLS,
                 0, win_protocols_list);

	/* fill in the per-screen structure */
 	attr.override_redirect = 1;
	TAILQ_FOREACH(screen, &screen_list, s_list)
       {
          /* create the gnome-compliance check window */
          netwmscr[screen->num].wm_check = XCreateWindow(display, screen->root,
                                                         -30, -30, 2, 2, 0, CopyFromParent,
                                                         CopyFromParent, CopyFromParent,
                                                         CWOverrideRedirect, &attr);
          XChangeProperty(display, screen->root, win_supporting_wm_check, XA_CARDINAL,
                          32, PropModeReplace, (u_char *) &netwmscr[screen->num].wm_check, 1);
          XChangeProperty(display, netwmscr[screen->num].wm_check,
                          win_supporting_wm_check, XA_CARDINAL, 32, PropModeReplace,
                          (u_char *) &netwmscr[screen->num].wm_check, 1);

          /* set the win_protocols list onto the root window */
          XChangeProperty(display, screen->root, win_protocols, XA_ATOM,
                          32, PropModeReplace, (u_char *) win_protocols_list, NUM_PROTOCOLS);

          /* reuse the check window as the button proxy window */
          XChangeProperty(display, screen->root, WIN_DESKTOP_BUTTON_PROXY, XA_CARDINAL,
                          32, PropModeReplace, (u_char *) &netwmscr[screen->num].wm_check, 1);
          XChangeProperty(display, netwmscr[screen->num].wm_check,
                          WIN_DESKTOP_BUTTON_PROXY, XA_CARDINAL, 32, PropModeReplace,
                          (u_char *) &netwmscr[screen->num].wm_check, 1);

          /* setup workspace count, current workspace */
          val[0] = (CARD32) screen->desktop_count;
          XChangeProperty(display, screen->root, WIN_WORKSPACE_COUNT, XA_CARDINAL, 32,
                          PropModeReplace, (u_char *) val, 1);
          //bva
          XChangeProperty(display, screen->root, NET_NUMBER_OF_DESKTOPS, XA_CARDINAL,
                          32, PropModeReplace, (u_char *) val, 1);

          //end bva
          val[0] = (CARD32) screen->desktop->num;
          XChangeProperty(display, screen->root, WIN_WORKSPACE, XA_CARDINAL,
                          32, PropModeReplace, (u_char *) val, 1);
          // bva
          XChangeProperty(display, screen->root, NET_CURRENT_DESKTOP, XA_CARDINAL,
                          32, PropModeReplace, (u_char *) val, 1);

          val[0] = (CARD32) screen->desktop->viewx;
          val[1] = (CARD32) screen->desktop->viewy;
          XChangeProperty( display, screen->root, NET_DESKTOP_VIEWPORT,
                           XA_CARDINAL, 32,
                           PropModeReplace,  (u_char *) val, 2 );
          // end bva

          /* set the workspace area properties */
          val[0] = (CARD32) screen->desktop->width;
          val[1] = (CARD32) screen->desktop->height;
          XChangeProperty(display, screen->root, WIN_AREA_COUNT, XA_CARDINAL,
                          32, PropModeReplace, (u_char *) val, 2);
          val[0] = (CARD32) screen->desktop->viewx;
          val[1] = (CARD32) screen->desktop->viewy;
          XChangeProperty(display, screen->root, WIN_AREA, XA_CARDINAL,
                          32, PropModeReplace, (u_char *) val, 2);

          // bva
          updateWorkspaceNamesHint( screen,
                                    screen->desktop_count );
          // end bva


          desktop_count = 0;
          desktop_list = calloc(screen->desktop_count, sizeof(desktop_t *));
          TAILQ_FOREACH(desktop, &screen->s_desklist, d_list) {
             log( 0, "netwm adding a desktop: %x", desktop);
             desktop_list[desktop_count] = desktop;
             desktop_count++;
          }


          /* setup the CLIENT_LIST on the screen*/
          if (set_client_list(screen, NULL ) == -1)
          {
             return PLUGIN_UNLOAD;
          }
       }

	return PLUGIN_OK;
}
