/* This module, and the entire NoClutter program, and the concept for
 * interfacing this module to the Window Manager, are all original work
 * by Robert Nation and Nobutaka Suzuki <nobuta-s@is.aist-nara.ac.jp>
 *
 * Copyright 1994, Robert Nation and Nobutaka Suzuki.
 * No guarantees or warantees or anything
 * are provided or implied in any way whatsoever. Use this program at your
 * own risk. Permission to use this program for any purpose is given,
 * as long as the copyright is kept intact. */

/* This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "config.h"

#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/time.h>

#if HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif

#include <unistd.h>
#include <ctype.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xproto.h>
#include <X11/Xatom.h>
#include <X11/Intrinsic.h>
#include <X11/cursorfont.h>
#include <X11/keysym.h>
#ifdef I18N_MB
#include <X11/Xlocale.h>
#endif

#include "libs/fvwmlib.h"
#include "libs/FScreen.h"
#include "libs/FShape.h"
#include "libs/Module.h"
#include "libs/Picture.h"
#include "libs/Colorset.h"
#include "libs/fvwmsignal.h"
#include "FvwmIdent.h"

static RETSIGTYPE TerminateHandler(int);

static char *MyName;
static fd_set_size_t fd_width;
static int fd[2];

static Display *dpy;			/* which display are we talking to */
static Window Root;
static GC gc;
static XFontStruct *font;
#ifdef I18N_MB
static XFontSet fontset;
#endif

static int screen;
static int x_fd;

static char *yes = "Yes";
static char *no = "No";

/* default colorset to use, set to -1 when explicitly setting colors */
static int colorset = 0;

static char *BackColor = "white";
static char *ForeColor = "black";
static char *font_string = "fixed";

static Pixel fore_pix;
static Window main_win;
static Window app_win;

static EventMask mw_events =
  ExposureMask | ButtonPressMask | KeyPressMask |
  ButtonReleaseMask | KeyReleaseMask | StructureNotifyMask;

static Atom wm_del_win;

static struct target_struct target;
static int found=0;

static int ListSize=0;

static struct Item* itemlistRoot = NULL;
static int max_col1, max_col2;
static char id[15], desktop[10], swidth[10], sheight[10], borderw[10];
static char geometry[30], mymin_aspect[11], max_aspect[11], layer[10];

/***********************************************************************
 *
 *  Procedure:
 *	main - start of module
 *
 ***********************************************************************/
