/* FvwmWinList Module for Fvwm.
 *
 *  Copyright 1994,  Mike Finger (mfinger@mermaid.micro.umn.edu or
 *                               Mike_Finger@atk.com)
 *
 * The author makes not guarantees or warantees, either express or
 * implied.  Feel free to use any contained here for any purpose, as long
 * and this and any other applicible copyrights are kept intact.

 * The functions in this source file that are based on part of the FvwmIdent
 * module for Fvwm are noted by a small copyright atop that function, all others
 * are copyrighted by Mike Finger.  For those functions modified/used, here is
 *  the full, origonal copyright:
 *
 * 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.

 * Modifications Done to Add Pixmaps, focus highlighting and Current Desk Only
 * By Don Mahurin, 1996, Some of this Code was taken from FvwmTaskBar

 * Bug Notes:(Don Mahurin)

 * Moving a window doesnt send M_CONFIGURE, as I thought it should. Desktop
 * for that button is not updated.

*/

#define TRUE  1
#define FALSE 0

#ifndef NO_CONSOLE
#define NO_CONSOLE
#endif

#define YES "Yes"
#define NO  "No"

#include "config.h"

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

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

#include <unistd.h>
#include <ctype.h>

#ifdef HAVE_SYS_BSDTYPES_H
#include <sys/bsdtypes.h> /* Saul */
#endif

#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 "../../fvwm/module.h"

#include "FvwmWinList.h"
#include "ButtonArray.h"
#include "List.h"
#include "Colors.h"
#include "Mallocs.h"

#define GRAB_EVENTS (ButtonPressMask|ButtonReleaseMask|ButtonMotionMask|EnterWindowMask|LeaveWindowMask)

#define SomeButtonDown(a) ((a)&Button1Mask||(a)&Button2Mask||(a)&Button3Mask)

/* File type information */
FILE *console;
int fd_width;
int Fvwm_fd[2];
int x_fd;

/* X related things */
Display *dpy;
Window Root, win;
int screen,d_depth,ScreenWidth,ScreenHeight;
Pixel back[MAX_COLOUR_SETS], fore[MAX_COLOUR_SETS];
GC  graph[MAX_COLOUR_SETS],shadow[MAX_COLOUR_SETS],hilite[MAX_COLOUR_SETS];
GC  background[MAX_COLOUR_SETS];
XFontStruct *ButtonFont;
int fontheight;
static Atom wm_del_win;
Atom MwmAtom = None;

/* Module related information */
char *Module;
int WindowIsUp=0,win_width=5,win_height=5,win_grav,win_x,win_y,win_title,win_border;
int Clength,Transient=0,Pressed=0,ButPressed,Checked=0;
int MinWidth=DEFMINWIDTH,MaxWidth=DEFMAXWIDTH;
ButtonArray buttons;
List windows;
char *ClickAction[3]={"Iconify -1,Raise","Iconify","Lower"},*EnterAction,
      *BackColor[MAX_COLOUR_SETS] = { "white" },
      *ForeColor[MAX_COLOUR_SETS] = { "black" },
      *geometry="";
char *font_string = "fixed";
int UseSkipList=0,Anchor=1,UseIconNames=0,LeftJustify=0,TruncateLeft=0,ShowFocus=1;

long CurrentDesk = 0;
int ShowCurrentDesk = 0;

static volatile sig_atomic_t isTerminated = False;

static RETSIGTYPE TerminateHandler(int sig);

/***************************************************************************
 * TerminateHandler - reentrant signal handler that ends the main event loop
 ***************************************************************************/
static RETSIGTYPE TerminateHandler(int sig)
{
  isTerminated = True;
}

int ItemCountD(List *list )
{
	if(!ShowCurrentDesk)
		return ItemCount(list);
	/* else */
	return ItemCountDesk(list, CurrentDesk);

}

