/* $XConsortium: comm.c,v 1.23 94/04/17 20:38:51 dave Exp $ */
/*

Copyright (c) 1990  X Consortium

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 X CONSORTIUM 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.

Except as contained in this notice, the name of the X Consortium shall
not be used in advertising or otherwise to promote the sale, use or
other dealings in this Software without prior written authorization
from the X Consortium.

*/


/*
 * This file contains the code to communicate with the client that is
 * being edited.
 */

#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>	/* Get standard string definitions. */
#include <X11/Xatom.h>
#include <X11/cursorfont.h>	/* For crosshair cursor. */
#include <X11/Xproto.h>
#include <X11/Xos.h>		/* for XtNewString */

#include <stdio.h>
#include <X11/Xmu/Error.h>

#include "editresP.h"

/*
 * static Globals.
 */

static Atom atom_comm, atom_command, atom_resource_editor, atom_client_value;
static Atom atom_editres_protocol;

/*
 * external function definitions.
 */

extern void RebuildMenusAndLabel();
extern ResIdent GetNewIdent();
extern void SetMessage(), BuildVisualTree(),DisplayChild();
extern char * GetFormattedSetValuesError(), *HandleFlashWidget();
extern char * HandleGetResources(),  *PrintSetValuesError();
char * GetFailureMessage(), * ProtocolFailure();
extern int HandleXErrors();
extern void SetEntriesSensitive();

static void TellUserAboutMessage(), BuildHeader(), FreeEvent();
static Event * BuildEvent();
static char * DispatchEvent();
static void GetClientValue();
static void ClientTimedOut(), LoseSelection(), SelectionDone();
static Boolean ConvertCommand();


extern Widget CM_entries[NUM_CM_ENTRIES], TM_entries[NUM_TM_ENTRIES];



/*	Function Name: ClientTimedOut
 *	Description: Called if the client takes too long to take our selection.
 *	Arguments: data - The widget that owns the client 
 *                        communication selection.
 *                 id - *** UNUSED ***
 *	Returns: none.
 */

/* ARGSUSED */
static void
ClientTimedOut(data, id)
XtPointer data;
XtIntervalId * id;
{
  char msg[BUFSIZ];
  Widget w = (Widget) data;
  
  global_client.ident = NO_IDENT;
  XtDisownSelection(w, global_client.atom, 
		    XtLastTimestampProcessed(XtDisplay(w)));
  
  sprintf(msg, res_labels[4],
	  "the Editres Protocol.");
  SetMessage(global_screen_data.info_label, msg);
}



/*	Function Name: GetClientWindow
 *	Description: Gets the Client's window by asking the user.
 *	Arguments: w - a widget.
 *	Returns: a clients window, or None.
 */

Window 
GetClientWindow(w, x, y)
Widget w;
int *x, *y;
{
  int status;
  Cursor cursor;
  XEvent event;
  int buttons = 0;
  Display * dpy = XtDisplayOfObject(w);
  Window target_win = None, root = RootWindowOfScreen(XtScreenOfObject(w));
  XtAppContext app = XtWidgetToApplicationContext(w);
  
  /* Make the target cursor */
  cursor = XCreateFontCursor(dpy, XC_crosshair);
  
  /* Grab the pointer using target cursor, letting it room all over */
  status = XGrabPointer(dpy, root, False,
			ButtonPressMask|ButtonReleaseMask, GrabModeSync,
			GrabModeAsync, root, cursor, CurrentTime);
  if (status != GrabSuccess) {
    SetMessage(global_screen_data.info_label, res_labels[5]);
    return(None);
  }
  
  /* Let the user select a window... */
  while ((target_win == None) || (buttons != 0)) {
    /* allow one more event */
    XAllowEvents(dpy, SyncPointer, CurrentTime);
    XtAppNextEvent(app, &event);
    switch (event.type) {
    case ButtonPress:
      if (event.xbutton.window != root) {
	XtDispatchEvent(&event);
	break;
      }
      
      if (target_win == None) {
	target_win = event.xbutton.subwindow; /* window selected */
	if (x != NULL)
	  *x = event.xbutton.x_root;
	if (y != NULL)
	  *y = event.xbutton.y_root;
      }
      buttons++;
      break;
    case ButtonRelease:
      if (event.xbutton.window != root) {
	XtDispatchEvent(&event);
	break;
      }
      
      if (buttons > 0) /* There may have been some
			  down before we started */
	buttons--;
      break;
    default:
      XtDispatchEvent(&event);
      break;
    }
  } 
  
  XUngrabPointer(dpy, CurrentTime);      /* Done with pointer */
  
  return(XmuClientWindow(dpy, target_win));
}