int main(int argc, char **argv)
{
  char *temp, *s;
  char *display_name = NULL;
  int Clength;
  char *tline;

#ifdef I18N_MB
  setlocale(LC_CTYPE, "");
#endif

  /* Save the program name for error messages and config parsing */
  temp = argv[0];
  s=strrchr(argv[0], '/');
  if (s != NULL)
    temp = s + 1;

  MyName = safemalloc(strlen(temp)+2);
  strcpy(MyName,"*");
  strcat(MyName, temp);
  Clength = strlen(MyName);

  if((argc != 6)&&(argc != 7))
    {
      fprintf(stderr,"%s Version %s should only be executed by fvwm!\n",MyName,
	      VERSION);
      exit(1);
    }

#ifdef HAVE_SIGACTION
  {
    struct sigaction  sigact;

    sigemptyset(&sigact.sa_mask);
    sigaddset(&sigact.sa_mask, SIGPIPE);
    sigaddset(&sigact.sa_mask, SIGTERM);
    sigaddset(&sigact.sa_mask, SIGQUIT);
    sigaddset(&sigact.sa_mask, SIGINT);
    sigaddset(&sigact.sa_mask, SIGHUP);
# ifdef SA_INTERRUPT
    sigact.sa_flags = SA_INTERRUPT;
# else
    sigact.sa_flags = 0;
# endif
    sigact.sa_handler = TerminateHandler;

    sigaction(SIGPIPE, &sigact, NULL);
    sigaction(SIGTERM, &sigact, NULL);
    sigaction(SIGQUIT, &sigact, NULL);
    sigaction(SIGINT,  &sigact, NULL);
    sigaction(SIGHUP,  &sigact, NULL);
  }
#else
  /* We don't have sigaction(), so fall back to less robust methods.  */
#ifdef USE_BSD_SIGNALS
  fvwmSetSignalMask( sigmask(SIGPIPE) |
                     sigmask(SIGTERM) |
                     sigmask(SIGQUIT) |
                     sigmask(SIGINT) |
                     sigmask(SIGHUP) );
#endif
  signal(SIGPIPE, TerminateHandler);
  signal(SIGTERM, TerminateHandler);
  signal(SIGQUIT, TerminateHandler);
  signal(SIGINT,  TerminateHandler);
  signal(SIGHUP,  TerminateHandler);
#ifdef HAVE_SIGINTERRUPT
  siginterrupt(SIGPIPE, 1);
  siginterrupt(SIGTERM, 1);
  siginterrupt(SIGQUIT, 1);
  siginterrupt(SIGINT, 1);
  siginterrupt(SIGHUP, 1);
#endif
#endif

  fd[0] = atoi(argv[1]);
  fd[1] = atoi(argv[2]);

  /* An application window may have already been selected - look for it */
  sscanf(argv[4],"%x",(unsigned int *)&app_win);

  /* Open the Display */
  if (!(dpy = XOpenDisplay(display_name)))
    {
      fprintf(stderr,"%s: can't open display %s", MyName,
	      XDisplayName(display_name));
      exit (1);
    }
  x_fd = XConnectionNumber(dpy);
  screen= DefaultScreen(dpy);
  Root = RootWindow(dpy, screen);

  InitPictureCMap(dpy);
  FScreenInit(dpy);
  /* prevent core dumps if fvwm doesn't provide any colorsets */
  AllocColorset(0);
  FShapeInit(dpy);

  SetMessageMask(fd, M_CONFIGURE_WINDOW | M_WINDOW_NAME | M_ICON_NAME
                 | M_RES_CLASS | M_RES_NAME | M_END_WINDOWLIST | M_CONFIG_INFO
                 | M_END_CONFIG_INFO | M_SENDCONFIG);
  /* scan config file for set-up parameters */
  /* Colors and fonts */

  InitGetConfigLine(fd,MyName);
  GetConfigLine(fd,&tline);

  while(tline != (char *)0) {
    if(strlen(tline)>1) {
      if(strncasecmp(tline, CatString3(MyName,"Font",""),Clength+4)==0) {
        CopyStringWithQuotes(&font_string,&tline[Clength+4]);
      }
      else if(strncasecmp(tline,CatString3(MyName,"Fore",""), Clength+4)==0) {
        CopyString(&ForeColor,&tline[Clength+4]);
        colorset = -1;
      }
      else if(strncasecmp(tline,CatString3(MyName, "Back",""), Clength+4)==0) {
        CopyString(&BackColor,&tline[Clength+4]);
        colorset = -1;
      }
      else if(strncasecmp(tline,CatString3(MyName,"Colorset",""),Clength+8)==0){
        sscanf(&tline[Clength+8], "%d", &colorset);
        AllocColorset(colorset);
      }
      else if(strncasecmp(tline, "Colorset", 8) == 0){
        LoadColorset(&tline[8]);
      }
      else if(strncasecmp(tline, XINERAMA_CONFIG_STRING,
			  sizeof(XINERAMA_CONFIG_STRING) - 1) == 0){
	FScreenConfigureModule(
	  tline + sizeof(XINERAMA_CONFIG_STRING) - 1);
      }
    }
    GetConfigLine(fd,&tline);
  }

  if(app_win == 0)
    fvwmlib_get_target_window(dpy, screen, MyName, &app_win, True);

  fd_width = GetFdWidth();

  /* Create a list of all windows */
  /* Request a list of all windows,
   * wait for ConfigureWindow packets */
  SendInfo(fd,"Send_WindowList",0);

  /* tell fvwm we're running */
  SendFinishedStartupNotification(fd);
  if (app_win == Root)
    exit(0);

  Loop(fd);
  return 0;
}