/******************************************************************************
  Main - Setup the XConnection,request the window list and loop forever
    Based on main() from FvwmIdent:
      Copyright 1994, Robert Nation and Nobutaka Suzuki.
******************************************************************************/
int main(int argc, char **argv)
{
  char *temp, *s;
#ifdef HAVE_SIGACTION
  struct sigaction  sigact;
#endif

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

  /* Setup my name */
  Module = safemalloc(strlen(temp)+2);
  strcpy(Module,"*");
  strcat(Module, temp);
  Clength = strlen(Module);

  /* Open the console for messages */
  OpenConsole();

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


  if ((argc==7)&&(!strcasecmp(argv[6],"Transient"))) Transient=1;

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

#ifdef HAVE_SIGACTION
#ifdef SA_INTERRUPT
  sigact.sa_flags = SA_INTERRUPT;
#else
  sigact.sa_flags = 0;
#endif
  sigemptyset(&sigact.sa_mask);
  sigact.sa_handler = TerminateHandler;
  sigaction(SIGPIPE, &sigact, NULL);
  sigaction(SIGTERM, &sigact, NULL);  
#else
  signal(SIGPIPE, TerminateHandler);
  signal(SIGTERM, TerminateHandler);
#ifdef HAVE_SIGINTERRUPT
  siginterrupt(SIGPIPE, True);
  siginterrupt(SIGTERM, True);
#endif
#endif

  /* Parse the config file */
  ParseConfig();

  /* Setup the XConnection */
  StartMeUp();
  XSetErrorHandler(ErrorHandler);

  InitPictureCMap(dpy, Root);

  InitArray(&buttons,0,0,win_width, fontheight+6);
  InitList(&windows);

  fd_width = GetFdWidth();

  /* Request a list of all windows,
   * wait for ConfigureWindow packets */

  SetMessageMask(Fvwm_fd,M_CONFIGURE_WINDOW | M_RES_CLASS | M_RES_NAME |
                 M_ADD_WINDOW | M_DESTROY_WINDOW | M_ICON_NAME |
                 M_DEICONIFY | M_ICONIFY | M_END_WINDOWLIST |
                 M_NEW_DESK | M_NEW_PAGE | M_FOCUS_CHANGE | M_WINDOW_NAME |
#ifdef MINI_ICONS
                 M_MINI_ICON |
#endif
                 M_STRING);

  SendFvwmPipe("Send_WindowList",0);

  /* Recieve all messages from Fvwm */
  atexit(ShutMeDown);
  MainEventLoop();
  return 0;
}


/******************************************************************************
  MainEventLoop -  Read and redraw until we die, blocking when can't read
******************************************************************************/
void MainEventLoop(void)
{
fd_set readset;

  while( !isTerminated ) {
    FD_ZERO(&readset);
    FD_SET(Fvwm_fd[1],&readset);
    FD_SET(x_fd,&readset);

    /* This code restyled after FvwmIconMan, which is simpler.
     * The biggest advantage over the original approach is
     * having one fewer select statements
     */
    XFlush(dpy);
    if (select(fd_width,SELECT_TYPE_ARG234 &readset,NULL,NULL,NULL) > 0) {

      if (FD_ISSET(x_fd,&readset) || XPending(dpy)) LoopOnEvents();
      if (FD_ISSET(Fvwm_fd[1],&readset)) ReadFvwmPipe();

    }

  } /* while */
}

/******************************************************************************
  ReadFvwmPipe - Read a single message from the pipe from Fvwm
    Originally Loop() from FvwmIdent:
      Copyright 1994, Robert Nation and Nobutaka Suzuki.
******************************************************************************/
void ReadFvwmPipe(void)
{
  unsigned long header[HEADER_SIZE],*body;

  if(ReadFvwmPacket(Fvwm_fd[1],header,&body) > 0)
    {
      ProcessMessage(header[1],body);
      free(body);
    }
}