/*	Function Name: SetCommand
 *	Description: Causes this widget to own the resource editor's 
 *                   command selection.
 *	Arguments: w - the widget that will own the selection.
 *                 command - command to send to client.
 *                 msg - message to prompt the user to select a client.
 *	Returns: none.
 */

/* ARGSUSED */
void
SetCommand(w, command, msg)
     Widget w;
     ResCommand command;
     char * msg;
{
  XClientMessageEvent client_event;
  Display * dpy = XtDisplay(w);
  
  if (msg == NULL) 
    msg = res_labels[6];
  
  SetMessage(global_screen_data.info_label, msg);
  
  if (global_client.window == None)
    if ( (global_client.window = GetClientWindow(w, NULL, NULL)) == None) 
      return;

  global_client.ident = GetNewIdent();
    
  global_client.command = command;
  global_client.atom = atom_comm;

  BuildHeader(&(global_client)); 

  if (!XtOwnSelection(w, global_client.atom, CurrentTime, ConvertCommand, 
		      LoseSelection, SelectionDone))
    SetMessage(global_screen_data.info_label,
	       res_labels[7]);

  client_event.window = global_client.window;
  client_event.type = ClientMessage;
  client_event.message_type = atom_resource_editor;
  client_event.format = EDITRES_SEND_EVENT_FORMAT;
  client_event.data.l[0] = XtLastTimestampProcessed(dpy);
  client_event.data.l[1] = global_client.atom;
  client_event.data.l[2] = (long) global_client.ident;
  client_event.data.l[3] = global_effective_protocol_version;
  
  global_error_code = NO_ERROR;                 /* Reset Error code. */
  global_old_error_handler = XSetErrorHandler(HandleXErrors);
  global_serial_num = NextRequest(dpy);
  
  XSendEvent(dpy, global_client.window, FALSE, (long) 0, 
	     (XEvent *) &client_event);
  
  XSync(dpy, FALSE);
  XSetErrorHandler(global_old_error_handler);
  if (global_error_code == NO_WINDOW) {
    char error_buf[BUFSIZ];
    
    global_error_code = NO_ERROR;	/* Reset Error code. */
    sprintf(error_buf, "The communication window with%s%s.",
	    " application is no longer available\n",
	    "Please select a new widget tree");
    
    global_client.window = None;
    SetCommand(w, LocalSendWidgetTree, error_buf);
    return;
  }   
  
  TellUserAboutMessage(global_screen_data.info_label, command);
  global_client.timeout = XtAppAddTimeOut(XtWidgetToApplicationContext(w),
					  CLIENT_TIME_OUT, 
					  ClientTimedOut, (XtPointer) w);
}



/*	Function Name: TellUserAboutMessage
 *	Description: Informs the user that we have sent a message to the client
 *	Arguments: label - the info label.
 *                 command - command that we have executed.
 *	Returns: none.
 */

static void
TellUserAboutMessage(label, command)
Widget label;
ResCommand command;
{
    char msg[BUFSIZ], *str;

    switch(command) {
    case LocalSendWidgetTree:
	str = " asking for widget tree";
	break;
    case LocalSetValues:
	str = " asking it to perform SetValues()";
	break;
    case LocalFlashWidget:
    case LocalGetGeometry:
	str = " asking it to perform GetGeometry()";
	break;
    case LocalGetResources:
	str = " asking it to get a widget's resource list";
	break;
    case LocalFindChild:
	str = " asking it to find the child Widget.";
	break;
    default:
	str = "";
	break;
    }

    sprintf(msg, res_labels[8], str);
    SetMessage(label, msg);
}



/*	Function Name: ConvertCommand
 *	Description: Converts the command string into a selection that can
 *                   be sent to the client.
 *	Arguments: (see Xt)
 *	Returns: TRUE if we could convert the selection and target asked for.
 */

/* ARGSUSED */
static Boolean
ConvertCommand(w,selection,target,type_ret, value_ret, length_ret, format_ret)
Widget w;
Atom * selection, * target, * type_ret;
XtPointer *value_ret;
unsigned long * length_ret;
int * format_ret;
{
    if ((*selection != atom_comm) || (*target != atom_command))
	return(FALSE);

    *type_ret = atom_editres_protocol;
    *value_ret = (XtPointer) global_client.stream.real_top;
    *length_ret = global_client.stream.size + HEADER_SIZE;
    *format_ret = EDITRES_FORMAT;
    
    return(TRUE);
}