/**************************************************************************
 *
 * Read the entire window list from fvwm
 *
 *************************************************************************/
void Loop(int *fd)
{
  while (1)
  {
    FvwmPacket* packet = ReadFvwmPacket(fd[1]);
    if ( packet == NULL )
      exit(0);
    else
      process_message( packet->type, packet->body );
  }
}


/**************************************************************************
 *
 * Process window list messages
 *
 *************************************************************************/
void process_message(unsigned long type,unsigned long *body)
{
  switch(type)
    {
    /* should turn off this packet but it comes after config_list so have to
       accept at least one */
    case M_CONFIGURE_WINDOW:
      list_configure(body);
      break;
    case M_WINDOW_NAME:
      list_window_name(body);
      break;
    case M_ICON_NAME:
      list_icon_name(body);
      break;
    case M_RES_CLASS:
      list_class(body);
      break;
    case M_RES_NAME:
      list_res_name(body);
      break;
    case M_END_WINDOWLIST:
      list_end();
      break;
    default:
      break;

    }
}




/***********************************************************************
 *
 *  Procedure:
 *	SIGPIPE handler - SIGPIPE means fvwm is dying
 *
 ***********************************************************************/
static RETSIGTYPE
TerminateHandler(int sig)
{
  fvwmSetTerminate(sig);
}

/***********************************************************************
 *
 * Got window configuration info - if its our window, save data
 *
 ***********************************************************************/
void list_configure(unsigned long *body)
{
  struct ConfigWinPacket  *cfgpacket = (void *) body;

  if((app_win == cfgpacket->frame)||(app_win == cfgpacket->w)
     ||((cfgpacket->icon_w != 0)&&(app_win == cfgpacket->icon_w))
     ||((cfgpacket->icon_pixmap_w)&&(app_win == cfgpacket->icon_pixmap_w)))
    {
      app_win = cfgpacket->frame;
      target.id = cfgpacket->w;
      target.frame = cfgpacket->frame;
      target.frame_x = cfgpacket->frame_x;
      target.frame_y = cfgpacket->frame_y;
      target.frame_w = cfgpacket->frame_width;
      target.frame_h = cfgpacket->frame_height;
      target.desktop = cfgpacket->desk;
      target.layer = cfgpacket->layer;
      memcpy(&target.flags, &(cfgpacket->flags), sizeof(cfgpacket->flags));
      target.title_h = cfgpacket->title_height;
      target.border_w = cfgpacket->border_width;
      target.base_w = cfgpacket->hints_base_width;
      target.base_h = cfgpacket->hints_base_height;
      target.width_inc = cfgpacket->hints_width_inc;
      target.height_inc = cfgpacket->hints_height_inc;
      target.gravity = cfgpacket->hints_win_gravity;
      found = 1;
    }
}

/*************************************************************************
 *
 * Capture  Window name info
 *
 ************************************************************************/
void list_window_name(unsigned long *body)
{
  if((app_win == (Window)body[1])||(app_win == (Window)body[0]))
    {
      strncpy(target.name,(char *)&body[3],255);
    }
}

/*************************************************************************
 *
 * Capture  Window Icon name info
 *
 ************************************************************************/
void list_icon_name(unsigned long *body)
{
  if((app_win == (Window)body[1])||(app_win == (Window)body[0]))
    {
      strncpy(target.icon_name,(char *)&body[3],255);
    }
}


/*************************************************************************
 *
 * Capture  Window class name info
 *
 ************************************************************************/