/******************************************************************************
  ProcessMessage - Process the message coming from Fvwm
    Skeleton based on processmessage() from FvwmIdent:
      Copyright 1994, Robert Nation and Nobutaka Suzuki.
******************************************************************************/
void ProcessMessage(unsigned long type,unsigned long *body)
{
  int redraw=0,i;
  long flags;
  char *name,*string;
  static int current_focus=-1;

  FvwmPicture p;

  switch(type)
  {
    case M_ADD_WINDOW:
    case M_CONFIGURE_WINDOW:
      if ((i = FindItem(&windows,body[0]))!=-1)
      {
	if(UpdateItemDesk(&windows, i, body[7]) > 0)
        {
          AdjustWindow();
          RedrawWindow(1);
        }
	break;
      }

      if (!(body[8]&WINDOWLISTSKIP) || !UseSkipList)
        AddItem(&windows,body[0],body[8], body[7] /* desk */);
      break;
    case M_DESTROY_WINDOW:
      if ((i=DeleteItem(&windows,body[0]))==-1) break;
      RemoveButton(&buttons,i);
      if (WindowIsUp)
        AdjustWindow();

      redraw=1;
      break;

#ifdef MINI_ICONS
  case M_MINI_ICON:
    if ((i=FindItem(&windows,body[0]))==-1) break;

    if (UpdateButton(&buttons,i,NULL,-1)!=-1)
    {
      p.width = body[3];
      p.height = body[4];
      p.depth = body[5];
      p.picture = body[6];
      p.mask = body[7];

      UpdateButtonPicture(&buttons, i, &p);
      redraw = 0;
    }
    break;
#endif

    case M_WINDOW_NAME:
    case M_ICON_NAME:
      if ((type==M_ICON_NAME && !UseIconNames) ||
          (type==M_WINDOW_NAME && UseIconNames)) break;
      if ((i=UpdateItemName(&windows,body[0],(char *)&body[3]))==-1) break;
      string=(char *)&body[3];
      name=makename(string,ItemFlags(&windows,body[0]));
      if (UpdateButton(&buttons,i,name,-1)==-1)
      {
	AddButton(&buttons, name, NULL, 1);
	UpdateButtonSet(&buttons,i,ItemFlags(&windows,body[0])&ICONIFIED?1:0);
        UpdateButtonDesk(&buttons,i,ItemDesk(&windows, body[0]));
      }
      free(name);
      if (WindowIsUp) AdjustWindow();
      redraw=1;
      break;
    case M_DEICONIFY:
    case M_ICONIFY:
      if ((i=FindItem(&windows,body[0]))==-1) break;
      flags=ItemFlags(&windows,body[0]);
      if (type==M_DEICONIFY && !(flags&ICONIFIED)) break;
      if (type==M_ICONIFY && flags&ICONIFIED) break;
      flags^=ICONIFIED;
      UpdateItemFlags(&windows,body[0],flags);
      string=ItemName(&windows,i);
      name=makename(string,flags);
      if (UpdateButton(&buttons,i,name,-1)!=-1) redraw=1;
      if (i!=current_focus||(flags&ICONIFIED))
        if (UpdateButtonSet(&buttons,i,(flags&ICONIFIED) ? 1 : 0)!=-1) redraw=1;
      free(name);
      break;
    case M_FOCUS_CHANGE:
      if ((i=FindItem(&windows,body[0]))!=-1)
      {
        flags=ItemFlags(&windows,body[0]);
        UpdateItemFlags(&windows,body[0],flags);
        RadioButton(&buttons,i);
      }
      else
        RadioButton(&buttons,-1);
      redraw = 1;
      break;
    case M_END_WINDOWLIST:
      if (!WindowIsUp) MakeMeWindow();
      redraw = 1;
      break;
    case M_NEW_DESK:
      CurrentDesk = body[0];
      if(ShowCurrentDesk)
      {
        AdjustWindow();
        RedrawWindow(1);
      }
      break;
    case M_NEW_PAGE:
      break;
  }

  if (redraw && WindowIsUp==1) RedrawWindow(0);
}

/******************************************************************************
  SendFvwmPipe - Send a message back to fvwm
    Based on SendInfo() from FvwmIdent:
      Copyright 1994, Robert Nation and Nobutaka Suzuki.
******************************************************************************/
void SendFvwmPipe(char *message,unsigned long window)
{
  int w;
  char *hold,*temp,*temp_msg;

  hold=message;

  while(1)
  {
    temp=strchr(hold,',');
    if (temp!=NULL)
    {
      temp_msg=safemalloc(temp-hold+1);
      strncpy(temp_msg,hold,(temp-hold));
      temp_msg[(temp-hold)]='\0';
      hold=temp+1;
    } else temp_msg=hold;

    write(Fvwm_fd[0],&window, sizeof(unsigned long));

    w=strlen(temp_msg);
    write(Fvwm_fd[0],&w,sizeof(int));
    write(Fvwm_fd[0],temp_msg,w);

    /* keep going */
    w=1;
    write(Fvwm_fd[0],&w,sizeof(int));

    if(temp_msg!=hold) free(temp_msg);
    else break;
  }
}

/***********************************************************************
  Detected a broken pipe - time to exit
    Based on DeadPipe() from FvwmIdent:
      Copyright 1994, Robert Nation and Nobutaka Suzuki.
 **********************************************************************/