/*	Function Name: SelectionDone
 *	Description: done with the selection.
 *	Arguments: *** UNUSED ***
 *	Returns: none.
 */

/* ARGSUSED */
static void
SelectionDone(w, sel, targ)
    Widget w;
    Atom *sel, *targ;
{
    /* Keep the toolkit from automaticaly freeing the selection value */
}



/*	Function Name: LoseSelection
 *	Description: Called when we have lost the selection, asks client
 *                   for the selection value.
 *	Arguments: w - the widget that just lost the selection.
 *                 sel - the selection.
 *	Returns: none.
 */

static void
LoseSelection(w, sel)
Widget w;
Atom * sel;
{
    if (global_client.timeout != 0) {
	XtRemoveTimeOut(global_client.timeout);
	global_client.timeout = 0;
    }

    XtGetSelectionValue(w, *sel, atom_client_value, GetClientValue,
			NULL, XtLastTimestampProcessed(XtDisplay(w)));
}



/*	Function Name: GetClientValue
 *	Description: Gets the value out of the client, and does good things
 *                   to it.
 *	Arguments: w - the widget that asked for the selection.
 *                 data - client_data *** UNUSED ***.
 *                 sel - the selection.
 *                 type - the type of the selection.
 *                 value - the selection's value.
 *                 length - the length of the selection's value.
 *                 format - the format of the selection.
 *	Returns: none.
 */

static Boolean reset_protocol_level = True;

/* ARGSUSED */
static void
GetClientValue(w, data, selection, type, value, length, format)
Widget w;
XtPointer data, value;
Atom *selection, *type;
unsigned long *length;
int * format;
{
    Event * event;
    ProtocolStream alloc_stream, *stream;
    unsigned char ident, error_code;
    char * error_str, msg[BUFSIZ];

    if (*length == 0)
	return;

    stream = &alloc_stream;	/* easier to think of it this way... */

    stream->current = stream->top = (unsigned char *) value;
    stream->size = HEADER_SIZE;		/* size of header. */

    /*
     * Retrieve the Header.
     */

    if (*length < HEADER_SIZE) {
	SetMessage(global_screen_data.info_label,
		   res_labels[9]);
	return;
    }

    (void) _XEditResGet8(stream, &ident);
    if (global_client.ident != ident) {
#ifdef DEBUG
	if (global_resources.debug)
	    printf("Incorrect ident from client.\n");
#endif 
	if (!XtOwnSelection(w, *selection, CurrentTime, ConvertCommand, 
			    LoseSelection, SelectionDone))
	    SetMessage(global_screen_data.info_label,
		       res_labels[10]);
	return;
    }

    (void) _XEditResGet8(stream, &error_code); 
    (void) _XEditResGet32(stream, &(stream->size));
    stream->top = stream->current; /* reset stream to top of value.*/

    switch ((int) error_code) {
    case PartialSuccess:
/*****
        if (global_client.command == LocalSendWidgetTree &&
	    global_effective_protocol_version < CURRENT_PROTOCOL_VERSION)
	  ++global_effective_protocol_version;
*****/
	if ((event = BuildEvent(stream)) != NULL) {
	    error_str = DispatchEvent(event);
	    FreeEvent(event);
	}
	else {
	    sprintf(msg, "Unable to unpack protocol request.");
	    error_str = XtNewString(msg);
	}
	break;
    case Failure:
	error_str = GetFailureMessage(stream);
	break;
    case ProtocolMismatch:
	error_str = ProtocolFailure(stream);
	--global_effective_protocol_version;
	/* normaly protocol version is reset to current during a SendWidgetTree
         * request, however, after a protocol failure this is skiped once for
         * a retry.
         */
	reset_protocol_level = False;
	SetCommand(w, LocalSendWidgetTree, NULL);
	break;
    default:
	sprintf(msg, res_labels[11], (int) error_code);
	SetMessage(global_screen_data.info_label, msg);
	break;
    }

    if (error_str == NULL) {
	WNode * top;
	
	if (global_tree_info == NULL)
	    return;
	
	top = global_tree_info->top_node;
	sprintf(msg, res_labels[12], top->name, top->class);
	SetMessage(global_screen_data.info_label, msg);
	return;
    }
    SetMessage(global_screen_data.info_label, error_str);
    XtFree(error_str);
}