void list_class(unsigned long *body)
{
  if((app_win == (Window)body[1])||(app_win == (Window)body[0]))
    {
      strncpy(target.class,(char *)&body[3],255);
    }
}


/*************************************************************************
 *
 * Capture  Window resource info
 *
 ************************************************************************/
void list_res_name(unsigned long *body)
{
  if((app_win == (Window)body[1])||(app_win == (Window)body[0]))
    {
      strncpy(target.res,(char *)&body[3],255);
    }
}

/*************************************************************************
 *
 * End of window list, open an x window and display data in it
 *
 ************************************************************************/
XSizeHints mysizehints;
void list_end(void)
{
  XGCValues gcv;
  unsigned long gcm;
  int lmax,height;
  XEvent Event;
  int x,y;
  XSetWindowAttributes attributes;
  int is_key_pressed = 0;
  int is_button_pressed = 0;
#ifdef I18N_MB
  char **ml;
  int mc;
  char *ds;
  XFontStruct **fs_list;
#endif

  if(!found)
    {
/*    fprintf(stderr,"%s: Couldn't find app window\n",MyName); */
      exit(0);
    }

  /* tell fvwm to only send config messages */
  SetMessageMask(fd, M_CONFIG_INFO | M_SENDCONFIG);

#ifdef I18N_MB
  if ((fontset = XCreateFontSet(dpy, font_string, &ml, &mc, &ds)) == NULL) {
#ifdef STRICTLY_FIXED
      if ((fontset = XCreateFontSet(dpy, "fixed", &ml, &mc, &ds)) == NULL)
#else
      if ((fontset = XCreateFontSet(dpy, "-*-fixed-medium-r-normal-*-14-*-*-*-*-*-*-*", &ml, &mc, &ds)) == NULL)
#endif
	  exit(1);
  }
  XFontsOfFontSet(fontset, &fs_list, &ml);
  font = fs_list[0];
#else
  /* load the font */
  if ((font = XLoadQueryFont(dpy, font_string)) == NULL)
    if ((font = XLoadQueryFont(dpy, "fixed")) == NULL)
      exit(1);
#endif

  /* make window infomation list */
  MakeList();

  /* size and create the window */
  lmax = max_col1 + max_col2 + 15;

  height = ListSize * (font->ascent + font->descent);

  mysizehints.flags=
    USSize|USPosition|PWinGravity|PResizeInc|PBaseSize|PMinSize|PMaxSize;
  mysizehints.width = lmax + 10;
  mysizehints.height= height + 10;
  mysizehints.width_inc = 1;
  mysizehints.height_inc = 1;
  mysizehints.base_height = mysizehints.height;
  mysizehints.base_width = mysizehints.width;
  mysizehints.min_height = mysizehints.height;
  mysizehints.min_width = mysizehints.width;
  mysizehints.max_height = mysizehints.height;
  mysizehints.max_width = mysizehints.width;
  mysizehints.win_gravity = NorthWestGravity;
  {
    int sx;
    int sy;
    int sw;
    int sh;
    Window JunkW;
    int JunkC;
    unsigned int JunkM;
    fscreen_scr_arg fscr;

    if (!XQueryPointer(
	  dpy, Root, &JunkW, &JunkW, &x, &y, &JunkC, &JunkC, &JunkM))
    {
      x = 0;
      y = 0;
    }

    fscr.xypos.x = x;
    fscr.xypos.y = y;
    FScreenGetScrRect(&fscr, FSCREEN_XYPOS, &sx, &sy, &sw, &sh);
    if (y + height + 100 > sy + sh)
    {
      y = sy + sh - height - 10;
      mysizehints.win_gravity = SouthWestGravity;
    }
    if (x + lmax + 100 > sx + sw)
    {
      x = sx + sw - lmax - 10;
      if (mysizehints.win_gravity == SouthWestGravity)
	mysizehints.win_gravity = SouthEastGravity;
      else
	mysizehints.win_gravity = NorthEastGravity;
    }
  }
  mysizehints.x = x;
  mysizehints.y = y;

  if (Pdepth < 2)
  {
    attributes.background_pixel = GetColor("white");
    fore_pix = GetColor("black");
  }
  else
  {
    attributes.background_pixel = (colorset < 0)
				  ? GetColor(BackColor)
    				  : Colorset[colorset].bg;
    fore_pix = (colorset < 0) ? GetColor(ForeColor)
			      : Colorset[colorset].fg;
  }

  attributes.colormap = Pcmap;
  attributes.border_pixel = 0;
  main_win = XCreateWindow(dpy, Root, x, y,
			   mysizehints.width, mysizehints.height, 0, Pdepth,
			   InputOutput, Pvisual,
			   CWColormap | CWBackPixel | CWBorderPixel,
			   &attributes);
  wm_del_win = XInternAtom(dpy,"WM_DELETE_WINDOW",False);
  XSetWMProtocols(dpy,main_win,&wm_del_win,1);

  XSetWMNormalHints(dpy,main_win,&mysizehints);
  /* have to ask for configure events when transparent */
  if (colorset >= 0 && Colorset[colorset].pixmap == ParentRelative)
    mw_events |= StructureNotifyMask;
  XSelectInput(dpy, main_win, mw_events);
  change_window_name(&MyName[1]);

  gcm = GCForeground|GCFont;
  gcv.foreground = fore_pix;
  gcv.font = font->fid;
  gc = fvwmlib_XCreateGC(dpy, main_win, gcm, &gcv);

  if (colorset >= 0)
    SetWindowBackground(dpy, main_win, mysizehints.width, mysizehints.height,
			&Colorset[(colorset)], Pdepth, gc, True);

  XMapWindow(dpy,main_win);

  /* Window is created. Display it until the user clicks or deletes it. */
  /* also grok any dynamic config changes */
  while(1)
  {
    FvwmPacket* packet;
    int x_fd = XConnectionNumber(dpy);
    fd_set fdset;
    char buf[32];

    FD_ZERO(&fdset);
    FD_SET(fd[1], &fdset);
    FD_SET(x_fd, &fdset);

    /* process all X events first */
    while (XPending(dpy)) {
      XNextEvent(dpy,&Event);
      switch(Event.type) {
        case Expose:
          while (XCheckTypedEvent(dpy, Expose, &Event)) ;
	  RedrawWindow();
	  break;
        case KeyPress:
	  is_key_pressed = 1;
	  break;
        case ButtonPress:
	  is_button_pressed = Event.xbutton.button;
	  break;
        case KeyRelease:
	  if (is_key_pressed)
	    exit(0);
	  else
	    break;
        case ButtonRelease:
	  if (is_button_pressed)
	  {
	    if (is_button_pressed == 2 && Event.xbutton.button == 2)
	    {
	      /* select a new window when button 2 is pressed */
	      SetMessageMask(
		fd, M_CONFIGURE_WINDOW | M_WINDOW_NAME | M_ICON_NAME
		| M_RES_CLASS | M_RES_NAME | M_END_WINDOWLIST | M_CONFIG_INFO
		| M_END_CONFIG_INFO | M_SENDCONFIG);
	      SendInfo(fd,"Send_WindowList",0);
	      XDestroyWindow(dpy, main_win);
	      DestroyList();
	      fvwmlib_get_target_window(dpy, screen, MyName, &app_win, True);
	      found = 0;
	      return;
	    }
	    else
	    {
	      exit(0);
	    }
	  }
	  break;
        case ClientMessage:
	  if (Event.xclient.format==32 && Event.xclient.data.l[0]==wm_del_win)
	    exit(0);
	  break;
	case ReparentNotify:
	  sprintf(buf, "Layer 0 %d", (int)target.layer);
	  SendInfo(fd, buf, main_win);
	  SendInfo(fd, "Raise", main_win);
	  break;
	case ConfigureNotify:
	{
	  /* this only happens with transparent windows, slurp up as many
	   * events as possible before redrawing to reduce flickering */
	  XEvent event;
	  while (XCheckTypedEvent(dpy, ConfigureNotify, &event))
	  {
	    if (!event.xconfigure.send_event)
	      continue;
            Event.xconfigure.x = event.xconfigure.x;
            Event.xconfigure.y = event.xconfigure.y;
            Event.xconfigure.send_event = True;
	  }
	  /* only refresh if moved */
	  if (x != Event.xconfigure.x || y != Event.xconfigure.y)
	  {
	    x = Event.xconfigure.x;
	    y = Event.xconfigure.y;
	    /* flush any expose events */
	    while (XCheckTypedEvent(dpy, Expose, &Event)) ;
	    XClearWindow(dpy, main_win);
	    RedrawWindow();
	  }
	  break;
	}
      }
    }

    /* wait for X-event or config line */
    select( fd_width, SELECT_FD_SET_CAST &fdset, NULL, NULL, NULL );

    /* parse any dynamic config lines */
    if (FD_ISSET(fd[1], &fdset)) {
      char *tline, *token;

      packet = ReadFvwmPacket(fd[1]);
      if (packet == NULL)
	exit(0);
      if (colorset >= 0 && packet && packet->type == M_CONFIG_INFO) {
	tline = (char*)&(packet->body[3]);
	tline = GetNextToken(tline, &token);
	if (StrEquals(token, "Colorset")) {
	  /* track all colorset changes and update display if necessary */
	  if (LoadColorset(tline) == colorset) {
	    XSetForeground(dpy, gc, Colorset[colorset].fg);
	    SetWindowBackground(dpy, main_win, mysizehints.width,
				mysizehints.height,
				&Colorset[colorset], Pdepth, gc,
				True);
	    /* ask for movement events if transparent */
	    if (Colorset[colorset].pixmap == ParentRelative)
	      mw_events |= StructureNotifyMask;
	    else
	      mw_events &= ~StructureNotifyMask;
	    XSelectInput(dpy, main_win, mw_events);
	  }
	}
	else if (StrEquals(token, XINERAMA_CONFIG_STRING)) {
	  FScreenConfigureModule(tline);
	}
	free(token);
      }
    }
  }
}