void DeadPipe(int nonsense)
{
  /* ShutMeDown(1); */
  /*
   * do not call ShutMeDown, it may make X calls which are not allowed
   * in a signal hander.
   *
   * THIS IS NO LONGER A SIGNAL HANDLER - we may now shut down gracefully
   * NOTE: ShutMeDown will now be called automatically by exit().
   */
  exit(1);
}

/******************************************************************************
  WaitForExpose - Used to wait for expose event so we don't draw too early
******************************************************************************/
void WaitForExpose(void)
{
  XEvent Event;

  while(1)
  {
    /*
     * Temporary solution to stop the process blocking
     * in XNextEvent once we have been asked to quit.
     * There is still a small race condition between
     * checking the flag and calling the X-Server, but
     * we can fix that ...
     */
    if (isTerminated)
    {
      /* Just exit - the installed exit-procedure will clean up */
      exit(0);
    }
    /**/

    XNextEvent(dpy,&Event);
    if (Event.type==Expose)
    {
      if (Event.xexpose.count==0) break;
    }
  }
}

/******************************************************************************
  RedrawWindow - Update the needed lines and erase any old ones
******************************************************************************/
void RedrawWindow(int force)
{
  DrawButtonArray(&buttons, force);
  if (XQLength(dpy) && !force) LoopOnEvents();
}

/******************************************************************************
  ConsoleMessage - Print a message on the console.  Works like printf.
******************************************************************************/
void ConsoleMessage(const char *fmt, ...)
{
#ifndef NO_CONSOLE
  va_list args;
  FILE *filep;

  if (console==NULL) filep=stderr;
  else filep=console;
  va_start(args,fmt);
  vfprintf(filep,fmt,args);
  va_end(args);
#endif
}

/******************************************************************************
  OpenConsole - Open the console as a way of sending messages
******************************************************************************/
int OpenConsole(void)
{
#ifndef NO_CONSOLE
  if ((console=fopen("/dev/console","w"))==NULL) {
    fprintf(stderr,"%s: cannot open console\n",Module);
    return 0;
  }
#endif
  return 1;
}

/******************************************************************************
  ParseConfig - Parse the configuration file fvwm to us to use
    Based on part of main() from FvwmIdent:
      Copyright 1994, Robert Nation and Nobutaka Suzuki.
******************************************************************************/
void ParseConfig(void)
{
  char *tline;

  GetConfigLine(Fvwm_fd,&tline);
  while(tline != (char *)0)
    {
      if(strlen(tline)>1)
	{
	  if(strncasecmp(tline, CatString3(Module, "Font",""),Clength+4)==0)
	    CopyString(&font_string,&tline[Clength+4]);
	  else if(strncasecmp(tline,CatString3(Module,"Fore",""), Clength+4)==0)
	    CopyString(&ForeColor[0],&tline[Clength+4]);
	  else if(strncasecmp(tline,CatString3(Module,"IconFore",""), Clength+8)==0)
	    CopyString(&ForeColor[1],&tline[Clength+8]);
	  else if(strncasecmp(tline,CatString3(Module,"FocusFore",""), Clength+9)==0)
	    {
	      CopyString(&ForeColor[2],&tline[Clength+9]);
	      CopyString(&ForeColor[3],&tline[Clength+9]);
	    }
	  else if(strncasecmp(tline,CatString3(Module, "Geometry",""), Clength+8)==0)
	    CopyString(&geometry,&tline[Clength+8]);
	  else if(strncasecmp(tline,CatString3(Module, "Back",""), Clength+4)==0)
	    CopyString(&BackColor[0],&tline[Clength+4]);
	  else if(strncasecmp(tline,CatString3(Module,"IconBack",""), Clength+8)==0)
	    CopyString(&BackColor[1],&tline[Clength+8]);
	  else if(strncasecmp(tline,CatString3(Module,"FocusBack",""), Clength+9)==0)
	    {
	      CopyString(&BackColor[2],&tline[Clength+9]);
	      CopyString(&BackColor[3],&tline[Clength+9]);
	    }
	  else if(strncasecmp(tline,CatString3(Module, "NoAnchor",""),
				Clength+8)==0) Anchor=0;
	  else if(strncasecmp(tline,CatString3(Module, "Action",""), Clength+6)==0)
	    LinkAction(&tline[Clength+6]);
	  else if(strncasecmp(tline,CatString3(Module, "UseSkipList",""),
				Clength+11)==0) UseSkipList=1;
	  else if(strncasecmp(tline,CatString3(Module, "UseIconNames",""),
				Clength+12)==0) UseIconNames=1;
	  else if(strncasecmp(tline,CatString3(Module, "ShowCurrentDesk",""),
				Clength+15)==0) ShowCurrentDesk=1;
	  else if(strncasecmp(tline,CatString3(Module, "LeftJustify",""),
				Clength+11)==0) LeftJustify=1;
	  else if(strncasecmp(tline,CatString3(Module, "TruncateLeft",""),
				Clength+12)==0) TruncateLeft=1;
          else if(strncasecmp(tline,CatString3(Module, "MinWidth",""),
                                Clength+8)==0) MinWidth=atoi(&tline[Clength+8]);
          else if(strncasecmp(tline,CatString3(Module, "MaxWidth",""),
                                Clength+8)==0) MaxWidth=atoi(&tline[Clength+8]);
	  else if(strncasecmp(tline,CatString3(Module, "DontDepressFocus",""),
				Clength+16)==0) ShowFocus=0;
	}
      GetConfigLine(Fvwm_fd,&tline);
    }
}