/*	Function Name: BuildHeader
 *	Description: Puts the header into the message.
 *	Arguments: client_data - the client data.
 *	Returns: none.
 */

static void
BuildHeader(client_data)
CurrentClient * client_data;
{
    unsigned long old_alloc, old_size;
    unsigned char * old_current;
    EditresCommand command;
    ProtocolStream * stream = &(client_data->stream);

    /*
     * We have cleverly keep enough space at the top of the header
     * for the return protocol stream, so all we have to do is
     * fill in the space.
     */

    /* 
     * Fool the insert routines into putting the header in the right
     * place while being damn sure not to realloc (that would be very bad.
     */
    
    old_current = stream->current;
    old_alloc = stream->alloc;
    old_size = stream->size;

    stream->current = stream->real_top;
    stream->alloc = stream->size + (2 * HEADER_SIZE);	
    
    _XEditResPut8(stream, client_data->ident);
    switch(client_data->command) {
    case LocalSendWidgetTree: 
        if (reset_protocol_level) global_effective_protocol_version = 
	  CURRENT_PROTOCOL_VERSION;
        reset_protocol_level = True;
	command = SendWidgetTree;
	break;
    case LocalSetValues:
	command = SetValues;
	break;
    case LocalFlashWidget:
	command = GetGeometry;
	break;
    case LocalGetResources:
	command = GetResources;
	break;
    case LocalFindChild:
	command = FindChild;
	break;
    case LocalGetValues:
	command = GetValues;
	break;
    default:
	command = SendWidgetTree;
	break;
    }
				  
    _XEditResPut8(stream, (unsigned char) command);
    _XEditResPut32(stream, old_size);

    stream->alloc = old_alloc;
    stream->current = old_current;
    stream->size = old_size;
}



/*	Function Name: BuildEvent
 *	Description: Builds the event structure from the 
 *	Arguments: stream - the protocol data stream.
 *	Returns: event - the event.
 */