/************************************************************************
 *
 * Draw the window
 *
 ***********************************************************************/
void RedrawWindow(void)
{
  int fontheight,i=0;
  struct Item *cur = itemlistRoot;

  fontheight = font->ascent + font->descent;

  while(cur != NULL)
    {
      /* first column */
#ifdef I18N_MB
      XmbDrawString(dpy, main_win, fontset, gc, 5,
		  5 + font->ascent + i * fontheight, cur->col1,
		  strlen(cur->col1));
#else
      XDrawString(dpy, main_win, gc, 5,
		  5 + font->ascent + i * fontheight, cur->col1,
		  strlen(cur->col1));
#endif
      /* second column */
#ifdef I18N_MB
      XmbDrawString(dpy, main_win, fontset, gc, 10 + max_col1,
		  5 + font->ascent + i * fontheight, cur->col2,
		  strlen(cur->col2));
#else
      XDrawString(dpy, main_win, gc, 10 + max_col1,
		  5 + font->ascent + i * fontheight, cur->col2,
		  strlen(cur->col2));
#endif
      ++i;
      cur = cur->next;
    }
}

/**************************************************************************
 *  Change the window name displayed in the title bar.
 **************************************************************************/
void change_window_name(char *str)
{
  XTextProperty name;
  XClassHint myclasshints;

  if (XStringListToTextProperty(&str,1,&name) == 0)
    {
      fprintf(stderr,"%s: cannot allocate window name",MyName);
      return;
    }
  XSetWMName(dpy,main_win,&name);
  XSetWMIconName(dpy,main_win,&name);
  XFree(name.value);
  myclasshints.res_name = str;
  myclasshints.res_class = "FvwmIdent";
  XSetClassHint(dpy,main_win,&myclasshints);
}