/******************************************************************************
  LoopOnEvents - Process all the X events we get
******************************************************************************/
void LoopOnEvents(void)
{
  int num;
  char buffer[10];
  XEvent Event;
  Window dummyroot,dummychild;
  int x,x1,y,y1;
  unsigned int dummy1;

  if (Transient && !Checked)
  {
    XQueryPointer(dpy,win,&dummyroot,&dummychild,&x1,&y1,&x,&y,&dummy1);
    num=WhichButton(&buttons,x,y);
    if (num!=-1)
    {
      Pressed=1;
      ButPressed=num;
      SwitchButton(&buttons,num);
    } else Pressed=0;
    Checked=1;
  }

  while(XPending(dpy))
  {
    XNextEvent(dpy,&Event);

    switch(Event.type)
    {
      case ButtonRelease:
        if (Pressed)
        {
          num=WhichButton(&buttons,Event.xbutton.x,Event.xbutton.y);
          if (num!=-1)
          {
            SendFvwmPipe(ClickAction[(Transient) ? 0:Event.xbutton.button-1],
              ItemID(&windows,num));
            SwitchButton(&buttons,num);
          }
        }
        if (Transient) exit(0);
        Pressed=0;
        ButPressed=-1;
        break;
      case ButtonPress:
        num=WhichButton(&buttons,Event.xbutton.x,Event.xbutton.y);
        if (num != -1)
        {
          SwitchButton(&buttons,num);
          ButPressed=num;
        } else ButPressed=-1;
        Pressed=1;
        break;
      case Expose:
        if (Event.xexpose.count==0)
          RedrawWindow(1);
        break;
      case KeyPress:
        num=XLookupString(&Event.xkey,buffer,10,NULL,0);
        if (num==1)
        {
          if (buffer[0]=='q' || buffer[0]=='Q') exit(0);
          else if (buffer[0]=='i' || buffer[0]=='I') PrintList(&windows);
          else if (buffer[0]=='b' || buffer[0]=='B') PrintButtons(&buttons);
        }
        break;
      case ClientMessage:
        if ((Event.xclient.format==32) && (Event.xclient.data.l[0]==wm_del_win))
          exit(0);
      case EnterNotify:
        if (!SomeButtonDown(Event.xcrossing.state)) break;
        num=WhichButton(&buttons,Event.xcrossing.x,Event.xcrossing.y);
        if (num!=-1)
        {
          SwitchButton(&buttons,num);
          ButPressed=num;
        } else ButPressed=-1;
        Pressed=1;
        break;
      case LeaveNotify:
        if (!SomeButtonDown(Event.xcrossing.state)) break;
        if (ButPressed!=-1) SwitchButton(&buttons,ButPressed);
        Pressed=0;
        break;
      case MotionNotify:
        if (!Pressed) break;
        num=WhichButton(&buttons,Event.xmotion.x,Event.xmotion.y);
        if (num==ButPressed) break;
        if (ButPressed!=-1) SwitchButton(&buttons,ButPressed);
        if (num!=-1)
        {
          SwitchButton(&buttons,num);
          ButPressed=num;
        }
        else ButPressed=-1;

        break;
    }
  }
}