static Event * 
BuildEvent(stream)
ProtocolStream * stream;
{
    int i;
    Event * event = (Event *) XtCalloc(sizeof(Event), 1);

    /*
     * The return value will be different depending upon the
     * request sent out.
     */

    switch(global_client.command) {
    case LocalSendWidgetTree:
        {
	    SendWidgetTreeEvent * send_event = (SendWidgetTreeEvent *) event;

	    send_event->type = SendWidgetTree;

	    if (!_XEditResGet16(stream, &(send_event->num_entries)))
		goto done;
	    
	    send_event->info = (WidgetTreeInfo *)
		                XtCalloc(sizeof(WidgetTreeInfo),
					 send_event->num_entries);

	    for (i = 0; i < (int)send_event->num_entries; i++) {
		WidgetTreeInfo * info = send_event->info + i;
		if (!(_XEditResGetWidgetInfo(stream, &(info->widgets)) &&
		      _XEditResGetString8(stream, &(info->name)) &&
		      _XEditResGetString8(stream, &(info->class)) &&
		      _XEditResGet32(stream, &(info->window)))) 
		{
		    goto done;
		}
	    }

	    if (global_effective_protocol_version == 
		CURRENT_PROTOCOL_VERSION) {
				/* get toolkit type and reset if necessary */
	      if (!_XEditResGetString8(stream, &(send_event->toolkit)))
 	        goto done;
	    }
	    /* set the command menu entries senitive */
	    SetEntriesSensitive(&CM_entries[CM_OFFSET], CM_NUM, True);
	    /* set the tree menu entries senitive */
	    SetEntriesSensitive(TM_entries, TM_NUM, True);
	    if (global_effective_protocol_version == 
		CURRENT_PROTOCOL_VERSION) {
	      if (!strcmp(send_event->toolkit, "InterViews")) 
	        RebuildMenusAndLabel("iv");
            } 
            else
              RebuildMenusAndLabel("xt");
	}
	break;
    case LocalSetValues:
        {
	    SetValuesEvent * sv_event = (SetValuesEvent *) event;

	    sv_event->type = SetValues;

	    if (!_XEditResGet16(stream, &(sv_event->num_entries)))
		goto done;
	    
	    sv_event->info = (SetValuesInfo *) XtCalloc(sizeof(SetValuesInfo),
							sv_event->num_entries);

	    for (i = 0; i < (int)sv_event->num_entries; i++) {
		SetValuesInfo * info = sv_event->info + i;
		if (!(_XEditResGetWidgetInfo(stream, &(info->widgets)) &&
		      _XEditResGetString8(stream, &(info->message))))
		{
		    goto done;
		}
	    }
	}
	break;
    case LocalGetResources:
        {
	    GetResourcesEvent * res_event = (GetResourcesEvent *) event;
	    
	    res_event->type = GetGeometry;

	    if (!_XEditResGet16(stream, &(res_event->num_entries)))
		goto done;

	    res_event->info = (GetResourcesInfo *) 
		                   XtCalloc(sizeof(GetResourcesInfo),
					    res_event->num_entries);

	    for (i = 0; i < (int)res_event->num_entries; i++) {
		GetResourcesInfo * res_info = res_event->info + i;
		if (!(_XEditResGetWidgetInfo(stream, &(res_info->widgets)) &&
		      _XEditResGetBoolean(stream, &(res_info->error))))
		{
		    goto done;
		}
		if (res_info->error) {
		    if (!_XEditResGetString8(stream, &(res_info->message))) 
			goto done;
		}
		else {
		    unsigned int j;

		    if (!_XEditResGet16(stream, &(res_info->num_resources)))
			goto done;

		    res_info->res_info = (ResourceInfo *) 
			                  XtCalloc(sizeof(ResourceInfo),
						   res_info->num_resources);

		    for (j = 0; j < res_info->num_resources; j++) {
			unsigned char temp;
			ResourceInfo * info = res_info->res_info + j;
			if (!(_XEditResGetResType(stream, &(temp)) &&
			      _XEditResGetString8(stream, &(info->name)) &&
			      _XEditResGetString8(stream, &(info->class)) &&
			      _XEditResGetString8(stream, &(info->type))))
			{
			    goto done;
			}
			else
			    info->res_type = (ResourceType) temp;
		    } /* for */
		} /* else */
	    } /* for */
	}
	break;
    case LocalFlashWidget:
    case LocalGetGeometry:
        {
	    GetGeomEvent * geom_event = (GetGeomEvent *) event;

	    geom_event->type = GetGeometry;

	    if (!_XEditResGet16(stream, &(geom_event->num_entries)))
		goto done;
	    
	    geom_event->info = (GetGeomInfo *) XtCalloc(sizeof(GetGeomInfo),
						      geom_event->num_entries);

	    for (i = 0; i < (int)geom_event->num_entries; i++) {
		GetGeomInfo * info = geom_event->info + i;
		if (!(_XEditResGetWidgetInfo(stream, &(info->widgets)) &&
		      _XEditResGetBoolean(stream, &(info->error))))
		{
		    goto done;
		}
		if (info->error) {
		    if (!_XEditResGetString8(stream, &(info->message)))
			goto done;
		}
		else {
		    if (!(_XEditResGetBoolean(stream, &(info->visable)) &&
			  _XEditResGetSigned16(stream, &(info->x)) &&
			  _XEditResGetSigned16(stream, &(info->y)) &&
			  _XEditResGet16(stream, &(info->width)) &&
			  _XEditResGet16(stream, &(info->height)) &&
			  _XEditResGet16(stream, &(info->border_width))))
		    {
			goto done;
		    }
		}
	    }
	}
	break;
    case LocalFindChild:
        {
	    FindChildEvent * find_event = (FindChildEvent *) event;

	    find_event->type = FindChild;

	    if (!_XEditResGetWidgetInfo(stream, &(find_event->widgets)))
		goto done;
	}
	break;
    case LocalGetValues: /* This is for REPLY... */
	{
	  Arg args[1];
	  GetValuesEvent * gv_event = (GetValuesEvent *) event;
	  
	  gv_event->type = GetValues;
	  
	  if (!_XEditResGet16(stream, &(gv_event->num_entries)))
	    goto done;

	  gv_event->info = (GetValuesInfo*)XtCalloc(sizeof(GetValuesInfo),1);

	  {
	    GetValuesInfo * info = gv_event->info;
	    if (!(_XEditResGetString8(stream, &(info->value))))
	      {
		goto done;
	      }

	    /* set the string value of the asciitext widget. note that only
             * one active node is dealt with here.  This is ok because only
	     * one node can be active when the resource box is up.
	     */

	    XtSetArg (args[0], XtNstring, info->value);

	    XtSetValues(
	      global_tree_info->active_nodes[0]->resources->res_box->value_wid,
 	      args, 1
	    );
	  }
	}
	break;

    default:
	goto done;
    }

    return(event);

 done:
    FreeEvent(event);
    return(NULL);
}