void DestroyList(void)
{
  struct Item *t;
  struct Item *tmp;

  for (t = itemlistRoot; t; t = tmp)
  {
    tmp = t->next;
    free(t);
  }
  itemlistRoot = NULL;
}

/**************************************************************************
*
* Add s1(string at first column) and s2(string at second column) to itemlist
*
 *************************************************************************/
void AddToList(char *s1, char* s2)
{
  int tw1, tw2;
  struct Item* item, *cur = itemlistRoot;

  tw1 = XTextWidth(font, s1, strlen(s1));
  tw2 = XTextWidth(font, s2, strlen(s2));
  max_col1 = max_col1 > tw1 ? max_col1 : tw1;
  max_col2 = max_col2 > tw2 ? max_col2 : tw2;

  item = (struct Item*)safemalloc(sizeof(struct Item));

  item->col1 = s1;
  item->col2 = s2;
  item->next = NULL;

  if (cur == NULL)
    itemlistRoot = item;
  else {
    while(cur->next != NULL)
       cur = cur->next;
    cur->next = item;
  }
  ListSize++;
}

void MakeList(void)
{
  int bw,width,height,x1,y1,x2,y2;
  char loc[20];
  static char xstr[6],ystr[6];
  /* GSFR - quick hack because the new macros depend on a prt reference  */
  struct target_struct  *targ  =  &target;

  ListSize = 0;

  bw = 2*target.border_w;
  width = target.frame_w - bw;
  height = target.frame_h - target.title_h - bw;

  sprintf(desktop, "%ld",  target.desktop);
  sprintf(layer,   "%ld",  target.layer);
  sprintf(id,      "0x%x", (unsigned int)target.id);
  sprintf(swidth,  "%d",   width);
  sprintf(sheight, "%d",   height);
  sprintf(borderw, "%ld",  target.border_w);
  sprintf(xstr,    "%ld",  target.frame_x);
  sprintf(ystr,    "%ld",  target.frame_y);

  AddToList("Name:",          target.name);
  AddToList("Icon Name:",     target.icon_name);
  AddToList("Class:",         target.class);
  AddToList("Resource:",      target.res);
  AddToList("Window ID:",     id);
  AddToList("Desk:",          desktop);
  AddToList("Layer:",         layer);
  AddToList("Width:",         swidth);
  AddToList("Height:",        sheight);
  AddToList("X (current page):",   xstr);
  AddToList("Y (current page):",   ystr);
  AddToList("Boundary Width:", borderw);

  AddToList("Sticky:",        (IS_STICKY(targ)    ? yes : no));
  AddToList("NoTitle:",       (HAS_TITLE(targ)    ? no : yes));
  AddToList("Iconified:",     (IS_ICONIFIED(targ) ? yes : no));
  AddToList("Transient:",     (IS_TRANSIENT(targ) ? yes : no));

  switch(target.gravity)
    {
    case ForgetGravity:
      AddToList("Gravity:", "Forget");
      break;
    case NorthWestGravity:
      AddToList("Gravity:", "NorthWest");
      break;
    case NorthGravity:
      AddToList("Gravity:", "North");
      break;
    case NorthEastGravity:
      AddToList("Gravity:", "NorthEast");
      break;
    case WestGravity:
      AddToList("Gravity:", "West");
      break;
    case CenterGravity:
      AddToList("Gravity:", "Center");
      break;
    case EastGravity:
      AddToList("Gravity:", "East");
      break;
    case SouthWestGravity:
      AddToList("Gravity:", "SouthWest");
      break;
    case SouthGravity:
      AddToList("Gravity:", "South");
      break;
    case SouthEastGravity:
      AddToList("Gravity:", "SouthEast");
      break;
    case StaticGravity:
      AddToList("Gravity:", "Static");
      break;
    default:
      AddToList("Gravity:", "Unknown");
      break;
    }
  x1 = target.frame_x;
  if(x1 < 0)
    x1 = 0;
  x2 = DisplayWidth(dpy,screen) - x1 - target.frame_w;
  if(x2 < 0)
    x2 = 0;
  y1 = target.frame_y;
  if(y1 < 0)
    y1 = 0;
  y2 = DisplayHeight(dpy,screen) - y1 -  target.frame_h;
    if(y2 < 0)
    y2 = 0;
  width = (width - target.base_w)/target.width_inc;
  height = (height - target.base_h)/target.height_inc;

  sprintf(loc,"%dx%d",width,height);
  strcpy(geometry, loc);

  if ((target.gravity == EastGravity) ||(target.gravity == NorthEastGravity)||
      (target.gravity == SouthEastGravity))
    sprintf(loc,"-%d",x2);
  else
    sprintf(loc,"+%d",x1);
  strcat(geometry, loc);

  if((target.gravity == SouthGravity)||(target.gravity == SouthEastGravity)||
     (target.gravity == SouthWestGravity))
    sprintf(loc,"-%d",y2);
  else
    sprintf(loc,"+%d",y1);
  strcat(geometry, loc);
  AddToList("Geometry:", geometry);

  {
    Atom *protocols = NULL, *ap;
    Atom _XA_WM_TAKE_FOCUS = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
    XWMHints *wmhintsp = XGetWMHints(dpy,target.id);
    int i,n;
    Boolean HasTakeFocus=False,InputField=True;
    char *focus_policy="",*ifstr="",*tfstr="";

    if (wmhintsp)
    {
      InputField=wmhintsp->input;
      ifstr=InputField?"True":"False";
      XFree(wmhintsp);
    }
    else
    {
      ifstr="XWMHints missing";
    }
    if (XGetWMProtocols(dpy,target.id,&protocols,&n))
    {
      for (i = 0, ap = protocols; i < n; i++, ap++)
      {
        if (*ap == (Atom)_XA_WM_TAKE_FOCUS)
          HasTakeFocus = True;
      }
      tfstr=HasTakeFocus?"Present":"Absent";
      XFree(protocols);
    }
    else
    {
      tfstr="XGetWMProtocols failed";
    }
    if (HasTakeFocus)
    {
      if (InputField)
      {
        focus_policy = "Locally Active";
      }
      else
      {
        focus_policy = "Globally Active";
      }
    }
    else
    {
      if (InputField)
      {
        focus_policy = "Passive";
      }
      else
      {
        focus_policy = "No Input";
      }
    }
    AddToList("Focus Policy:",focus_policy);
    AddToList("  - Input Field:",ifstr);
    AddToList("  - WM_TAKE_FOCUS:",tfstr);
    {
      long supplied_return;             /* flags, hints that were supplied */
      int getrc;
      XSizeHints *size_hints = XAllocSizeHints(); /* the size hints */
      if ((getrc = XGetWMSizeHints(dpy,target.id, /* get size hints */
                          size_hints,    /* Hints */
                          &supplied_return,
                          XA_WM_ZOOM_HINTS))) {
        if (supplied_return & PAspect) { /* if window has a aspect ratio */
          sprintf(mymin_aspect, "%d/%d", size_hints->min_aspect.x,
                  size_hints->min_aspect.y);
          AddToList("Minimum aspect ratio:",mymin_aspect);
          sprintf(max_aspect, "%d/%d", size_hints->max_aspect.x,
                  size_hints->max_aspect.y);
          AddToList("Maximum aspect ratio:",max_aspect);
        } /* end aspect ratio */
        XFree(size_hints);
      } /* end getsizehints worked */
    }
  }
}


syntax highlighted by Code2HTML, v. 0.9.1