/******************************************************************************
  find_frame_window - looks for ancestor that is a child of the root
  Cribbed from FvwmIconMan/x.c - maybe should be in a library
  Returns the root-child and fills in off_x, off_y to give offset
******************************************************************************/
Window find_frame_window (Window win, int *off_x, int *off_y)
{
  Window root, parent, *junkw;
  int junki;
  XWindowAttributes attr;

  while (1) {
    XQueryTree (dpy, win, &root, &parent, &junkw, &junki);
    if (junkw)
      XFree (junkw);
    if (parent == root)
      break;
    XGetWindowAttributes (dpy, win, &attr);
    *off_x += attr.x + attr.border_width;
    *off_y += attr.y + attr.border_width;
    win = parent;
  }

  return win;
}

/******************************************************************************
  AdjustWindow - Resize the window according to maxwidth by number of buttons
******************************************************************************/
void AdjustWindow(void)
{
  int new_width=0,new_height=0,tw,i,total,off_x,off_y;
  char *temp;
  Window frame;
  XWindowAttributes win_attr, frame_attr;

  total = ItemCountD(&windows );
  if (!total)
  {
    if (WindowIsUp==1)
    {
      XUnmapWindow(dpy,win);
      WindowIsUp=2;
    }
    return;
  }
  for(i=0;i<total;i++)
  {
    temp=ItemName(&windows,i);
    if(temp != NULL)
    {
	tw=10+XTextWidth(ButtonFont,temp,strlen(temp));
	tw+=XTextWidth(ButtonFont,"()",2);

#ifdef MINI_ICONS
	tw+=14; /* for title icon */ /* Magic Number ? */
#endif

	new_width=max(new_width,tw);
    }
  }
  new_width=max(new_width, MinWidth);
  new_width=min(new_width, MaxWidth);
  new_height=(total*(fontheight+6+1));
  if (WindowIsUp && (new_height!=win_height  || new_width!=win_width))
  {
    if (Anchor)
    {
      off_x = off_y = 0;
      MyXGrabServer(dpy);
      frame = find_frame_window(win, &off_x, &off_y);
      XGetWindowAttributes(dpy, frame, &frame_attr);
      XGetWindowAttributes(dpy, win, &win_attr);
      win_x = frame_attr.x + frame_attr.border_width + off_x;
      win_y = frame_attr.y + frame_attr.border_width + off_y;

      if (win_grav == SouthEastGravity || win_grav == NorthEastGravity)
        win_x += win_attr.width - new_width;
      if (win_grav == SouthEastGravity || win_grav == SouthWestGravity)
        win_y += win_attr.height - new_height;

      XMoveResizeWindow(dpy, win, win_x, win_y, new_width, new_height);
      MyXUngrabServer(dpy);
    }
    else
      XResizeWindow(dpy, win, new_width, new_height);

  }
  UpdateArray(&buttons,-1,-1,new_width,-1);
  if (new_height>0) win_height = new_height;
  if (new_width>0) win_width = new_width;
  if (WindowIsUp==2)
  {
    XMapWindow(dpy,win);
    WindowIsUp=1;
    WaitForExpose();
  }
}

/******************************************************************************
  makename - Based on the flags return me '(name)' or 'name'
******************************************************************************/
char *makename(const char *string,long flags)
{
char *ptr;
  ptr=safemalloc(strlen(string)+3);
  *ptr = '\0';
  if (flags&ICONIFIED) strcpy(ptr,"(");
  strcat(ptr,string);
  if (flags&ICONIFIED) strcat(ptr,")");
  return ptr;
}

/******************************************************************************
  LinkAction - Link an response to a users action
******************************************************************************/
void LinkAction(char *string)
{
char *temp;
  temp=string;
  while(isspace(*temp)) temp++;
  if(strncasecmp(temp, "Click1", 6)==0)
    CopyString(&ClickAction[0],&temp[6]);
  else if(strncasecmp(temp, "Click2", 6)==0)
    CopyString(&ClickAction[1],&temp[6]);
  else if(strncasecmp(temp, "Click3", 6)==0)
    CopyString(&ClickAction[2],&temp[6]);
  else if(strncasecmp(temp, "Enter", 5)==0)
    CopyString(&EnterAction,&temp[5]);
}