/*	Function Name: FreeEvent
 *	Description: Frees all memory associated with the event. 
 *	Arguments: event - the event.
 *	Returns: none.
 *
 * NOTE: XtFree() returns w/o freeing if ptr is NULL.
 */

static void
FreeEvent(event)
Event * event;
{
    unsigned int i;

    switch(event->any_event.type) {
    case SendWidgetTree:
        {
	    SendWidgetTreeEvent * send_event = (SendWidgetTreeEvent *) event;
	    WidgetTreeInfo * info = send_event->info;
	    
	    if (info != NULL) {
		for (i = 0; i < send_event->num_entries; i++, info++) {
		    XtFree((char *)info->widgets.ids);
		    XtFree(info->name);
		    XtFree(info->class);
		}
		XtFree((char *)send_event->info);
	    }
	}
	break;
    case SetValues:
        {
	    SetValuesEvent * sv_event = (SetValuesEvent *) event;
	    SetValuesInfo * info = sv_event->info;
	    
	    if (info != NULL) {
		for (i = 0; i < sv_event->num_entries; i++, info++) {
		    XtFree((char *)info->widgets.ids);
		    XtFree(info->message);
		}
		XtFree((char *)sv_event->info);
	    }
	}
	break;
    case GetResources:
        {
	    GetResourcesEvent * get_event = (GetResourcesEvent *) event;
	    GetResourcesInfo * info = get_event->info;

	    if (info != NULL) {
		for (i = 0; i < get_event->num_entries; i++, info++) {
		    XtFree((char *)info->widgets.ids);
		    if (info->error) 
			XtFree(info->message);
		    else {
			unsigned int j;
			ResourceInfo * res_info = info->res_info;
			
			if (res_info != NULL) {
			    for (j = 0; 
				 j < info->num_resources; j++, res_info++) 
			    {
				XtFree(res_info->name);
				XtFree(res_info->class);
				XtFree(res_info->type);
			    }
			    XtFree((char *)info->res_info);
			}
		    }
		} 
		XtFree((char *)get_event->info);
	    }
	}
	break;
    case GetGeometry:
        {
	    GetGeomEvent * geom_event = (GetGeomEvent *) event;
	    GetGeomInfo * info = geom_event->info;

	    if (info != NULL) {
		for (i = 0; i < geom_event->num_entries; i++, info++) {
		    XtFree((char *)info->widgets.ids);
		    if (info->error) 
			XtFree(info->message);
		}
		XtFree((char *)geom_event->info);
	    }
	}
	break;
    case FindChild:
        {
	    FindChildEvent * find_event = (FindChildEvent *) event;
	    
	    XtFree((char *)find_event->widgets.ids);
	}
	break;
    default:
	break;
    }
}



/*	Function Name: DispatchEvent
 *	Description: Handles the event, calling the proper function.
 *	Arguments: event - the event.
 *	Returns: one.
 */
	
static char *
DispatchEvent(event)
Event * event;
{
    char * error = NULL;

    switch(global_client.command) {
    case LocalSendWidgetTree:
	BuildVisualTree(global_tree_parent, event);
	break;
    case LocalSetValues:
	error = PrintSetValuesError(event);
	break;
    case LocalFlashWidget:
	error = HandleFlashWidget(event);
	break;
    case LocalGetResources:
	error = HandleGetResources(event);
	break;
    case LocalFindChild:
	DisplayChild(event);
	break;
    case LocalGetValues:
	break;
    default:
        {
	    char msg[BUFSIZ];
	    sprintf(msg, "Internal error: Unknown command %d.", 
		    global_client.command);
	    error = XtNewString(msg);
	}
	break;
    }
    return(error);
}



/*	Function Name: InternAtoms
 *	Description: interns all static atoms.
 *	Arguments: display - the current display.
 *	Returns: none.
 */

void
InternAtoms(dpy)
Display * dpy;
{
    atom_comm = XInternAtom(dpy, EDITRES_COMM_ATOM, False);
    atom_command = XInternAtom(dpy, EDITRES_COMMAND_ATOM, False);
    atom_resource_editor = XInternAtom(dpy, EDITRES_NAME, False);
    atom_client_value = XInternAtom(dpy, EDITRES_CLIENT_VALUE, False);
    atom_editres_protocol = XInternAtom(dpy, EDITRES_PROTOCOL_ATOM, False);
}

ResIdent
GetNewIdent()
{
    static ResIdent ident = 1;

    return(ident++);
}