/******************************************************************************
  MakeMeWindow - Create and setup the window we will need
******************************************************************************/
void MakeMeWindow(void)
{
  XSizeHints hints;
  XGCValues gcval;
  unsigned long gcmask;
  unsigned int dummy1, dummy2;
  int x, y, ret, count;
  Window dummyroot, dummychild;
  int i;


  if ((count = ItemCountD(&windows))==0 && Transient) exit(0);
  AdjustWindow();

  hints.width=win_width;
  hints.height=win_height;
  hints.win_gravity=NorthWestGravity;
  hints.flags=PSize|PWinGravity|PResizeInc;
  hints.width_inc=0;
  hints.height_inc=0;

  if (geometry!= NULL)
  {
    ret=XParseGeometry(geometry,&x,&y,&dummy1,&dummy2);

    if (ret&XValue && ret &YValue)
    {
      hints.x=x;
      if (ret&XNegative)
        hints.x+=XDisplayWidth(dpy,screen)-win_width;

      hints.y=y;
      if (ret&YNegative)
        hints.y+=XDisplayHeight(dpy,screen)-win_height;

      hints.flags|=USPosition;
    }

    if (ret&XNegative)
    {
      if (ret&YNegative) hints.win_gravity=SouthEastGravity;
      else  hints.win_gravity=NorthEastGravity;
    }
    else
    {
      if (ret&YNegative) hints.win_gravity=SouthWestGravity;
      else  hints.win_gravity=NorthWestGravity;
    }

  }

  if (Transient)
  {
    XQueryPointer(dpy,Root,&dummyroot,&dummychild,&hints.x,&hints.y,&x,&y,&dummy1);
    hints.win_gravity=NorthWestGravity;
    hints.flags |= USPosition;
  }
  win_grav=hints.win_gravity;
  win_x=hints.x;
  win_y=hints.y;


  for (i = 0; i != MAX_COLOUR_SETS; i++)
  if(d_depth < 2)
  {
    back[i] = GetColor("white");
    fore[i] = GetColor("black");
  }
  else
  {
    back[i] = GetColor(BackColor[i] == NULL ? BackColor[0] : BackColor[i]);
    fore[i] = GetColor(ForeColor[i] == NULL ? ForeColor[0] : ForeColor[i]);
  }

  win=XCreateSimpleWindow(dpy,Root,hints.x,hints.y,hints.width,hints.height,0,
    fore[0],back[0]);

  wm_del_win=XInternAtom(dpy,"WM_DELETE_WINDOW",False);
  XSetWMProtocols(dpy,win,&wm_del_win,1);

  XSetWMNormalHints(dpy,win,&hints);

  if (!Transient)
  {
    XGrabButton(dpy,1,AnyModifier,win,True,GRAB_EVENTS,GrabModeAsync,
      GrabModeAsync,None,None);
    XGrabButton(dpy,2,AnyModifier,win,True,GRAB_EVENTS,GrabModeAsync,
      GrabModeAsync,None,None);
    XGrabButton(dpy,3,AnyModifier,win,True,GRAB_EVENTS,GrabModeAsync,
      GrabModeAsync,None,None);
    SetMwmHints(MWM_DECOR_ALL|MWM_DECOR_RESIZEH|MWM_DECOR_MAXIMIZE|MWM_DECOR_MINIMIZE,
      MWM_FUNC_ALL|MWM_FUNC_RESIZE|MWM_FUNC_MAXIMIZE|MWM_FUNC_MINIMIZE,
      MWM_INPUT_MODELESS);
  }
  else
  {
    SetMwmHints(0,MWM_FUNC_ALL,MWM_INPUT_MODELESS);
  }

  for (i = 0; i != MAX_COLOUR_SETS; i++)
  {
    gcval.foreground=fore[i];
    gcval.background=back[i];
    gcval.font=ButtonFont->fid;
    gcmask=GCForeground|GCBackground|GCFont;
    graph[i]=XCreateGC(dpy,Root,gcmask,&gcval);

    if(d_depth < 2)
      gcval.foreground=GetShadow(fore[i]);
    else
      gcval.foreground=GetShadow(back[i]);
    gcval.background=back[i];
    gcmask=GCForeground|GCBackground;
    shadow[i]=XCreateGC(dpy,Root,gcmask,&gcval);

    gcval.foreground=GetHilite(back[i]);
    gcval.background=back[i];
    gcmask=GCForeground|GCBackground;
    hilite[i]=XCreateGC(dpy,Root,gcmask,&gcval);

    gcval.foreground=back[i];
    gcmask=GCForeground;
    background[i]=XCreateGC(dpy,Root,gcmask,&gcval);
  }

  XSelectInput(dpy,win,(ExposureMask | KeyPressMask));

  ChangeWindowName(&Module[1]);

  if (ItemCountD(&windows) > 0)
  {
    XMapRaised(dpy,win);
    WaitForExpose();
    WindowIsUp=1;
  } else WindowIsUp=2;

  if (Transient)
  {
    if ( XGrabPointer(dpy,win,True,GRAB_EVENTS,GrabModeAsync,GrabModeAsync,
      None,None,CurrentTime)!=GrabSuccess) exit(1);
    XQueryPointer(dpy,Root,&dummyroot,&dummychild,&hints.x,&hints.y,&x,&y,&dummy1);
    if (!SomeButtonDown(dummy1)) exit(0);
  }

}

/******************************************************************************
  StartMeUp - Do X initialization things
******************************************************************************/
void StartMeUp(void)
{
  if (!(dpy = XOpenDisplay("")))
  {
    fprintf(stderr,"%s: can't open display %s", Module,
      XDisplayName(""));
    exit (1);
  }
  x_fd = XConnectionNumber(dpy);
  screen= DefaultScreen(dpy);
  Root = RootWindow(dpy, screen);
  d_depth = DefaultDepth(dpy, screen);

  ScreenHeight = DisplayHeight(dpy,screen);
  ScreenWidth = DisplayWidth(dpy,screen);

  if ((ButtonFont=XLoadQueryFont(dpy,font_string))==NULL)
  {
    if ((ButtonFont=XLoadQueryFont(dpy,"fixed"))==NULL) exit(1);
  }

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

  win_width=XTextWidth(ButtonFont,"XXXXXXXXXXXXXXX",10);

}

/******************************************************************************
  ShutMeDown - Do X client cleanup
******************************************************************************/
void ShutMeDown(void)
{
  FreeList(&windows);
  FreeAllButtons(&buttons);
/*  XFreeGC(dpy,graph);*/
  if (WindowIsUp) XDestroyWindow(dpy,win);
  XCloseDisplay(dpy);
}

/******************************************************************************
  ChangeWindowName - Self explanitory
    Original work from FvwmIdent:
      Copyright 1994, Robert Nation and Nobutaka Suzuki.
******************************************************************************/
void ChangeWindowName(char *str)
{
XTextProperty name;
  if (XStringListToTextProperty(&str,1,&name) == 0) {
    fprintf(stderr,"%s: cannot allocate window name.\n",Module);
    return;
  }
  XSetWMName(dpy,win,&name);
  XSetWMIconName(dpy,win,&name);
  XFree(name.value);
}

/**************************************************************************
 *
 * Sets mwm hints
 *
 *************************************************************************/
/*
 *  Now, if we (hopefully) have MWW - compatible window manager ,
 *  say, mwm, ncdwm, or else, we will set useful decoration style.
 *  Never check for MWM_RUNNING property.May be considered bad.
 */

void SetMwmHints(unsigned int value, unsigned int funcs, unsigned int input)
{
PropMwmHints prop;

  if (MwmAtom==None)
    {
      MwmAtom=XInternAtom(dpy,"_MOTIF_WM_HINTS",False);
    }
  if (MwmAtom!=None)
    {
      /* sh->mwm.decorations contains OR of the MWM_DECOR_XXXXX */
      prop.decorations= value;
      prop.functions = funcs;
      prop.inputMode = input;
      prop.flags = MWM_HINTS_DECORATIONS| MWM_HINTS_FUNCTIONS | MWM_HINTS_INPUT_MODE;

      /* HOP - LA! */
      XChangeProperty (dpy,win,
		       MwmAtom, MwmAtom,
		       32, PropModeReplace,
		       (unsigned char *)&prop,
		       PROP_MWM_HINTS_ELEMENTS);
    }
}

/************************************************************************
  X Error Handler
************************************************************************/
int ErrorHandler(Display *d, XErrorEvent *event)
{
    char errmsg[256];
    
    XGetErrorText(d, event->error_code, errmsg, sizeof(errmsg));
    ConsoleMessage("%s failed request: %s\n", Module, errmsg);
    ConsoleMessage("Major opcode: 0x%x, resource id: 0x%x\n",
	event->request_code, (unsigned int)event->resourceid);
    return 0;
}

