/****************************************************************************
 ***
 ***	Copyright © 1990 by Sun/Lucid,  All Rights Reserved.
 ***	Copyright © 1991-1993 by Lucid, Inc.  All Rights Reserved.
 ***
 *****************************************************************************/

#include "config.h"

#ifdef ENERGIZE 	/* whole file */

#include "lisp.h"
#include "events.h"

#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>		/* some typedefs are used in sys/file.h */
#include <sys/file.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>		/* if not provided BLOCK_INPUT loses */
#include <signal.h>		/* must be before xterm.c ?? */
#include <string.h>
#include <errno.h>

/* editing and buffer operations */
#include "buffer.h" 
#include "extents.h"
#include "process.h"

/* screen management */
#include "xterm.h"
#include "screen.h"
#include "window.h" 

#include <lwlib.h>

/* Display Context for the icons */ 
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <Xm/DialogS.h>

/* Energize editor requests and I/O operations */
#include "editorside.h"

#include <editorreq.h>
#include <editorconn.h>
#include <editoption.h>

#include "extents-data.h"


extern void CWriteQueryChoicesRequest ();
extern void CWriteExecuteChoicesRequest();
extern void CWriteSheetRequest();
extern void CWriteSetControlRequest();
extern void CWriteChoice();
extern void CWriteProtocol();
extern int  CGetPortNumber();
extern int  CReadSomeMore(Connection*, int);
extern void NewPixmapImage();
extern void NewAttributeImage();


/************** Typedefs and Structs ***********************/

/* structure argument used by the next mapping function */
typedef struct 
{
  BufferInfo *binfo;
  int n_extents;
} binfo_and_n_extents;

typedef struct 
{
  BufferInfo* 	binfo;
  int 		state;
  int		tell_energize;
} binfo_and_state;

struct reply_wait {
  int		serial;
  EId		objectId;
  EId		genericId;
  EId		itemId;
  char		answered_p;
  char		status;
  char*		message;
  Lisp_Object	menu_result;
  Lisp_Object	only_name;
  struct reply_wait*	next;
};

static struct reply_wait*
global_reply_wait;

Lisp_Object Venergize_kernel_busy;
Lisp_Object Qenergize_kernel_busy;
Lisp_Object Venergize_attributes_mapping;
Lisp_Object Venergize_kernel_busy_hook;
Lisp_Object Venergize_menu_update_hook;
Lisp_Object Qenergize_extent_data;

/*static int energize_font_lock_p;*/


/************************ Functions ********************/
extern Lisp_Object Venergize_kernel_busy;
extern Lisp_Object make_extent_for_data
(BufferInfo *binfo, Energize_Extent_Data *ext, int from, int to, int set_endpoints);

static int
wait_for_reply (struct reply_wait* rw);
static Energize_Extent_Data *extent_to_data (Lisp_Object extent_obj);
static char *copy_string (char *s);
Lisp_Object word_to_lisp (unsigned int item);
unsigned int lisp_to_word (Lisp_Object obj);
static void *get_object (EId id, BufferInfo *binfo);
static void put_object (EId id, BufferInfo *binfo, void *object);
static void remove_object (EId id, BufferInfo *binfo);
static void free_object (void *key, void *contents, void *arg);
static GDataClass *alloc_GDataclass (EId id, BufferInfo *binfo);
static void free_GDataclass (GDataClass *cl, BufferInfo *binfo);
static GenericData *alloc_GenericData (EId id, GDataClass *cl, 
                                       BufferInfo *binfo);
static void free_GenericData (GenericData *gen, BufferInfo *binfo);
static Energize_Extent_Data *alloc_Energize_Extent_Data (EId id, BufferInfo *binfo);
static void free_Energize_Extent_Data (Energize_Extent_Data *ext, BufferInfo *binfo, 
                              enum Energize_Object_Free_Type free_type);
void energize_extent_finalization (EXTENT extent);
static BufferInfo *alloc_BufferInfo
(EId id, Lisp_Object name, Lisp_Object filename, 
 char *class_str, Editor *editor, Window win);
static void free_buffer_info (BufferInfo *binfo);
static BufferInfo *get_buffer_info_for_emacs_buffer (Lisp_Object emacs_buf, Editor *editor);
static long get_energize_buffer_id (Lisp_Object emacs_buf);
static char *kernel_buffer_type_to_elisp_type (char *kernel_type);
static Lisp_Object get_buffer_type_for_emacs_buffer 
(Lisp_Object emacs_buf, Editor *editor);
static Lisp_Object set_buffer_type_for_emacs_buffer 
(Lisp_Object emacs_buf, Editor *editor, Lisp_Object type);
static BufferInfo *get_buffer_info_for_id (EId id, Editor *editor);
static void put_buffer_info 
(EId id, Lisp_Object emacs_buf, BufferInfo *binfo, Editor *editor);
static void remove_buffer_info (EId id, Lisp_Object emacs_buf, Editor *editor);
static void Post (char *msg);
static void Post1 (char *msg, int a1);
static void Post2 (char *msg, int a1, int a2);
static EmacsPos EmacsPosForEnergizePos (EnergizePos energizePos);
static EnergizePos EnergizePosForEmacsPos (EmacsPos emacs_pos);
Lisp_Object Fenergize_update_menubar (Lisp_Object screen);
Lisp_Object Fenergize_extent_menu_p (Lisp_Object extent_obj);
void free_zombie_bitmaps (void);
static void ParseClasses (Connection *conn, unsigned int number, 
                          BufferInfo *binfo, unsigned int modify_ok);
static void ParseGenerics (Connection *conn, unsigned int number, 
                           BufferInfo *binfo, unsigned int modify_ok);
static void insert_one_extent (CExtent *ptr, BufferInfo *binfo, int modify_ok);
static void ParseExtents (Connection *conn, unsigned int number, 
                          BufferInfo *binfo, unsigned int modify_ok, 
                          int extent_offset);
static int string_buffer_compare (char *string, int string_len, 
                                  struct buffer *buf, int bufpos);
static void rename_the_buffer (Lisp_Object new_name);
static void ParseBuffer (Connection *conn, CBuffer *cbu, Editor *editor, 
                         EnergizePos delete_from, EnergizePos delete_to, 
                         Window win, int relative_p);
static void forget_buffer (BufferInfo *binfo);
static void WriteExtent (Connection *conn, Energize_Extent_Data *ext, 
                  unsigned int start, unsigned int end);
static char *get_buffer_as_string (unsigned int *len);
static int write_an_extent (Lisp_Object extent_obj, void *arg);
static void SaveBufferToEnergize (BufferInfo *binfo);
static Lisp_Object
get_energize_menu (Lisp_Object buffer, Lisp_Object extent_obj, int selection_p,
		   Lisp_Object only_name);
static int something_answered_p (void *arg);
void wait_delaying_user_input (int (*)(), void*);
Lisp_Object Fenergize_request_menu (Lisp_Object buffer, Lisp_Object extent);
Lisp_Object Fenergize_list_menu (Lisp_Object buffer, Lisp_Object extent_obj,
				 Lisp_Object selection_p,
				 Lisp_Object only_name);
Lisp_Object Fenergize_execute_menu_item 
(Lisp_Object buffer, Lisp_Object extent_obj, 
 Lisp_Object item, Lisp_Object selection, Lisp_Object no_confirm);
Lisp_Object Fenergize_buffer_type (Lisp_Object buffer);
Lisp_Object Fset_energize_buffer_type_internal (Lisp_Object buffer, 
                                                Lisp_Object type);
Lisp_Object Fenergize_buffer_p (Lisp_Object buffer);
Lisp_Object Fenergize_buffer_id (Lisp_Object buffer);
Lisp_Object Fenergize_request_kill_buffer (Lisp_Object buffer);
static void HandleControlChange (Widget, EId sheet_id, void* arg);
static void HandleLoggingRequest (Editor *editor, CLoggingRequest *creq);
static void HandleNewBufferRequest (Editor *editor, CNewBufferRequest *creq);
static void HandleModifyBufferRequest (Editor *editor, CModifyBufferRequest *creq);
static void MakeBufferAndExtentVisible (Lisp_Object list, Lisp_Object go_there);
static void HandleEnsureVisibleRequest (Editor *editor, CEnsureVisibleRequest *creq);
static void HandleEnsureManyVisibleRequest (Editor *editor, CEnsureManyVisibleRequest *creq);
static void HandleProposeChoicesRequest (Editor *editor, CProposeChoicesRequest *creq);
static void unmodify_buffer_and_kill_it (Lisp_Object buffer);
static void HandleKillBufferRequest (Editor *editor, CKillBufferRequest *creq);
static void HandleRemoveExtentsRequest (Editor *editor, CRemoveExtentsRequest *creq);
static void HandleSaveBufferRequest (Editor *editor, CSaveBufferRequest *creq);
static void HandleSetModifiedFlagRequest (Editor *editor, CSetModifiedFlagRequest *creq);
static void add_in_list_of_ids (int **ids, int *n_ids, int id);
static void remove_from_list_of_ids (int **ids, int *n_ids, int id);
static void HandleBufferSheetRequest (Editor *editor, CSheetRequest *sreq, EId buffer_id);
static void HandlePostitRequest (Editor *editor, CGenericRequest *preq);
static void HandleShowBusyRequest (Editor *editor, CGenericRequest *preq);
static void add_in_connection_input_buffer (Connection *conn, char *s, int l);
static Lisp_Object ProcessJustOneEnergizeRequest (void);
static Lisp_Object ProcessEnergizeRequest1 (void);
static void setup_connection (Editor *ed, unsigned int id1, unsigned int id2);
static Connection *EditorSideConnection (Editor *editor, int fdin, int fdout);
Lisp_Object Fhandle_energize_request (Lisp_Object proc, Lisp_Object string);
static void ConnectToEnergize (char *server_str, char *arg);
static void CloseConnection (void);
Lisp_Object Fconnect_to_energize_internal (Lisp_Object server_name, 
                                           Lisp_Object energize_arg);
Lisp_Object Fclose_connection_to_energize (void);
static void SendBufferModificationState (Editor *editor, BufferInfo *binfo, 
                                         int flag);
static int CheckBufferLock (Editor *editor, BufferInfo *binfo);
static Lisp_Object buffer_locked_p (Lisp_Object buffer);
static int notify_extent_modified (Lisp_Object extent_obj, void *arg);
static int ceiwme_lower_mf (EXTENT extent, void *arg);
static int ceiwme_upper_mf (EXTENT extent, void *arg);
static void coerce_endpoints_to_be_inside_warn_on_modify_extents 
(int *from_ptr,  int *to_ptr,  struct buffer *b);
static void mark_all_extents_as_unmodified (BufferInfo *binfo);
Lisp_Object Fsend_buffer_modified_request 
(Lisp_Object state, Lisp_Object from, Lisp_Object to);
Lisp_Object Fenergize_barf_if_buffer_locked (void);
Lisp_Object Fenergize_send_region (Lisp_Object start, Lisp_Object end);
Lisp_Object Fconnected_to_energize_p (void);
Lisp_Object Fenergize_user_input_buffer_mark (void);
Lisp_Object Fenergize_query_buffer (Lisp_Object filename, Lisp_Object just_ask);
static int get_energize_connection_and_buffer_id (Lisp_Object buffer, void **conn_ptr, long *buffer_id_ptr);
static int get_energize_connection_and_current_buffer_id (void **conn_ptr, long *buffer_id_ptr);
int *get_psheets_for_buffer (Lisp_Object buffer, int *count_ptr);
void notify_that_sheet_has_been_hidden (EId id);
void syms_of_editorside (void);



/**************************** Variables *****************************/

/* debugging variable */
int ignore_kernel;

Lisp_Object Venergize_kernel_modification_hook;
Lisp_Object Venergize_create_buffer_hook;

Lisp_Object Venergize_buffer_modified_hook;
Lisp_Object Qenergize_buffer_modified_hook;
Lisp_Object Qfirst_change_function;
Lisp_Object Qbefore_change_function;
Lisp_Object Qafter_change_function;
Lisp_Object Qbuffer_locked_by_kernel;
Lisp_Object Qdefault_directory;
Lisp_Object Qbuffer_file_name;
Lisp_Object Qenergize_user_input_buffer_mark;
Lisp_Object Qenergize_user_input_mode;
Lisp_Object Qenergize_make_many_buffers_visible;
Lisp_Object Qbuffer_undo_list;

extern Lisp_Object Qerror_conditions, Qerror_message;

int inside_parse_buffer;
Lisp_Object Qinside_parse_buffer;

/* List of all buffers currently managed by Energize.  This is
Staticpro'ed so that they don't get GC'ed from under us. */
static Lisp_Object energize_buffers_list;

static Editor *energize_connection;
static protocol_edit_options *peo;

static int request_serial_number;

extern int current_debuggerpanel_exposed_p;
extern int desired_debuggerpanel_exposed_p;
extern int debuggerpanel_sheet;

/* extern DisplayContext *display_context; */

/**************************** Macros *****************************/

#define max(A,B) ((A) > (B) ? (A) : (B))
#define min(A,B) ((A) <= (B) ? (A) : (B))
#define xnew(type) ((type*)xmalloc (sizeof (type)))

#define BUFFER_NOTIFY_BACKGROUND_BIT_SET_P(buffer) 1
  
#define get_extent_data(id,binfo) (Energize_Extent_Data*)get_object(id, binfo)
#define get_class(id,binfo) (GDataClass*)get_object(id, binfo)
#define get_generic(id,binfo) (GenericData*)get_object(id, binfo)

#define put_extent_data(id,binfo,obj) put_object(id, binfo, obj)
#define put_class(id,binfo,obj) put_object(id, binfo, obj)
#define put_generic(id,binfo,obj) put_object(id, binfo, obj)

#define remove_extent_data(id,binfo) remove_object(id, binfo)
#define remove_class(id,binfo) remove_object(id, binfo)
#define remove_generic(id,binfo) remove_object(id, binfo)

#define DEBUGGER_PSHEET_NAME  "DEBUGGER_P_SHEET"

/**************************** Utilities *****************************/

static int
emacs_CWriteRequestBuffer (Connection* conn)
{
  int result;
  signal (SIGPIPE, SIG_IGN);	/* don't kill emacs with SIGPIPE */
  result = CWriteRequestBuffer (conn);	/* the real one; macroized later */
  signal (SIGPIPE, SIG_DFL);
  return result;
}

#define CWriteRequestBuffer emacs_CWriteRequestBuffer


static Energize_Extent_Data *
extent_to_data (Lisp_Object extent_obj)
{
  Energize_Extent_Data *ext = 0;
  
  if (!EXTENTP (extent_obj)) 
    return 0;
  else
    ext = energize_extent_data (XEXTENT (extent_obj));
  
  if (ext)
    {
      if (EQ (ext->extent, extent_obj))
        return ext;
      else 
        abort ();
    }
  else
    return 0;
}


/* duplicate a string */
static char*
copy_string (char *s)
{
  if (!s)
    return 0;
  else
    {
      int len = strlen (s);
      char *res = (char *) xmalloc (len + 1);
      return strcpy (res, s);
    }
}

/* Get objects from the hashtables */
static void *
get_object_internal (EId id, c_hashtable table)
{
  void *res;
  void *found = gethash ((void*)id, table, &res);
  
  if (found) CHECK_OBJECT (res, 0);

  return found ? res : 0;
}

static void *
get_object (EId id, BufferInfo *binfo)
{
  return get_object_internal (id, binfo->id_to_object);
}

static void
put_object_internal (EId id, c_hashtable table, void *object)
{
  if (!PUT_ABLE_OBJECT (object))
    error ("Can't put 0x%x in table", object);
  CHECK_OBJECT (object, 0);
  puthash ((void*)id, object, table);
}

static void
put_object (EId id, BufferInfo *binfo, void *object)
{
  put_object_internal (id, binfo->id_to_object, object);
  return;
}

static void
remove_object_internal (EId id, c_hashtable table)
{
  void *res;

  if (gethash ((void*)id, table, &res))
    {
      if (OBJECT_FREE (res))
        error ("Free'd object 0x%x still in table!", res);
      remhash ((void*)id, table);
    }
  else if (id)
    error ("EId %d not in table!", id);
}

static void
remove_object (EId id, BufferInfo *binfo)
{
  remove_object_internal (id, binfo->id_to_object);
  return;
}

/* maphash_function called by free_buffer_info */
static void
free_object (void *key, void *contents, void *arg)
{
  BufferInfo *binfo = arg;

  if (contents)
    {
      switch (OBJECT_SEAL (contents))
        {
        case BUF_INFO_SEAL:
          break;
        case EXTENT_SEAL:
          free_Energize_Extent_Data ((Energize_Extent_Data *) contents, binfo, OFT_MAPHASH);
          break;
        case GDATA_CLASS_SEAL:
          free_GDataclass ((GDataClass *) contents, binfo);
          break;
        case GDATA_SEAL:
          free_GenericData ((GenericData *) contents, binfo);
          break;
        default:
          error ("Bad argument 0x%x to free_object()", contents);
          return;
        }
    }
}

static GDataClass *
alloc_GDataclass (EId id, BufferInfo *binfo)
{
  GDataClass *cl = xnew (GDataClass);
  memset (cl, 0, sizeof (GDataClass));
  cl->seal = GDATA_CLASS_SEAL;
  cl->id = id;
  put_class (cl->id, binfo, cl);
  return cl;
} 

static void
free_GDataclass (GDataClass *cl, BufferInfo *binfo)
{
  if (cl)
    {
      remove_class (cl->id, binfo);
      SET_OBJECT_FREE (cl);
    }
  return;
} 


static GenericData *
alloc_GenericData (EId id, GDataClass *cl, BufferInfo *binfo)
{
  GenericData *gen = xnew (GenericData);
  gen->seal = GDATA_SEAL;
  gen->id = id;
  gen->cl = cl;
/*  gen->image = 0;*/
  gen->flags = 0;
  gen->modified_state = 0;
  put_generic (gen->id, binfo, gen);
  return gen;
}
          
static void
free_GenericData (GenericData *gen, BufferInfo *binfo)
{
  if (gen)
    {
      remove_generic (gen->id, binfo);
      gen->cl = 0;
      SET_OBJECT_FREE (gen);
    }
  return;
} 

static Energize_Extent_Data *
alloc_Energize_Extent_Data (EId id, BufferInfo *binfo)
{              
  Energize_Extent_Data *ext = xnew (Energize_Extent_Data);
  ext->seal = EXTENT_SEAL;
  ext->id = id;
  ext->extent = 0;
  put_extent_data (ext->id, binfo, ext);
  return ext;
}

static void
free_Energize_Extent_Data 
(Energize_Extent_Data *ext, BufferInfo *binfo, enum Energize_Object_Free_Type free_type)
{              
  if (ext)
    {
      Lisp_Object extent_obj = ext->extent;
      ext->extent = 0;

      if (extent_obj && ext == energize_extent_data (XEXTENT (extent_obj)))
        {
          detach_extent (XEXTENT(extent_obj));
	  set_energize_extent_data (XEXTENT (extent_obj), 0);
        }
      remove_extent_data (ext->id, binfo);
      ext->id = 0;

      /* don't free this "sub-guy" via maphash, as it will get taken care
         of during the course of the maphash without our doing anything */
      if (free_type != OFT_MAPHASH)
        {
          if (ext->extentType == CEGeneric)
            free_GenericData (ext->u.generic.gData, binfo);
        }

      SET_OBJECT_FREE (ext);
    }
  return;
}

/* called from the GC when an extent is returned to the free list */
void
energize_extent_finalization (EXTENT extent)
{
  Energize_Extent_Data *ext = energize_extent_data (extent);
  Lisp_Object buffer = extent->buffer;
  BufferInfo *binfo;

  /* the if 0 case bypasses the freeing of extentData objects.  It's a big
     memory leak but could be causing some problems. */
#if 0
  if (ext)
    {
      ext->extent = 0;
      set_energize_extent_data (extent, 0);
    }
  return;
#else
  if (!ext || (!BUFFERP (buffer)))
    return;

  binfo = 
    energize_connection ?
      get_buffer_info_for_emacs_buffer (buffer, energize_connection) : 0;
    
  if (binfo && ext)
    free_Energize_Extent_Data (ext, binfo, OFT_GC);
  else
    ext->extent = 0;

  set_energize_extent_data (extent, 0);
  return;
#endif
}

extern int find_file_compare_truenames;

static BufferInfo *
alloc_BufferInfo (EId id, Lisp_Object name, Lisp_Object filename,
		  char *class_str, Editor *editor, Window win)
{
  BufferInfo *binfo = xnew (BufferInfo);
  Widget nw = 0;
  Lisp_Object buffer = Qnil;

  if (win){
    char win_as_string [16];
    nw = XtWindowToWidget (x_current_display, win);
    if (nw)
     nw = XtParent (nw);

    if (nw)
      sprintf (win_as_string, "w%x", nw);
    else
      sprintf (win_as_string, "0x%x", win);

    binfo->screen =
      Fx_create_screen (Qnil, build_string (win_as_string));
  }else
    binfo->screen = Qnil;

  /* try to re-use a buffer with the same file name if one already exists.
   * If a buffer already exists but is modified we should do a dialog and
   * ask the user what to do.  For now I'll just use a new buffer in that case.
   * ParseBuffer will erase the buffer.
   **/
  if (!NILP (filename))
    {
      int offct = find_file_compare_truenames;
      find_file_compare_truenames = 1;
      buffer = Fget_file_buffer (filename);
      find_file_compare_truenames = offct;

      if (!NILP (buffer) && !NILP (Fbuffer_modified_p (buffer)))
	buffer = Qnil;
    }

  if (NILP (buffer))
    buffer = Fget_buffer_create (Fgenerate_new_buffer_name (name));

  binfo->seal = BUF_INFO_SEAL;
  binfo->id = id;
  binfo->flags = 0;
  binfo->editor = editor;
  binfo->id_to_object = make_hashtable (100);
  binfo->emacs_buffer = buffer;
  binfo->modified_state = 0;
  binfo->editable = 0;
  binfo->output_mark = Qnil;
  binfo->buffer_type = kernel_buffer_type_to_elisp_type (class_str);
  binfo->p_sheet_ids = 0;
  binfo->n_p_sheets = 0;
  binfo->note_ids = 0;
  binfo->n_notes = 0;
  put_buffer_info (id, binfo->emacs_buffer, binfo, editor);

  energize_buffers_list = Fcons (buffer, energize_buffers_list);

#if 0
 *  if (nw){
 *    Lisp_Object window = Fscreen_selected_window (binfo->screen);
 *    Fset_window_buffer (window, binfo->emacs_buffer);
 *    BLOCK_INPUT;
 *    set_text_widget ((NoteWidget)nw,
 *		     XSCREEN(binfo->screen)->display.x->widget);
 *    UNBLOCK_INPUT;
 *  }
#endif

  return binfo;
}

/* free a buffer_info */
static void
free_buffer_info (BufferInfo *binfo)
{
  maphash (free_object, binfo->id_to_object, (void *)binfo);
  free_hashtable (binfo->id_to_object);
  if (energize_connection && energize_connection->binfo_hash) 
    {
      if (binfo->id)
        remhash ((void *)binfo->id, energize_connection->binfo_hash);
      if (binfo->emacs_buffer)
        remhash ((void *)binfo->emacs_buffer, energize_connection->binfo_hash);
    }
  binfo->id = 0;
  binfo->emacs_buffer = 0;
  SET_OBJECT_FREE (binfo);
}

/* hash for BufferInfo structures */
static BufferInfo*
get_buffer_info_for_emacs_buffer (Lisp_Object emacs_buf, Editor *editor)
{
  BufferInfo *res;
  if (!editor || !editor->binfo_hash)
    return 0;
  else
    return (gethash ((void *)emacs_buf, editor->binfo_hash, (void *)&res)
	    ? res : 0);
}


struct buffer_and_sheet_ids
{
  EId	buffer_id;
  EId	sheet_id;
};

static void
find_sheet_id (void* key, void* contents, void* arg)
{
  BufferInfo* binfo = (BufferInfo*)contents;
  EId buffer_id = (EId)key;
  struct buffer_and_sheet_ids* result = (struct buffer_and_sheet_ids*)arg;
  int i;

  if (!result->buffer_id)
    for (i = 0; i < binfo->n_p_sheets; i++)
      if (binfo->p_sheet_ids [i] == result->sheet_id)
	{
	  result->buffer_id = buffer_id;
	  return;
	}
}

static EId
buffer_id_of_sheet (EId id)
{
  Editor *editor = energize_connection;
  struct buffer_and_sheet_ids basi;
  if (!energize_connection)
    return 0;

  basi.buffer_id = 0;
  basi.sheet_id = id;
  maphash (find_sheet_id, editor->binfo_hash, (void*)&basi);
  return basi.buffer_id;
}

static long
get_energize_buffer_id (Lisp_Object emacs_buf)
{
  Editor *editor = energize_connection;
  BufferInfo *res;
  if (!editor || !editor->binfo_hash)
    return -1;
  else if (!gethash ((void *)emacs_buf, editor->binfo_hash, (void *)&res))
    return -1;
  else
    return (long) res->id;
}

static char *
kernel_buffer_type_to_elisp_type (char *kernel_type)
{
  struct buffer_type_struct *bts = 
    kernel_buffer_types_to_elisp_buffer_types_vector;
  char *elisp_type = 0;

  if (!kernel_type)
    return UNINITIALIZED_BUFFER_TYPE;

  while (bts->kernel_name)
    {
      if (!strcmp (bts->kernel_name, kernel_type))
        {
          elisp_type = bts->elisp_name;
          break;
        }
      bts++;
    }
  
  if (!elisp_type)
    return kernel_type;
  else
    return elisp_type;
}

static Lisp_Object
get_buffer_type_for_emacs_buffer (Lisp_Object emacs_buf, Editor *editor)
{
  BufferInfo *binfo;
  if (!(binfo = get_buffer_info_for_emacs_buffer (emacs_buf, editor)))
    return Qnil;
  else
    {
      if (!binfo->buffer_type) binfo->buffer_type = 
        UNINITIALIZED_BUFFER_TYPE;

      return intern (binfo->buffer_type);
    }
}

static Lisp_Object
set_buffer_type_for_emacs_buffer (Lisp_Object emacs_buf, Editor *editor,
                                    Lisp_Object type)
{
  BufferInfo *binfo;
  if (!(binfo = get_buffer_info_for_emacs_buffer (emacs_buf, editor)))
    return Qnil;
  else
    {
      char *type_string;

      if (NILP (type)) return Qnil;
        
      if (SYMBOLP (type))
        XSET (type, Lisp_String, XSYMBOL (type)->name);

      if (STRINGP (type)) 
        type_string = (char *)XSTRING (type)->data;

      type_string = copy_string (type_string);

      if (!type_string) return Qnil;

      binfo->buffer_type = type_string;

      return intern (binfo->buffer_type);
    }
}

static BufferInfo*
get_buffer_info_for_id (EId id, Editor *editor)
{
  BufferInfo *res;
  return (gethash ((void *)id, editor->binfo_hash, (void *)&res))?res:0;
}

static void
put_buffer_info (EId id, Lisp_Object emacs_buf, BufferInfo *binfo,
                 Editor *editor)
{
  puthash ((void *)id, binfo, editor->binfo_hash);
  puthash ((void *)emacs_buf, binfo, editor->binfo_hash);
}

static void
remove_buffer_info (EId id, Lisp_Object emacs_buf, Editor *editor)
{
  remhash ((void *)id, editor->binfo_hash);
  remhash ((void *)emacs_buf, editor->binfo_hash);
}

/* Display messages in minibuffer */
static void
  Post (char *msg) 
{
  message (msg, 0, 0, 0);
}

static void
  Post1 (char *msg, int a1) 
{
  message (msg, a1, 0, 0);
}

static void
Post2 (char *msg, int a1, int a2) 
{
  message (msg, a1, a2, 0);
}


/* Conversion between Energize and Emacs buffer positions */

static EmacsPos
EmacsPosForEnergizePos (EnergizePos energizePos)
{
  return ((energizePos >= (1 << VALBITS))?Z:(energizePos + 1));
}

static EnergizePos
EnergizePosForEmacsPos (EmacsPos emacs_pos)
{
  return (emacs_pos - 1);
}


/* extent data */

Energize_Extent_Data *
energize_extent_data (EXTENT extent)
{
  Lisp_Object data = extent->user_data;
  if (!CONSP (data) ||
      !EQ (XCONS (data)->car, Qenergize_extent_data))
    return 0;
  data = XCONS (data)->cdr;
  if (!FIXNUMP (XCONS (data)->car) || !FIXNUMP (XCONS (data)->cdr))
    abort ();
  return ((Energize_Extent_Data *)
	  ((XUINT (XCONS (data)->car) << 16) | (XUINT (XCONS (data)->cdr))));
}

void
set_energize_extent_data (EXTENT extent, void *data)
{
  unsigned short high = ((unsigned int) data) >> 16;
  unsigned short low = ((unsigned int) data) & 0xFFFF;
  Lisp_Object old = extent->user_data;
  if (old == 0)
    extent->user_data = Qnil;
  else if (NILP (old))
    extent->user_data = Fcons (Qenergize_extent_data,
			       Fcons (make_number (high), make_number (low)));
  else if (!CONSP (old) || !EQ (XCONS (old)->car, Qenergize_extent_data))
    abort ();
  else
    {
      XFASTINT (XCONS (XCONS (old)->cdr)->car) = high;
      XFASTINT (XCONS (XCONS (old)->cdr)->cdr) = low;
    }
}



extern int windows_or_buffers_changed;

extern void recompute_screen_menubar (struct screen *);

DEFUN ("energize-update-menubar", Fenergize_update_menubar,
       Senergize_update_menubar, 0, 1, 0,
       "Updates the menubar of optional SCREEN argument to reflect the \n\
commands currently enabled by Energize.")
     (screen)
     Lisp_Object screen;
{
  struct screen* s;

  if (!energize_connection) return Qnil;
  
  if (NILP (screen))
    s = selected_screen;
  else
    {
      CHECK_SCREEN (screen, 0);
      s = XSCREEN (screen);
    }
  
  if (!SCREEN_IS_X (s))
    error ("not an X screen");

  recompute_screen_menubar (selected_screen);
  return Qnil;
}


/* Return true if the extent is an Energize extent that can have a menu */
DEFUN ("energize-extent-menu-p", Fenergize_extent_menu_p,
       Senergize_extent_menu_p, 1, 1, 0,
       "Return true if the extent has a set of commands defined by Energize")
     (extent_obj)
     Lisp_Object extent_obj;
{
  CHECK_EXTENT (extent_obj, 0);

  if (NILP (Fconnected_to_energize_p())) 
    return Qnil;
  else
    {
      Energize_Extent_Data *ext = extent_to_data (extent_obj);
      return (ext && ext->extentType == CEGeneric) ? Qt : Qnil;
    }
}


/* Do what is needed so that the delayed requests will be notified by
** the event loop */

static void 
notify_delayed_requests ()
{
  extern void mark_process_as_being_ready (struct Lisp_Process* process);

  if (energize_connection && !NILP (energize_connection->proc) && energize_connection->conn
      && CRequestDelayedP (energize_connection->conn))
    mark_process_as_being_ready (XPROCESS (energize_connection->proc));
}


/******************* IMAGE storage maintenance *******************/

/* called by redisplay() -- presumably by the time this is called,
   all "dead" references into the x_bitmaps[] vector have been flushed
   from the display state */
void
free_zombie_bitmaps ()
{
}


extern struct x_pixmap *x_get_pixmap (Lisp_Object lisp_name, char *hash);

static c_hashtable image_cache;

/* Parses an image from the image language */
static GLYPH
ParseAnImage (Connection *conn, BufferInfo *binfo)
{
  ReqLen l;
  char *s = CGetVstring (conn, &l);
  char pix_name [255];
  char buf [255];
  int attr_number, pix_len;
  char *file;
  GLYPH result = 0;
  /* I don't know if it's ok to pass the address of a short to gethash... */
  int hashed = 0;

  if (s[0] != 'f')
    return 0;

  if (gethash ((void *) s, image_cache, (void *) &hashed))
    /* If we have already parsed this image spec (string=) then just return
       the old glyph, instead of calling the lisp code, x_get_pixmap, and
       XtGetSubResources again.  The result may be 0 if there is no pixmap
       file name in the resource database.
     */
    return (GLYPH) hashed;

  if (3 != sscanf (s, "f %d p %d %s", &attr_number, &pix_len, pix_name))
    {
      sprintf (buf, "unparsable image: \"%s\"", s);
      error (buf);
    }

  if (pix_len != strlen (pix_name))
    abort ();

  /* Read the pixmap file name for this image from the resource db */
  {
    XtResource resource [1];
    resource[0].resource_name = pix_name;
    resource[0].resource_class = XtCBitmap;
    resource[0].resource_type = XtRString;
    resource[0].resource_size = sizeof (char *);
    resource[0].resource_offset = 0;
    resource[0].default_type = XtRImmediate;
    resource[0].default_addr = 0;
    file = 0;
    BLOCK_INPUT;
    XtGetSubresources (selected_screen->display.x->widget, (XtPointer) &file,
		       "image", "Image", resource, 1, NULL, 0);
    UNBLOCK_INPUT;
  }

  if (! file)
    result = 0;
  else
    {
      struct x_pixmap *p;
      sprintf (buf, "attribute%d", attr_number);
      p = x_get_pixmap (build_string (file), buf);
      result = p->glyph_id;

      if (p->depth == 0)
	/* if depth is >0 then this is an XPM, and its colors are not
	   controlled by the fg/bg of a face.  So don't bother making a
	   face for it. */
	{
	  Lisp_Object face, face_id;
	  /* run the lisp code to make a face for this image. */
	  face = call1 (intern ("find-face"), intern (buf));
	  if (NILP (face))
	    {
	      call1 (intern ("make-face"), intern (buf));
	      face = call1 (intern ("find-face"), intern (buf));
	      if (NILP (face)) error ("make-face is broken");
	    }
	  face_id = call1 (intern ("face-id"), face);
	  CHECK_FIXNUM (face_id, 0);
	  p->face_id = XINT (face_id);
	}
    }

  BLOCK_INPUT;
  /* CGetVstring returns a pointer into the connection buffer; we need to
     copy it to use it as a hash key. */
  s = strdup (s);
  UNBLOCK_INPUT;

  hashed = result;
  puthash ((void *) s, (void *) hashed, image_cache);
  return result;
}


/* Parses Classes from the connection buffer.  Defines them for
 * the buffer given as argument */
static void
ParseClasses (Connection *conn, unsigned int number, BufferInfo *binfo,
              unsigned int modify_ok)
{
  CClass *ptr;                  /* pointer to class data in buffer */
  GDataClass *cl;               /* unmodified class data */
  GLYPH g;
  int i;
  
  for (i = 0; i < number; i++)
    {
      ptr = CGet (conn, CClass);
      g = ParseAnImage (conn, binfo);
      cl = get_class (ptr->classId, binfo);
      
      if (!cl)
        cl = alloc_GDataclass (ptr->classId, binfo);
      else if (!modify_ok)
        Post1("Attempt to create class with existing Id %8x", ptr->classId);
      
      if (ignore_kernel) continue;

      /* if it did exist, we just clobber it */
      if (cl->flags != ptr->flags)
	{
	  cl->flags = ptr->flags;
	  BUF_FACECHANGE (XBUFFER (binfo->emacs_buffer))++;
	}
      if (cl->glyph != g)
        {
	  cl->glyph = g;
	  BUF_FACECHANGE (XBUFFER (binfo->emacs_buffer))++;
        }
    }
}

/* Parse GenericData form the connection buffer.  Defines them for the buffer
 * given as argument */
static void
ParseGenerics (Connection *conn, unsigned int number, 
                 BufferInfo *binfo, unsigned int modify_ok)
{
  CGeneric *ptr;
  GenericData *gen;
  GDataClass *cl;
  GLYPH g;
  int i;
  
  for (i = 0; i < number; i++)
    {
      ptr = CGet (conn, CGeneric);
      g = ParseAnImage (conn, binfo);
      gen = get_generic (ptr->genericId, binfo);
      cl = get_class (ptr->classId, binfo);
      
      if (!gen)
        { 
          /* create generic if it didn't already exist */
          
          if (!cl)
            {
              Post2 ("Attempt to create generic %8x with undefined class %8x",
                     ptr->genericId, ptr->classId);
              continue;
            }
          
          gen = alloc_GenericData (ptr->genericId, cl, binfo);
	  gen->glyph = g;
	  if (ptr->flags != 0xff) gen->flags = ptr->flags;
	  gen->attribute = ptr->attribute;
        }
      else if (!modify_ok)
        Post1("Attempt to create generic with existing id %8x", 
              ptr->genericId);
      else{
	/* modify the generic */
	int modified = 0;
	if (cl != gen->cl)
	  {
	    modified = 1;
	    gen->cl = cl;
	  }
	if (gen->glyph != g)
	  {
	    modified = 1;
	    gen->glyph = g;
	  }
	if (ptr->flags != 0xff) 
	  {
	    modified = 1;
	    gen->flags = ptr->flags;
	  }
	if (gen->attribute != ptr->attribute)
	  {
	    modified = 1;
	    gen->attribute = ptr->attribute;
	  }
	if (modified)
	  BUF_FACECHANGE (XBUFFER (binfo->emacs_buffer))++;
      }
    }
}

static void
insert_one_extent (CExtent* ptr, BufferInfo* binfo, int modify_ok)
{
  Energize_Extent_Data *ext;
  GenericData *gen;
  int extent_start;
  int extent_end;
  int set_extent_endpoints = 1;

  ext = get_extent_data (ptr->extentId, binfo);
  
  if (!ext)
    ext = alloc_Energize_Extent_Data (ptr->extentId, binfo);
  else if (!modify_ok) 
    Post1 ("Creating extent with existing id %8x", ptr->extentId);

  ext->extentType = ptr->extentType;
  
  if (ignore_kernel) return;
  
  switch (ptr->extentType)
    {
    case CEAttribute:
      ext->u.attr.attrValue = ptr->data;
      break;
      
    case CEAbbreviation:
      ext->u.abbrev.isOpened = ptr->data;
      break;
      
    case CEWriteProtect:
      break;
      
    case CEGeneric:
      gen = get_generic (ptr->data, binfo);
      if (!gen) 
	{
	  Post1 ("NewExtents: Nonexistent generic data %8x", ptr->data);
	  return;
	}
      ext->u.generic.gData = gen;
      break;
      
    default:
      Post1 ("Unknown extent type %d", ptr->extentType);
      break;
    }
  
  /* instruct redisplay to recompute the screen */
  BUF_FACECHANGE (XBUFFER (binfo->emacs_buffer))++;
  
  /* ptr->startPosition == ptr->endPosition == ~0 means to not change
   * the extent endpoints */
  if (ptr->startPosition == ~0 && ptr->endPosition == ~0)
    {
      set_extent_endpoints = 0;
      extent_start = ~0;
      extent_end = ~0;
    }
  else
    {
      struct buffer *b = XBUFFER (binfo->emacs_buffer);
      extent_start = EmacsPosForEnergizePos (ptr->startPosition);
      extent_end = EmacsPosForEnergizePos (ptr->endPosition);
      
      if (extent_end >= BUF_ZV (b))
	{
	  extent_end = BUF_ZV (b) - 1;
	  if (extent_start >= extent_end)
	    extent_start = extent_end - 1;
	}
    }      

  /* no zero width extent */
  if (set_extent_endpoints && extent_start == extent_end)
    extent_end += 1;

  /* this function calls update_extent if the the ext->extent already
     exists */

  make_extent_for_data (binfo, ext, extent_start, extent_end,
			set_extent_endpoints);
}

/* Parse GenericData from the connection buffer.  Defines them for the buffer
 * given as argument.  Creates the Emacs extents while parsing.
 * Energize sends the extents ordered by increasing starting position.
 * Emacs is __much__ faster at inserting them in decreasing starting position
 * also for overlaps to work correctly the outmost extents have to be
 * inserted first.  This is what the recursive function is trying to do.
 */
static void
ParseExtents (Connection *conn, unsigned int number, 
              BufferInfo *binfo, unsigned int modify_ok, int extent_offset)
{
  CExtent* all_extents;
  int i;

  /* Gets the extents from the connection */
  all_extents = CGetN (conn, CExtent, number);

  /* adjusts the endpoints with the offset */
  for (i = 0; i < number; i++)
    {
      if (all_extents [i].startPosition != ~0)
	all_extents [i].startPosition += extent_offset;
      if (all_extents [i].endPosition != ~0)
	all_extents [i].endPosition += extent_offset;
    }
  
  /* inserts them all */
  for (i = number - 1; i >= 0; i--)
    {
      insert_one_extent (all_extents + i, binfo, modify_ok);
    }
}

/* Parses a CBuffer in the connection stream. If (delete_from != delete_to) 
   all characters in this range must be deleted.
   */

static int
string_buffer_compare (char *string, int string_len, 
                       struct buffer *buf, int bufpos)
{
  int first_section_length =
    (bufpos < BUF_GPT(buf))?(min (string_len, (BUF_GPT(buf) - bufpos))):0;
  int second_section_length = 
    ((bufpos + string_len) >= BUF_GPT(buf))?
      (string_len - first_section_length):0;
  
  /* degenerate case, which we consider to be "true" */
  if (string_len == 0) return 0;

  /* string won't fit in the buffer, so comparison fails */
  if (BUF_Z(buf) < (bufpos + string_len)) return -1;

  /* bad starting position, so comparison fails */
  if (bufpos < BUF_BEG (buf)) return -1;

  if (first_section_length > 0)
    /* there is a first section */
    {
      char *first_section_chars = (char *) BUF_CHAR_ADDRESS (buf, bufpos);
      int comp = strncmp (string, first_section_chars, first_section_length);

      if (comp) return comp;
    }

  if (second_section_length > 0)
    /* there is a second section */
    {
      char *second_section_chars = 
        (char *) BUF_CHAR_ADDRESS (buf, BUF_GPT(buf));
      int comp = strncmp (string + first_section_length, 
                          second_section_chars, second_section_length);

      if (comp) return comp;
    }

  return 0;
}

/* called by unwind protect, from within ParseBuffer and HandleRemoveExtents */
static Lisp_Object
restore_buffer_state (Lisp_Object state_cons)
{
  BufferInfo *binfo;
  Lisp_Object bufferId_cons = Fcar (state_cons);
  unsigned int bufferId = lisp_to_word (bufferId_cons);
  Lisp_Object buffer_modified_state = Fcar (Fcdr (state_cons));
  Lisp_Object clear_undo_list = Fcdr (Fcdr (state_cons));
  
  if (bufferId != 0)
    {
      if (energize_connection && (binfo = get_buffer_info_for_id (bufferId, energize_connection))
          && binfo->emacs_buffer)
	{
	  /* Always ignore what Energize tells us about the buffer read-only
	     state.  For files Emacs knows better and for non-file buffers
	     Emacs is hacking the read-only state anyway so let it be. */
	  XBUFFER(binfo->emacs_buffer)->read_only = buffer_modified_state;
          if (!NILP (clear_undo_list))
            XBUFFER(binfo->emacs_buffer)->undo_list = Qnil;
	}
    }
  else
    /* this is just temporary */
    Post("Bad bufferId cons cell!");
  return Qnil;
}

static void
rename_the_buffer (Lisp_Object new_name)
{
  int count = 0;
  char number [8];
  struct gcpro gcpro1;
  
  Lisp_Object name = new_name;
  GCPRO1 (name);
  while (!NILP (Fget_buffer (name)))
    {
      sprintf (number, "<%d>", ++count);
      name = concat2 (new_name, build_string (number));
    }
  Frename_buffer (name, Qnil);
  UNGCPRO;
}

Lisp_Object
safe_funcall_hook (Lisp_Object hook, int nargs, Lisp_Object arg1,
		   Lisp_Object arg2, Lisp_Object arg3);

extern void del_range (int, int);

extern Lisp_Object Fdelete_extent (Lisp_Object);

static int
destroy_if_energize_extent (EXTENT ext, void* arg)
{
  if (energize_extent_data (ext))
    {
      Lisp_Object extent;
      XSET (extent, Lisp_Extent, ext);
      Fdelete_extent (extent);
    }
  return 0;
}

/*extern void process_extents_for_destruction (int, int, struct buffer *);*/
static void
destroy_all_energize_extents (struct buffer* buf)
{
/*  if (energize_font_lock_p)*/
    map_extents (BUF_BEG (buf), BUF_Z (buf), NULL, destroy_if_energize_extent,
		 NULL, buf, 1);
/*  else
    process_extents_for_destruction (BUF_BEG (buf), BUF_Z (buf), buf);*/
}

static void
ParseBuffer (Connection *conn, CBuffer *cbu, Editor *editor, 
             EnergizePos delete_from, EnergizePos delete_to, Window win, int relative_p)
{
  char *name;
  ReqLen name_len;
  char *pathname_str;
  ReqLen pathname_len;
  char *buffer_class_str;
  ReqLen buffer_class_len;
  Lisp_Object pathname = Qnil;
  Lisp_Object pathname_directory = Qnil;
  Lisp_Object buffer_name = Qnil;
  Lisp_Object filename = Qnil;
#if 1
  Lisp_Object display_window = Qnil;
#endif
  BufferInfo *binfo;
  int modifying_p = 0;
  EmacsPos previous_point;
  EmacsPos from;
  EmacsPos to;
#if 1
  EmacsPos display_start = 1;
#endif
  char *text;
  ReqLen text_len;
  Lisp_Object modified_buffer_flag;
  int count = specpdl_ptr - specpdl;
  int extent_offset;
  Lisp_Object restore_buffer_state_cons;
  int should_keep_window_start = 1;
  int no_text_deleted = 0;

  struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;  

  record_unwind_protect (save_restriction_restore, save_restriction_save ());

  Fwiden ();

  GCPRO4 (buffer_name, pathname, pathname_directory, filename);

  name = CGetVstring (conn, &name_len);

  /* read the pathname and buffer-class -- Editor Protocol > 0 */
  pathname_str = CGetVstring (conn, &pathname_len);
  buffer_class_str = CGetVstring (conn, &buffer_class_len);

  if (name_len)
    buffer_name = build_string (name);
  if (pathname_len)
    pathname = build_string (pathname_str);
  
  /* set up pathname_directory */
  if (!NILP (pathname))
    {
      if (NILP (Ffile_directory_p (pathname)))
        pathname_directory = Ffile_name_directory (pathname);
      else
        pathname_directory = pathname;
    }

  /* make sure that pathname_directory ends with a '/', if it exists */
  if (!NILP (pathname_directory))
    {
      char *str = (char *) XSTRING (pathname_directory)->data;
      int size = XSTRING (pathname_directory)->size;
      if (str[size - 1] != '/')
        {
          Lisp_Object tmp = make_string (str, size + 1);
          XSTRING (tmp)->data[size] = '/';
          pathname_directory = tmp;
        }
    }


  /* get or create the BufferInfo */
  if (binfo = get_buffer_info_for_id (cbu->bufferId, editor)) 
    modifying_p = 1;
  else
    {      
      if (NILP (buffer_name))
        {
          char *dummy = "*Unnamed Energize Buffer*";
          buffer_name = build_string (dummy);
        }
      /* create new buffer */
      binfo = alloc_BufferInfo (cbu->bufferId, buffer_name, pathname,
				buffer_class_str, editor, win);
      XBUFFER (binfo->emacs_buffer)->read_only =
	cbu->flags == CBReadOnly ? Qt : Qnil;
    }
  
  /* remember where we were in which buffer before we change things */
  if (current_buffer != XBUFFER (binfo->emacs_buffer))
    {
      record_unwind_protect (save_excursion_restore, save_excursion_save ());
      Fset_buffer (binfo->emacs_buffer);
    }

  /* set default-directory */
  if (!NILP (pathname_directory))
    {
      if (!NILP (Ffile_directory_p (pathname_directory))
	  && !NILP (Ffile_executable_p (pathname_directory)))
	Fset (Qdefault_directory, pathname_directory);
      else
      	Fset (Qdefault_directory, Qnil);
    }

  /* set file name unless it's a directory */
  if (!NILP (pathname) && NILP (Ffile_directory_p (pathname)))
    {
      filename = Fexpand_file_name (pathname, Qnil);
      Fset (Qbuffer_file_name, filename);
    }

  /* set buffer name */
  if (!NILP (buffer_name))
    {
      if (modifying_p
	  && strcmp ((char*)XSTRING (buffer_name)->data,
		     (char*)
                     XSTRING (XBUFFER (binfo->emacs_buffer)->name)->data))
	rename_the_buffer (buffer_name);
    }
  
  if (modifying_p)
    {
      safe_funcall_hook (Venergize_kernel_modification_hook, 0, 0, 0, 0);
      /* Make sure buffer is current after the hook */
      Fset_buffer (binfo->emacs_buffer);
    }

  modified_buffer_flag = Fbuffer_modified_p (binfo->emacs_buffer);

  /* enables buffer edits */
  restore_buffer_state_cons =
    Fcons (word_to_lisp (cbu->bufferId),
	   Fcons (XBUFFER(binfo->emacs_buffer)->read_only, Qt));
  record_unwind_protect (restore_buffer_state, restore_buffer_state_cons);
  XBUFFER(binfo->emacs_buffer)->read_only = Qnil;
  
  /* any changes here should take place "underneath" these hooks, I think */
  specbind (Qenergize_buffer_modified_hook, Qnil);
  specbind (Qfirst_change_function, Qnil);
  specbind (Qbefore_change_function, Qnil);
  /* As energize does not use the after-change-function it's not useful to
     bind it to NIL */
  /* specbind (Qafter_change_function, Qnil); */
  specbind (Qinside_parse_buffer, Qt);
  specbind (Qbuffer_undo_list, Qt);

  XBUFFER (binfo->emacs_buffer)->undo_list = Qt;
  
  /* EmacsPosForEnergizePos uses the current-buffer */
  from = EmacsPosForEnergizePos (delete_from);
  to = EmacsPosForEnergizePos (delete_to);

  /* get text */
  text = CGetVstring (conn, &text_len);

  if (modifying_p && (from != to || text_len))
    /* updates the visited file modtime */
    Fset_buffer_modtime (binfo->emacs_buffer, Qnil);

  if (!modifying_p)
    {
      /* clears the buffer in case we re-use a non-energize buffer */
      previous_point = 1;
      Fset_buffer (binfo->emacs_buffer);
      del_range (BEG, Z);
    }
  else
    {
#if 1
      display_window = Fget_buffer_window (binfo->emacs_buffer, Qnil, Qnil);
#endif
      previous_point = point;

#if 1
      if (!NILP (display_window))
        display_start = Fmarker_position (XWINDOW (display_window)->start);
#endif

      if (from != to)
        {
          struct buffer *buf = XBUFFER (binfo->emacs_buffer);
                        
	  Fset_buffer (binfo->emacs_buffer);
          if (!NILP (binfo->output_mark)
              && marker_position (binfo->output_mark) >= from)
            Fset_marker (binfo->output_mark, make_number (from),
                         binfo->emacs_buffer);
          if (((to - from) == text_len) &&
              !string_buffer_compare (text, text_len, buf, from))
            /* the new text is the same as the old text, don't clear
               the undo list*/
            {
              Fsetcdr (Fcdr (restore_buffer_state_cons), Qnil);
              no_text_deleted = 1;
	      destroy_all_energize_extents (buf);
            }
	  else
            {
              /* Do not keep window start if we actually delete text */
              should_keep_window_start = 0;
	      Fset_buffer (binfo->emacs_buffer);
	      destroy_all_energize_extents (buf);
              del_range (from, to);
            }
        }
      else if (!text_len)
        /* if there is no text and we didn't delete anything, 
           don't clear the undo_list slot */
        Fsetcdr (Fcdr (restore_buffer_state_cons), Qnil);
    }
  
  /* buffer type */
  if (cbu->flags != 0xff && cbu->flags != binfo->flags)
    {
      if (!modifying_p)
	switch (cbu->flags)
	  {
	  case CBEditable:
	    break;
	    
	  case CBReadOnly:
	    /* handle it at the end !! */
	    break;
	    
	  case CBUserInput:
	    {
	      Lisp_Object buffer_local_variable_name = 
		Qenergize_user_input_buffer_mark;
	      binfo->output_mark = Fmake_marker ();	
	      Fset_marker (binfo->output_mark, 1, binfo->emacs_buffer);
	      /* make sure that this guy doesn't get GC'd out from under us */
	      Fmake_local_variable (buffer_local_variable_name);
	      Fput (buffer_local_variable_name, intern ("permanent-local"),
		    Qt);
	      Fset (buffer_local_variable_name, binfo->output_mark);
	      call0 (Qenergize_user_input_mode);
	      /* Make sure buffer is current after the hook */
	      Fset_buffer (binfo->emacs_buffer);
	    }
	    break;
	  }
      
      binfo->flags = cbu->flags;
    }
  
  if (!relative_p)
    extent_offset = 0;
  else if (!NILP (binfo->output_mark))
    extent_offset = Fmarker_position (binfo->output_mark) + text_len - 1;
  else
    extent_offset = BUF_Z (XBUFFER (binfo->emacs_buffer)) + text_len - 1;
  
  if (text_len)
    {
      if (!NILP (binfo->output_mark))
        {
	  Fset_buffer (binfo->emacs_buffer);
          if (XMARKER (binfo->output_mark)->buffer)
            Fgoto_char (binfo->output_mark);
          else
	    /* This should not happen */
	    Fgoto_char (BUF_ZV (XBUFFER (binfo->emacs_buffer)));
          
          if (point <= previous_point)
	    {
#if 1
              display_start += text_len;
#endif
	      previous_point += text_len;
	    }
	  insert_raw_string (text, text_len);
	  Fset_marker (binfo->output_mark, make_number (point),
		       binfo->emacs_buffer);
        }
      else if (modifying_p)
        {
          Fgoto_char (from);
          if (!no_text_deleted)
            insert_raw_string (text, text_len);
        }
      else
        insert_raw_string (text, text_len);
      
      previous_point = Fgoto_char (previous_point);
      
#if 1
      if (!NILP (display_window))
        {
	  
          Fset_marker (XWINDOW (display_window)->pointm, previous_point,
                       binfo->emacs_buffer);
	  if (NILP (binfo->output_mark) && should_keep_window_start)
	    {
	      Fset_marker (XWINDOW (display_window)->start, display_start,
			   binfo->emacs_buffer);
	      XWINDOW (display_window)->force_start = Qt;
	    }
        }
#endif
    }
    
  
  /* Classes, generics and extents */
  ParseClasses (conn, cbu->nClass, binfo, modifying_p);
  ParseGenerics (conn, cbu->nGeneric, binfo, modifying_p);
  ParseExtents (conn, cbu->nExtent, binfo, modifying_p, extent_offset);
  
  /* Restore the modified bit */
  Fset_buffer (binfo->emacs_buffer);
  Fset_buffer_modified_p (modified_buffer_flag);
  
  /* restore modified hooks and globals, and return the previous buffer */
  UNGCPRO;
  unbind_to (count, Qnil);
  inside_parse_buffer = 0;
}

/* kill an Energize buffer */
static void
forget_buffer (BufferInfo *binfo)
{
  int i;
  Lisp_Object buffer = binfo->emacs_buffer;

  remove_buffer_info (binfo->id, buffer, binfo->editor);
  energize_buffers_list = Fdelq (buffer, energize_buffers_list);

  /* if there was an associated screen */
  if (binfo->screen != Qnil)
    Fdelete_screen (binfo->screen);

  /* Also delete the dialog boxes associated to the buffer */
  BLOCK_INPUT;
  for (i = 0; i < binfo->n_p_sheets; i++)
    {
      lw_destroy_all_widgets (binfo->p_sheet_ids [i]);
      if (binfo->p_sheet_ids [i] == debuggerpanel_sheet)
	{
	  debuggerpanel_sheet = 0;
	  desired_debuggerpanel_exposed_p = 0;
	}
    }
  UNBLOCK_INPUT;

  free_buffer_info (binfo);

  XBUFFER(buffer)->undo_list = Qnil;
  /* flush the buffer SOE before flushing the extents */
  free_buffer_cached_stack (XBUFFER(buffer));
  XBUFFER(buffer)->extents = Qnil;
}

/********************** Request-related utilities ************************/

/* outputs a single extent in the connection buffer */
static void
WriteExtent 
  (Connection *conn, Energize_Extent_Data *ext, unsigned int start, unsigned int end)
{
  switch (ext->extentType)
    {
    case CEAttribute:
      CWriteExtent (conn, CEAttribute, ext->id, start, end,
                    (EId)ext->u.attr.attrValue);
      break;
      
    case CEAbbreviation:
      CWriteExtent (conn, CEAbbreviation, ext->id, start, end,
                    (EId)ext->u.abbrev.isOpened);
      break;
      
    case CEGeneric:
      CWriteExtent (conn, CEGeneric, ext->id, start, end, 0);
      break;

    case CEWriteProtect:
      CWriteExtent (conn, CEWriteProtect, ext->id, start, end, 0);
      break;
    }
}

extern void move_gap (int);

/* Utility to return a char* to the contents of the current buffer */
static char*
get_buffer_as_string (unsigned int *len)
{
  if (BEGV < GPT && ZV > GPT) move_gap (BEGV);
  if (len) *len = ZV - BEGV;
  return (char *)CHAR_ADDRESS (BEGV);
}

/* Function called by map_extents in SaveBufferToEnergize. Outputs the
   extents for the extents corresponding to Energize objects, and
   increments the n_extents count. */

static int
write_an_extent (Lisp_Object extent_obj, void *arg)
{
  binfo_and_n_extents *bane = (binfo_and_n_extents*)arg;
  Energize_Extent_Data *ext = extent_to_data (extent_obj);
  if (ext)
    {
      unsigned int first = XINT (Fextent_start_position (extent_obj));
      unsigned int last = XINT (Fextent_end_position (extent_obj));
      WriteExtent (bane->binfo->editor->conn, ext, EnergizePosForEmacsPos (first),
                   EnergizePosForEmacsPos (last));
      bane->n_extents += 1;
    }
  return 0;
}

/* Sends a BufferSaved request to energize for binfo */
static void
SaveBufferToEnergize (BufferInfo *binfo)
{
  Connection *conn = binfo->editor->conn;
  EId bufferId = binfo->id;
  CBuffer *cbu;
  CEditorRequest *req;
  char *text;
  int length;
  struct buffer *cur_buff = current_buffer;
  int count = specpdl_ptr - specpdl;
  Lisp_Object file_name;
  
  binfo_and_n_extents bane;
  
  /* selects the buffer as current */
  Fset_buffer (binfo->emacs_buffer);
  
  /* write header */
  cbu = CWriteBufferSavedHeader (conn);
  cbu->bufferId = bufferId;
  cbu->flags = 0;
  cbu->nClass = 0;
  cbu->nGeneric = 0;

  /* file name */
  file_name = current_buffer->filename;
  if (STRINGP (file_name))
    CWriteVstring0 (conn, (char *)XSTRING (file_name)->data);
  else
    CWriteVstring0 (conn, "");
  CWriteVstring0 (conn, "");    /* directory name */
  CWriteVstring0 (conn, "");    /* buffer name */
  
  /* write the text */
  CNeedOutputSize (conn, (Z - BEG) + 9);
  text = get_buffer_as_string ((unsigned int *) &length);
  CWriteVstringLen (conn, text, length);
  
  /* write the extents */
  bane.binfo = binfo;
  bane.n_extents = 0;
  map_extents (BUF_BEG (current_buffer), BUF_Z (current_buffer), 
	       write_an_extent, 0, &bane, XBUFFER(binfo->emacs_buffer), 1);
  
  /* update nextent in request's header */
  req = (CEditorRequest *)conn->header;
  req->buffersaved.buffer.nExtent = bane.n_extents;
  CWriteLength (conn);
  CWriteRequestBuffer (conn);
  
  /* sets the flags so that we will warn Energize about more modifications */
  binfo->modified_state = 0;

  /* Mark the buffer as non editable so that we will ask Energize about it
     before modifying it again */
  binfo->editable = 0;

  /* restores the buffer as current */
  internal_set_buffer (cur_buff);
  unbind_to (count, Qnil);
}

unsigned long
generic_id_for_extent (Energize_Extent_Data *ext)
{
  return ext ? ext->id : 0;
}


/********************** Menu ("keywords") operations **********************/


/* gets the menu for the buffer/extent pair at the head of the request buffer.
** returns the propose choice request if succeeds, 0 otherwise (kernel 
** connection closed, or not connected) */

static Lisp_Object
get_energize_menu (Lisp_Object buffer, Lisp_Object extent_obj, int selection_p,
		   Lisp_Object only_name)
{
  Connection*	conn;
  EId	buffer_id;
  EId	extent_id;
  int		result;
  struct reply_wait rw;
  struct gcpro gcpro1, gcpro2;

  if (!get_energize_connection_and_buffer_id (buffer,
					      (void**)&conn,
					      (long*)&buffer_id))
    return 0;

  if (EXTENTP (extent_obj))
    extent_id = generic_id_for_extent (extent_to_data (extent_obj));
  else
    extent_id = 0;
  
  CWriteQueryChoicesRequest (conn, buffer_id, extent_id);
  conn->header->data =
    selection_p ? CEChasCharSelection | CEChasObjectSelection : 0;
  conn->header->serial = ++request_serial_number;
  CWriteRequestBuffer (conn);

  /* wait for the acknowledge */
  rw.serial = request_serial_number;
  rw.objectId = buffer_id;
  rw.genericId = extent_id;
  rw.menu_result = Qnil;
  rw.only_name = only_name;
  
  GCPRO2 (rw.menu_result, rw.only_name);
  wait_for_reply (&rw);
  result = rw.menu_result;
  UNGCPRO;
  return result;
}

static int
something_answered_p (void* arg)
{
  struct reply_wait* rw = (struct reply_wait*)arg;
  return rw->answered_p || !energize_connection || !energize_connection->conn;
}


static void
push_wait (struct reply_wait* rw)
{
  rw->next = global_reply_wait;
  global_reply_wait = rw;
}

static Lisp_Object
remove_wait (Lisp_Object obj)
{
  struct reply_wait* gw;
  struct reply_wait* previous;
  struct reply_wait* rw = (struct reply_wait*)lisp_to_word (obj);

  for (previous = 0, gw = global_reply_wait;
       gw != rw;
       previous = gw, gw = gw->next);
  if (previous)
    previous->next = gw->next;
  else
    global_reply_wait = gw->next;
  return Qnil;
}

static struct reply_wait*
find_wait_reply (int serial)
{
  struct reply_wait* gw;
  for (gw = global_reply_wait; gw && gw->serial != serial; gw = gw->next);
  return gw;
}


static int
wait_for_reply (struct reply_wait* rw)
{
  int count = specpdl_ptr - specpdl;
  rw->answered_p = 0;
  push_wait (rw);
  record_unwind_protect (remove_wait, word_to_lisp ((int)rw));
  wait_delaying_user_input (something_answered_p, rw);
  unbind_to (count, Qnil);
  return rw->answered_p;
}

static void
execute_energize_menu (Lisp_Object buffer, Energize_Extent_Data* ext, char* name,
		       EId item_id, EId flags, Lisp_Object selection,
		       Lisp_Object no_confirm)
{
  Connection*	conn;
  EId	buffer_id;
  EId	extent_id;
  BufferInfo*	binfo;
  struct reply_wait rw;
  
  if (!get_energize_connection_and_buffer_id (buffer, (void**)&conn,
					      (long*)&buffer_id))
    return;

  extent_id = generic_id_for_extent (ext);
  
  if ((flags & CKBuffer) && Fbuffer_modified_p (buffer))
    {
      /* saves buffer if requested and needed */
      binfo = get_buffer_info_for_emacs_buffer (buffer, energize_connection);
      if (binfo)
	SaveBufferToEnergize (binfo);
    }

  CWriteExecuteChoicesRequest (conn, buffer_id, extent_id, item_id, 0, 0);
  /* send the menu name */
  if (energize_connection->minor >= 7)
    CWriteVstring0 (conn, name);
  conn->header->serial = ++request_serial_number;
  conn->header->data = 0;
  switch (XTYPE (selection))
    {
    case Lisp_String:
      conn->header->data |= CEChasCharSelection;
      CWriteVstringLen (conn, (char*)XSTRING (selection)->data,
			XSTRING (selection)->size);
      break;
    case Lisp_Vector:
      {
	int i;
	EId data;
	conn->header->data |= CEChasObjectSelection;

	/* writes the length */
	data = XVECTOR (selection)->size;
	CWrite (conn, EId, &data);	

	/* writes the elements */
	for (i = 0; i < XVECTOR (selection)->size; i++)
	  {
	    if (CONSP (XVECTOR (selection)->contents [i]))
	      data = lisp_to_word (XVECTOR (selection)->contents [i]);
	    else
	      data = XVECTOR (selection)->contents [i];
	    CWrite (conn, EId, &data);
	  }
      }
      break;

    case Lisp_Cons:
      {
	Lisp_Object type = Fcar (selection);
	Lisp_Object value = Fcdr (selection);
	if (EQ (type, intern ("ENERGIZE_OBJECT"))
	    && STRINGP (value))
	  {
	    conn->header->data |= CEChasObjectSelection;
	    CWriteN (conn, char, XSTRING (value)->data,
		     XSTRING (value)->size);
	  }
      }
      break;

    default:
      if (!NILP (selection))
	error ("unrecognised energize selection");
    }
  if (!NILP (no_confirm))
    conn->header->data |= CECnoConfirm;
  CWriteLength (conn);
  CWriteRequestBuffer (conn);

  /* wait for the acknowledge */
  rw.serial = request_serial_number;
  rw.objectId = buffer_id;
  rw.genericId = extent_id;
  rw.itemId = item_id;
  rw.message = 0;
  
  if (wait_for_reply (&rw) && !rw.status)
    {
      char message [128];
      if (energize_connection && energize_connection->conn)
	sprintf (message, "Energize command failed: %.80s",
		 (rw.message ? rw.message : "(null)"));
      else
	sprintf (message, "Connection to Energize was closed.");
      if (rw.message)
	xfree (rw.message);
      error (message);
    }
  else
    {
      if (rw.message)
	xfree (rw.message);
      if (!energize_connection)
	error ("Connection to Energize was closed.");
    }
 }

/* Returns a list of vectors representing the menu choices.  Next request
   in connection must be a ProposeChoices.  The list is 
   (buffer extent <item1> ... <itemN>).  <itemI> is (name id1 id2 flags).
   Idi is (high .  low).  We build the list in reverse order and nreverse
   it.  If (only_name != 0), we only return the item of named only_name as
   a vector.  */

static Lisp_Object
list_choices (Lisp_Object buffer, Lisp_Object extent_obj, 
              Lisp_Object only_name, CProposeChoicesRequest* creq)
{
  Connection *conn;
  int i;
  Lisp_Object item_list;
  Lisp_Object item;
  struct Lisp_Vector *v;
  struct gcpro gcpro1, gcpro2, gcpro3;
  CChoice *choice;
  ReqLen name_length;
  char *name;
  char *arg_name;

  if (energize_connection && energize_connection->conn)
    conn = energize_connection->conn;
  else
    return Qnil;
  
  if (!creq || creq->head.reqType != ProposeChoicesRType)
    {
      CSkipRequest (conn);
      return Qnil;
    }
  
  item = Qnil;
  item_list = Qnil;
  
  GCPRO3 (only_name, item_list, item);
  
  for (i = 0; i < (int)(creq->nChoices); i++)
    {
      choice = CGet (conn, CChoice);
      name = CGetVstring (conn, &name_length);
      if (!name_length)
	continue;

      /* the argument, if passed, is another string after the NUL (!)
       * this is a quick hack to provide cheap arguments to menus entries */
      arg_name = strchr (name, 0240);
      if (arg_name)
	{
	  *arg_name= 0;
	  arg_name += 1;
	}

      if (!NILP (only_name))
	{
	  if (!strcmp ((char*)XSTRING(only_name)->data, name))
	    {
	      if (NILP (item))
		{
		  item = Fmake_vector (5, Qnil);
		  v = XVECTOR (item);
		  v->contents [0] = only_name;
		}
              v->contents [1] = word_to_lisp (choice->choiceId);
              v->contents [2] = Qnil;
              v->contents [3] = choice->flags;
	      v->contents [4] = arg_name ? build_string (arg_name) : Qnil;
	    }
	}
      else
	{
	  item = Fmake_vector (5, Qnil);
	  v = XVECTOR (item);
	  v->contents [0] = build_string (name);
	  v->contents [1] = word_to_lisp (choice->choiceId);
	  v->contents [2] = Qnil;
	  v->contents [3] = choice->flags;
	  v->contents [4] = arg_name ? build_string (arg_name) : Qnil;
	  item_list = Fcons (item, item_list); /* pushes in the list */
        }
    }
  
  if (NILP (only_name))
    item_list = Fcons (buffer, Fcons (extent_obj, Fnreverse (item_list)));
  UNGCPRO;
  
  return NILP (only_name) ? item_list : item;
}

DEFUN ("energize-list-menu", Fenergize_list_menu,
       Senergize_list_menu, 3, 4, 0,
       "(energize-list-menu buffer extent selection-p [only-name])\n\
Request the set of menu options from the Energize server that are\n\
appropriate to the buffer and the extent.  Extent can be (), in which case\n\
the options are requested for the whole buffer.  Selection-p tells\n\
if the selection is available on the dislpay emacs is using. \n\
Returns the options as\n\
a list that can be passed to energize-activate-menu.  Items\n\
in the list can also be passed to energize-execute-menu-item.\n\
The list is (buffer extent or () <item1> ... <itemN>).\n\
where <itemI> is (name id1 id2 flags); idI is (high . low).\n\
If optional argument only-name is provided only the item with name only-name\n\
is returned, or () if no such item exists.")
     (buffer, extent_obj, selection_p, only_name)
     Lisp_Object buffer, extent_obj, selection_p, only_name;
{
  Lisp_Object res;
  CHECK_BUFFER (buffer, 1);
  
  if (!energize_connection || !energize_connection->conn) return Qnil;

  if (!NILP (only_name))
    CHECK_STRING (only_name, 1);
  
  res = get_energize_menu (buffer, extent_obj, selection_p != Qnil,
			   only_name);
  notify_delayed_requests ();
  return res;
}

DEFUN ("energize-execute-menu-item", Fenergize_execute_menu_item,
       Senergize_execute_menu_item, 3, 5, 0,
       "(energize-execute-menu-item buffer extent item argument no-confirm)\n\
Item is a vector received by energize-list-menu.  Sends a request to\n\
execute the code associated to this menu inside the Energize server.\n\
Optional fourth argument is a string or a vector to be used as the selection\n\
for entry disabled because they need the selection.\n\
Optional fifth argument, if non NIL, tells Energize to not request \n\
confirmation before executing the command.")
(buffer, extent_obj, item, selection, no_confirm)
Lisp_Object buffer, extent_obj, item, selection, no_confirm;
{
  struct Lisp_Vector *v;
  
  if (!energize_connection || !energize_connection->conn) return Qnil;

  CHECK_BUFFER (buffer, 1);
  CHECK_VECTOR (item, 1);
  v = XVECTOR (item);

  if (v->size != 4)
    error ("Bad menu item to energize-execute-menu-item");
  
  /* ignore the flags for now */
  execute_energize_menu (buffer, extent_to_data (extent_obj),
			 (char*)XSTRING (v->contents [0])->data,
			 lisp_to_word (v->contents [1]),
			 v->contents [3],
			 selection,
			 no_confirm);

  return Qt;
}

DEFUN ("energize-execute-command-internal", Fenergize_execute_command_internal,
       Senergize_execute_command_internal, 3, 5, 0,
       "(energize-execute-command-internal buffer extent command argument no-confirm)\n\
Command is a string naming an energize command.  Sends a request to\n\
execute this command inside the Energize server.\n\
Optional fourth argument is a string or a vector to be used as the selection.\n\
Optional fifth argument, if non NIL, tells Energize to not request \n\
confirmation before executing the command.\n\
\n\
See also 'energize-list-menu'.")
(buffer, extent_obj, command, selection, no_confirm)
Lisp_Object buffer, extent_obj, command, selection, no_confirm;
{
  if (!energize_connection || !energize_connection->conn) return Qnil;

  CHECK_BUFFER (buffer, 1);
  CHECK_STRING (command, 1);
  
  execute_energize_menu (buffer, extent_to_data (extent_obj),
			 (char*)XSTRING (command)->data, 0, 0, selection,
			 no_confirm);

  return Qt;
}

/********************************* kill buffer interface ****************/

DEFUN ("energize-buffer-type-internal",
       Fenergize_buffer_type, Senergize_buffer_type,
       1, 1, 0,
       "(energize-buffer-type buffer) returns a symbol denoting the buffer \n\
type if buffer is an Energize buffer, else it returns NIL.")
	(buffer)
	Lisp_Object buffer;
{
  if (!energize_connection) return Qnil;
  
  CHECK_BUFFER (buffer, 1);
  return get_buffer_type_for_emacs_buffer (buffer, energize_connection);
}

DEFUN ("set-energize-buffer-type-internal", 
       Fset_energize_buffer_type_internal, 
       Sset_energize_buffer_type_internal, 2, 2, 0,
 "(set-energize-buffer-type-internal buffer type) returns the type \n\
symbol which is the new buffer-type, if the buffer is an Energize buffer and\n\
the type is non-NIL symbol, else it returns NIL.")
   (buffer, type)
    Lisp_Object buffer, type;
{
  BufferInfo *binfo;
  
  if (!energize_connection || (type == Qnil)) return Qnil;
  
  CHECK_BUFFER (buffer, 1);
  CHECK_SYMBOL (type, 1);
  
  if (!(binfo =
	get_buffer_info_for_emacs_buffer (buffer, energize_connection))) 
    return Qnil;
  else 
    return
      set_buffer_type_for_emacs_buffer (buffer, energize_connection, type);
}

DEFUN ("energize-buffer-p", Fenergize_buffer_p, Senergize_buffer_p, 1, 1, 0,
       "(energize-buffer-p buffer) returns T if buffer is \n\
an Energize buffer, otherwise NIL.")
  (buffer)
  Lisp_Object buffer;
{
  BufferInfo *binfo;
  
  if (!energize_connection) return Qnil;
  
  CHECK_BUFFER (buffer, 1);
  if (!(binfo =
	get_buffer_info_for_emacs_buffer (buffer, energize_connection)))
    return Qnil;
  else
    return Qt;
}  

DEFUN ("energize-buffer-id", Fenergize_buffer_id, Senergize_buffer_id, 1, 1, 0,
       "(energize-buffer-id buffer)\n\
Returns (high . low) if buffer is an Energize buffer, () otherwise")
     (buffer)
     Lisp_Object buffer;
{
  BufferInfo *binfo;
  
  if (!energize_connection) return Qnil;
  
  CHECK_BUFFER (buffer, 1);
  if (!(binfo =
	get_buffer_info_for_emacs_buffer (buffer, energize_connection)))
    return Qnil;
  else
    return word_to_lisp (binfo->id);
}  

DEFUN ("energize-request-kill-buffer", Fenergize_request_kill_buffer,
       Senergize_request_kill_buffer, 1, 1, 0,
       "(energize-request-kill-buffer buffer)\n\
Sends a request to energize for killing buffer")
   (buffer)
   Lisp_Object buffer;
{
  BufferInfo *binfo;
  
  if (!energize_connection) return Qnil;
  
  CHECK_BUFFER (buffer, 1);
  if (!(binfo = get_buffer_info_for_emacs_buffer (buffer, energize_connection))) 
    return Qnil;
  
  /* Tell Energize about it if connected */
  if (energize_connection->conn)
    {
      CWriteKillBufferHeader (energize_connection->conn, binfo->id);
      CWriteRequestBuffer (energize_connection->conn);
    }

  /* Clears the internal state */
  forget_buffer (binfo);

  return Qnil;
}

/******************** Handle requests from the kernel *********************/

#ifdef EMACS_BTL
#include "cadillac-btl-extern.h"
#endif

/* turn logging on or off, etc. */
static void
HandleLoggingRequest (Editor *editor, CLoggingRequest *creq)
     /* I'm a lumberjack and I'm ok... */
{
  ReqLen name_len;
  char* data_filename = CGetVstring (editor->conn, &name_len);

#ifdef EMACS_BTL
  {
    extern Lisp_Object Vexecution_path;
    char *execname = 
      (STRINGP (Vexecution_path))?
        ((char *) XSTRING(Vexecution_path)->data):0;

    switch (creq->type)
      {
      case CLRInitBTL:
        cadillac_terminate_logging(); /* #### rename me */
        cadillac_initialize_backtrace_logging /* #### rename me */
          (data_filename, execname, (long) creq->limit, (long) creq->interval);
        break;

      case CLRInitPCL:
        cadillac_terminate_logging(); /* #### rename me */
        cadillac_initialize_pc_logging /* #### rename me */
          (data_filename, execname, (long) creq->limit, (long) creq->interval);
        break;

      case CLRStart:
        cadillac_start_logging(); /* #### rename me */
        break;

      case CLRStop:
        cadillac_stop_logging(); /* #### rename me */
        break;

      case CLRTerminate:
        cadillac_terminate_logging(); /* #### rename me */
        break;
        
      case CLRSetLogSignal:
        cadillac_set_log_signal(creq->signal); /* #### rename me */
        break;
        
      default:
        error ("Bad logging request type %d", creq->type);
      }
  }
#else
  Post ("Logging request, but no such code in image.");
#endif
}



/* creates a new buffer */
static void
HandleNewBufferRequest (Editor *editor, CNewBufferRequest *creq)
{
  ParseBuffer (editor->conn, &creq->buffer, editor, 0, 0, creq->transientId,
	       0);
  if (!NILP (Venergize_create_buffer_hook))
    {
      CBuffer *cbu = &creq->buffer;
      BufferInfo *binfo = get_buffer_info_for_id (cbu->bufferId, editor);
      Lisp_Object buffer;
      if (binfo)
        {
	  struct screen* prev_screen;
          buffer = binfo->emacs_buffer;
	  if (!NILP (binfo->screen))
	    {
	      prev_screen = selected_screen;
	      selected_screen = XSCREEN(binfo->screen);
	    }
	  safe_funcall_hook (Venergize_create_buffer_hook, 1, buffer, 0, 0);
	  if (!NILP (binfo->screen))
	    selected_screen = prev_screen;
        }
    }
}

/* Modifies the contents of a buffer */
static void
HandleModifyBufferRequest (Editor *editor, CModifyBufferRequest *creq)
{
  windows_or_buffers_changed++;
  ParseBuffer (editor->conn, &creq->newData, editor, creq->startPosition,
               creq->endPosition, 0, creq->head.data);
}

static void
MakeBufferAndExtentVisible (Lisp_Object list, Lisp_Object go_there)
{
  call2 (Qenergize_make_many_buffers_visible, list, go_there);
}

/* pops a buffer and scroll to a extent: calls to lisp */
static void
HandleEnsureVisibleRequest (Editor *editor, CEnsureVisibleRequest *creq)
{
  BufferInfo *binfo;
  Energize_Extent_Data *ext;
  Lisp_Object buffer_extent_list;
  struct gcpro gcpro1;

  buffer_extent_list = Qnil;
  GCPRO1 (buffer_extent_list);
  
  binfo = get_buffer_info_for_id (creq->bufferId, editor);
  if (!binfo)
    {
      Post ("EnsureVisibleRequest: unknown buffer");
      goto finished;
    }
  
  if (binfo->screen != Qnil)
    {
      /* ignore ensure visible for postit note buffers */
      goto finished;
    }

  if (creq->extentId)
    {
      ext = get_extent_data (creq->extentId, binfo);
      if (!ext)
	Post ("EnsureVisibleRequest: ignoring unknown extent");
    }
  else
    ext = 0;
  
  buffer_extent_list = Fcons (ext ? ext->extent : Qnil, Qnil);
  buffer_extent_list = Fcons (binfo->emacs_buffer, buffer_extent_list);

  MakeBufferAndExtentVisible (buffer_extent_list, creq->head.data ? Qt : Qnil);
  
 finished:
  CSkipRequest (editor->conn);
  UNGCPRO;
}

static void
HandleEnsureManyVisibleRequest (Editor *editor,
				CEnsureManyVisibleRequest *creq)
{
  BufferInfo *binfo;
  Energize_Extent_Data *ext;
  Lisp_Object buffer_extent_list;
  int n;
  EId buffer_id;
  EId extent_id;
  struct gcpro gcpro1;

  buffer_extent_list = Qnil;
  GCPRO1 (buffer_extent_list);
  
  for (n = creq->head.data,
       buffer_id = creq->bufferId,
       extent_id = creq->extentId;
       n;
       n--,
       buffer_id = n ? *(CGet (editor->conn, EId)) : 0,
       extent_id = n ? *(CGet (editor->conn, EId)) : 0)
    {
      binfo = get_buffer_info_for_id (buffer_id, editor);
      if (!binfo)
	{
	  Post ("EnsureManyVisibleRequest: ignoring unknown buffer");
	  continue;
	}
      
      if (binfo->screen != Qnil)
	{
	  /* silently ignore ensure visible for postit note buffers */
	  continue;
	}
      
      if (extent_id)
	{
	  ext = get_extent_data (extent_id, binfo);
	  if (!ext)
	    Post ("EnsureManyVisibleRequest: ignoring unknown extent");
	}
      else
	ext = 0;

      /* cons in reverse order and reverse the list before
	 calling MakeBufferAndExtentVisible */
      buffer_extent_list = Fcons (binfo->emacs_buffer, buffer_extent_list);
      buffer_extent_list = Fcons (ext ? ext->extent : Qnil,
				  buffer_extent_list);
    }
  buffer_extent_list = Fnreverse (buffer_extent_list);
  MakeBufferAndExtentVisible (buffer_extent_list, Qt);
  
  UNGCPRO;
}

/* Update the cached menus, ie update the menubar for now. */
static void
HandleProposeChoicesRequest (Editor *editor, CProposeChoicesRequest *req)
{
  BufferInfo* binfo;
  Lisp_Object buffer = Qnil;
  Lisp_Object extent = Qnil;
  Lisp_Object choices = Qnil;
  struct gcpro gcpro1, gcpro2, gcpro3;
  struct reply_wait* rw;

  GCPRO3 (buffer, extent, choices);
  
  /* get the buffer */
  binfo = get_buffer_info_for_id (req->objectId, editor);
  if (binfo)
    buffer = binfo->emacs_buffer;
  else
    buffer = Qnil;

  /* get the extent */
  if (binfo && req->genericId)
    {
      Energize_Extent_Data* ext = get_extent_data (req->genericId, binfo);
      if (ext)
	extent = ext->extent;
      else
	extent = Qnil;
    }
  else
    extent = Qnil;
  
  /* find if we were waiting for a reply */
  rw = find_wait_reply (req->head.serial);

  /* handle the request */
  if (rw && rw->objectId == req->objectId && rw->genericId == req->genericId)
    {
      /* It's a reply for a get_energize_menu call */
      rw->answered_p = True;
      rw->status = 1;
      rw->menu_result = list_choices (buffer, extent, rw->only_name, req);
    }
  else
    {
      /* It's a menu update, call the hook */
      choices = list_choices (buffer, extent, Qnil, req);
      safe_funcall_hook (Venergize_menu_update_hook, 1, choices, Qnil, Qnil);
    }
  UNGCPRO;
}

/* Kills a buffer */
static void
unmodify_buffer_and_kill_it (Lisp_Object buffer)
{
  int count = specpdl_ptr - specpdl;

  if ((!BUFFERP (buffer)) || NILP (XBUFFER (buffer)->name)) 
    return;
  
  /* unmodify the buffer */
  if (buffer != Fcurrent_buffer ())
    {
      record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
      Fset_buffer (buffer);
    }
  Fset_buffer_modified_p (Qnil);
  unbind_to (count, Qnil);
  
  /* kill it.  This will call the Energize hook to do the right thing */
  Fkill_buffer (buffer);
}

static void
HandleKillBufferRequest (Editor *editor, CKillBufferRequest *creq)
{
  BufferInfo *binfo;
  
  if (!(binfo = get_buffer_info_for_id (creq->bufferId, editor)))
    {
      Post ("KillBufferVisibleRequest: unregistered buffer");
      return;
    }

  unmodify_buffer_and_kill_it (binfo->emacs_buffer);
}

static void
HandleRemoveExtentsRequest (Editor *editor, CRemoveExtentsRequest *creq)
{
  BufferInfo *binfo;
  int i;
  EId *ids;
  Lisp_Object restore_buffer_state_cons;
  int count = specpdl_ptr - specpdl;
  
  if (!(binfo = get_buffer_info_for_id (creq->bufferId, editor)))
    {
      Post ("RemoveExtentsRequest: unregistered buffer");
      CSkipRequest (editor->conn);
      return;
    }

  /* enable buffer edits */
  restore_buffer_state_cons =
    Fcons (word_to_lisp (creq->bufferId),
	   Fcons (XBUFFER(binfo->emacs_buffer)->read_only, Qnil));

  record_unwind_protect (restore_buffer_state, restore_buffer_state_cons);

  XBUFFER(binfo->emacs_buffer)->read_only = Qnil;

  /* save old hook values */
  specbind (Qenergize_buffer_modified_hook, Qnil); 
  
  BUF_FACECHANGE (XBUFFER (binfo->emacs_buffer))++;
  windows_or_buffers_changed++;
  
  ids = CGetN (editor->conn, EId, creq->nExtent);
  for (i = 0; i < creq->nExtent; i++)
    {
      Energize_Extent_Data *ext = get_extent_data (ids [i], binfo);
      if (ext)
        free_Energize_Extent_Data (ext, binfo, OFT_STANDALONE);
    }
  
  /* restore modified hooks and globals */
  unbind_to (count, Qnil);
}

/* handles a request to save a buffer from the kernel */
static void
HandleSaveBufferRequest (Editor *editor, CSaveBufferRequest *creq)
{
  BufferInfo *binfo;
  int count = specpdl_ptr - specpdl;
 
  if (!(binfo = get_buffer_info_for_id (creq->bufferId, editor)))
    {
      Post("Server attempt to save a non registered buffer");
      return;
    }
  
  if (binfo->emacs_buffer != Fcurrent_buffer ())
    {
      record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
      Fset_buffer (binfo->emacs_buffer);
    }

  if (creq->head.data == CSExecuteSave)
    {
      Lisp_Object args[2];
      args [0] = intern ("save-buffer");
      args [1] = Qnil;
      Fapply (2, args);
    }
  else
    SaveBufferToEnergize (binfo);
  
  unbind_to (count, Qnil);
}

static void
HandleSetModifiedFlagRequest (Editor* editor, CSetModifiedFlagRequest* creq)
{
  BufferInfo *binfo;
  int count = specpdl_ptr - specpdl;

  if (!(binfo = get_buffer_info_for_id (creq->bufferId, editor)))
    {
      Post("Server attempt to set modified flag of a non registered buffer");
      return;
    }
  
  record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
  Fset_buffer (binfo->emacs_buffer);
  specbind (Qenergize_buffer_modified_hook, Qnil);
  Fset_buffer_modtime (binfo->emacs_buffer, Qnil);
  Fset_buffer_modified_p (creq->state ? Qt : Qnil);
  binfo->modified_state = creq->state;
  /* Mark the buffer so that we ask permission to Energize when the
   * user tries to modify it again */
  binfo->editable = 0;
  if (!creq->state)
    mark_all_extents_as_unmodified (binfo);
  unbind_to (count, Qnil);
}


/* handles requests regarding p_sheet associated to buffers */
static void
add_in_list_of_ids (int** ids, int* n_ids, int id){
  if (*n_ids == 0){
    *n_ids = 1;
    *ids = (int*)xmalloc (sizeof (int));
  }else{
    *n_ids += 1;
    *ids = (int*)xrealloc (*ids, sizeof (int) * (*n_ids));
  }
  (*ids) [(*n_ids) - 1] = id;
}

static void
remove_from_list_of_ids (int** ids, int* n_ids, int id){
  int i;
  if (*n_ids){
    /* look for id in *ids */
    for (i = 0; i < (*n_ids) && (*ids) [i] != id; i++);
    /* shift the remaining ones */
    for (; i < (*n_ids) - 1; i++)
      (*ids) [i] = (*ids) [i + 1];
    /* decrease the count */
    *n_ids -= 1;
    /* free array if empty */
    if (!*n_ids){
      xfree (*ids);
      *ids = 0;
    }
  }
}

extern void make_psheets_desired (struct screen *, Lisp_Object);

static void
HandleBufferSheetRequest (Editor *editor, CSheetRequest *sreq,
			  EId buffer_id)
{
  BufferInfo *binfo;
  char* name;
  Connection* conn = editor->conn;
  
  if (!(binfo = get_buffer_info_for_id (buffer_id, editor)))
    {
      Post("Server attempt to use p_sheet in a non registered buffer");
      CSkipRequest (conn);
      return;
    }
  
  name = CGetVstring (conn, (ReqLen *) 0);
  switch ((CSheetRSubtype) sreq->head.data)
    {
    case CSCreate:
      BLOCK_INPUT;
      lw_register_widget (name, name, sreq->sheetId, NULL, NULL,
			  HandleControlChange, NULL);
      UNBLOCK_INPUT;
      add_in_list_of_ids (&binfo->p_sheet_ids, &binfo->n_p_sheets,
			  sreq->sheetId);
      if (!strcmp (name, DEBUGGER_PSHEET_NAME)) 
	debuggerpanel_sheet = sreq->sheetId;
      break;
      
    case CSDelete:
      remove_from_list_of_ids (&binfo->p_sheet_ids, &binfo->n_p_sheets,
			       sreq->sheetId);
      BLOCK_INPUT;
      lw_destroy_all_widgets (sreq->sheetId);
      UNBLOCK_INPUT;
      if (sreq->sheetId == debuggerpanel_sheet)
	{
	  desired_debuggerpanel_exposed_p = 0;
	  debuggerpanel_sheet = 0;
	}
      break;
      
    case CSHide:
      {
	struct screen *screen;
	Lisp_Object rest;
	
	BLOCK_INPUT;
	if (sreq->sheetId == debuggerpanel_sheet)
	  desired_debuggerpanel_exposed_p = 0;
	else
	  for (rest = Vscreen_list; rest != Qnil; rest = Fcdr(rest))
	    {
	      screen = XSCREEN(Fcar(rest));
	      if (SCREEN_IS_X (screen))
		make_psheets_desired (screen, Qnil);
	    }
	UNBLOCK_INPUT;
      }
      break;
      
    case CSShow:
      if (sreq->sheetId == debuggerpanel_sheet)
	desired_debuggerpanel_exposed_p = 1;
      else
	{
	  struct screen *screen;
	  struct window *window;
	  Lisp_Object rest;
	  for (rest = Vscreen_list; rest != Qnil; rest = Fcdr(rest))
	    {
	      screen = XSCREEN(Fcar(rest));
	      if (SCREEN_IS_X (screen))
		{
		  window = XWINDOW(screen->selected_window);
		  if (window->buffer == binfo->emacs_buffer)
		    make_psheets_desired (screen, binfo->emacs_buffer);
		}
	    }
	}
      break;
    }
}


#if 0
static void
note_being_destroyed (Widget w, XtPointer call_data, XtPointer client_data)
{
#if 0
 *  NoteWidget nw = (NoteWidget)w;
 *  Widget tw = get_text_widget (nw);
 *  SCREEN_PTR s;
 *  Lisp_Object screen;
 *
 *  if (tw){
 *    s = (SCREEN_PTR)x_any_window_to_screen (XtWindow (tw));
 *    if (s){
 *      BLOCK_INPUT;
 *      set_text_widget (nw, 0);
 *      UNBLOCK_INPUT;
 *      XSET (screen, Lisp_Screen, s);
 *      Fdelete_screen (screen);
 *    }
 *  }
#endif
}
#endif

#if 0
 * static void
 * note_instantiated (EId bufferId, EId noteId, NoteWidget nw, void* arg)
 * {
 *   XtAddCallback ((Widget)nw, XtNdestroyCallback, note_being_destroyed, 0);
 *   XtVaSetValues ((Widget)nw, "forceXtLoop", 1, 0);
 * }
#endif

static void
HandlePostitRequest (Editor *editor, CGenericRequest *preq)
{
#if 0
 *   BufferInfo* binfo;
 *   Connection* conn = editor->conn;
 *   NoteWidget notew;
 *   
 *   switch (preq->head.reqType)
 *     {
 *     case OpenPostitRType:{
 *       if (!(binfo = get_buffer_info_for_id (preq->openpostit.bufferId,
 *  					    editor)))
 *  	{
 *  	  Post("Server attempt to use postit in a non registered buffer");
 *  	  CSkipRequest (conn);
 *  	  return;
 *  	}
 *       
 *       BLOCK_INPUT;
 *       xc_create_note_window 
 *         (&preq->openpostit, conn, note_instantiated, 0,
 *          FONT_WIDTH (SCREEN_NORMAL_FACE (selected_screen).font),
 *          TOTAL_HEIGHT (SCREEN_NORMAL_FACE (selected_screen).font));
 *       UNBLOCK_INPUT;
 *       add_in_list_of_ids (&binfo->note_ids, &binfo->n_notes,
 *  			  preq->openpostit.bufferId);
 *     }
 *       break;
 *       
 *     case KillPostitRType:{
 *       if (!(binfo = get_buffer_info_for_id (preq->killpostit.bufferId,
 *  					    editor)))
 *  	{
 *  	  Post("Server attempt to kill postit in a non registered buffer");
 *  	  CSkipRequest (conn);
 *  	  return;
 *  	}
 *       
 *       BLOCK_INPUT;
 *       xc_destroy_note (preq->killpostit.postitId);
 *       UNBLOCK_INPUT;
 *       remove_from_list_of_ids (&binfo->note_ids, &binfo->n_notes,
 *  			       preq->killpostit.postitId);
 *       break;
 *     }}
#endif
}

/* show busy */
/* int needs_to_recompute_menubar_when_kernel_not_busy; */

static void
show_all_menubars_busy (int busy)
{
  struct screen* s;
  struct x_display* x;
  Lisp_Object tail;

  BLOCK_INPUT;
  for (tail = Vscreen_list; CONSP (tail); tail = XCONS (tail)->cdr)
    {
      s = XSCREEN (XCONS (tail)->car);
      if (SCREEN_IS_X (s))
	{
	  x = s->display.x;
	  if (x->menubar_widget)
	    lw_show_busy (x->menubar_widget, busy);
	}
    }
  UNBLOCK_INPUT;
}

static void
HandleShowBusyRequest (Editor *editor, CGenericRequest *preq)
{
  /* call the show busy routine of the  library for the menubar of
   * all screens */
  ReqLen len;
  extern Lisp_Object Venergize_kernel_busy;

  char* why = CGetVstring (editor->conn, &len);

  show_all_menubars_busy (preq->head.data);
  Venergize_kernel_busy = preq->head.data ? Qt : Qnil;
  safe_funcall_hook (Venergize_kernel_busy_hook, 1, build_string (why), Qnil,
		     Qnil);

  /* update the menubars if needed and kernel is not busy */
  if (!preq->head.data /*&& needs_to_recompute_menubar_when_kernel_not_busy*/)
    {
      SCREEN_PTR s;
      Lisp_Object tail;
      
/*      needs_to_recompute_menubar_when_kernel_not_busy = 0;*/
      for (tail = Vscreen_list; CONSP (tail); tail = XCONS (tail)->cdr)
	{
	  if (!SCREENP (XCONS (tail)->car))
	    continue;
	  
	  s = XSCREEN (XCONS (tail)->car);
	  if (SCREEN_IS_X (s) &&
	      ! MINI_WINDOW_P (XWINDOW (s->selected_window)))
	    recompute_screen_menubar (s);
	}
    }
}

static void
HandleSheetRequest (Connection* conn, CSheetRequest* sreq, Widget parent)
{
  char* name = CGetVstring (conn, NULL);

  switch ((CSheetRSubtype)sreq->head.data)
    {
    case CSCreate:
      lw_create_widget (name, name, sreq->sheetId, 0, parent,
			!sreq->bufferId, 0, HandleControlChange, 0);
      break;
    case CSDelete:
      lw_destroy_all_widgets (sreq->sheetId);
      break;
      
    case CSShow:
      lw_pop_up_all_widgets (sreq->sheetId);
      break;

    case CSHide:
      lw_pop_down_all_widgets (sreq->sheetId);
      break;
    }
}

static void
HandleSetControlRequest (Connection* conn, CGenericRequest* creq)
{
  CSetControlRequest* sreq = &creq->setcontrol;
  widget_value val;
  widget_value* contents;

  unsigned long i;
  unsigned long n = sreq->nChoices;
  
  if (n > 0)
    {
      contents = (widget_value *) xmalloc (n * sizeof (widget_value));
      memset (contents, 0, (n * sizeof (widget_value)));
    }
  else 
    contents = NULL;
  memset (&val, 0, sizeof (val));
  val.name = CGetVstring (conn, NULL);
  val.enabled = !(sreq->flags & CKInactive);
  val.selected = !!(sreq->flags & CKSelected);
  val.change = VISIBLE_CHANGE;
  val.contents = contents;

  for (i = 0; i < n; i++)
    {
      widget_value* cur = &contents [i];
      CChoice* choice = CGet (conn, CChoice);
      cur->name = CGetVstring (conn, NULL);
      cur->value = cur->name;
      cur->key = NULL;
      cur->enabled = !(choice->flags & CKInactive);
      cur->selected = !!(choice->flags & CKSelected);
      cur->change = VISIBLE_CHANGE;
      cur->contents = NULL;
      cur->call_data = NULL;
      cur->next = i == n - 1 ? NULL : &contents [i + 1];
      cur->toolkit_data = NULL;
      if ((i == 0 && n == 1) || cur->selected)
	{
	  val.value = cur->name;
	  if (!*val.value)
	    val.value = NULL;
	}
    }
  lw_modify_all_widgets (sreq->sheetId, &val, True);
  
  if (contents)
    xfree (contents);
}
    
static void
SendSheetStateChange (Connection* conn, EId buffer_id, EId sheet_id,
		      int shown)
{
  CWriteSheetRequest (conn, shown ? CSShow : CSHide, sheet_id, buffer_id, "");
}

static void
HandleControlChange (Widget widget, EId sheet_id, void* arg)
{
  Connection* 	conn;
  widget_value* val;
  widget_value* cur;
  widget_value* this_val = NULL;
  widget_value* cancel = NULL;
  char* 	this_name;
  int delete_window_p = (((int) arg) == -1);
  

  if (!energize_connection)
    return;

  conn = energize_connection->conn;
  if (!conn)
    return;

  this_name = XtName (widget);
  val = lw_get_all_values (sheet_id);

  if (delete_window_p)
    /* Complete and utter kludge.  If this dbox was dismissed with the
       WM close box (WM_DELETE_WINDOW, meaning the widget was destroyed)
       then we look for a likely "cancel" button and pretend the user
       clicked on that.  Really the protocol should be extended for this.
     */
    for (cur = val; cur; cur = cur->next)
      {
	char *v = cur->value;
	if (v &&
	    ((strlen (v) >= 6 && !strncmp (v, "cancel", 6)) ||
	     (strlen (v) >= 5 && !strncmp (v, "abort", 5))))
	  cancel = cur;
      }

  /* first send all the edited widgets */
  for (cur = val; cur; cur = cur->next)
    {
      /* do not send the widget that ran the callback */
      if (!strcmp (cur->name, this_name))
	this_val = cur;
      else if (cur == cancel)
	;
      /* send the edited widgets */
      else if (cur->edited)
	{
	  char* value = cur->value;
	  unsigned int flags = 0;

	  if (!cur->enabled)
	    flags |= CKInactive;
	  if (cur->selected)
	    flags |= CKSelected;

	  /* the kernel is brain dead and expect "1" and "0" as values
	     for the checkbox objects.  So if value is NULL, make it be "0"
	     or "1" depending on the selected state.  This is until we fix
	     the kernel. */
	  if (!value)
	    value = cur->selected ? "1" : "0";

	  CWriteSetControlRequest (conn, sheet_id, 0, cur->name, 1);
	  CWriteChoice (conn, 0, flags, value, 0);
	  CWriteLength (conn);
	}
    }

  if (delete_window_p && !this_val)
    {
      this_val = cancel;
/*      if (! this_val) abort (); */
    }

  /* Then send the widget that ran the callback */
  if (this_val)
    {
      CWriteSetControlRequest (conn, sheet_id, 0, this_val->name, 1);
      CWriteChoice (conn, 0, 0, this_val->value, 0);
      CWriteLength (conn);
      CWriteRequestBuffer (conn);
    }
}

/******************** Low level connection stuff ************************/
static void
add_in_connection_input_buffer (Connection *conn, char *s, int l)
{
  /* Should be in connection.c */
  if (conn->inread >= conn->infill) 
    conn->inread = conn->infill = conn->inbuffer;
  
  CNeedInputSize (conn, l);
  memcpy (conn->infill, s, l);
  conn->infill += l;
}

static Lisp_Object
ProcessJustOneEnergizeRequest ()
{
  Editor *editor = energize_connection;
  CEditorRequest *req;
  int res = 0;
  
  if (!editor) return make_number(res);
  
  if (!editor->conn)
    {
      CloseConnection();
      return make_number (res);
    }
  
  req = CReadEditorRequest(editor->conn);
  if (!req)
    {
      switch (errno)
        {
        case EWOULDBLOCK:
          /* Post("ProcessEnergizeRequest: internal error EWOULDBLOCK"); */
	  res = -1;
          break;
          
        case 0:
	case ECONNRESET:
          Post ("Connection to Energize was closed.");
          CloseConnection ();
          break;
          
        default:
          Post
            ("System error on connection to Energize, closing.");
          CloseConnection ();
          break;
        }
    }
  else
    {
      res = 1;
      switch (req->head.reqType)
        {
        case RefuseConnectionRType:
          Post("Energize connection refused");
          CloseConnection ();
          break;
          
        case AcceptConnectionRType:
	  {
	    CProtocol* proto = CGet (editor->conn, CProtocol);
	    editor->major = proto->major;
	    editor->minor = proto->minor;
	    Post("Energize connection accepted");
	    CSkipRequest(editor->conn);
	  }
          break;
          
        case NewBufferRType:
          HandleNewBufferRequest(editor, &req->newbuffer);
          break;
          
	case QueryBufferRType:
	  {
	    EId buffer_id;
	    struct reply_wait* rw = find_wait_reply (req->head.serial);
	    CGetVstring (editor->conn, 0); /* skip directory */
	    CGetVstring (editor->conn, 0); /* skip file */
	    buffer_id = *CGet (editor->conn, EId);
	    if (rw)
	      {
		rw->answered_p = 1;
		rw->status = req->head.data;
		rw->objectId = buffer_id;
	      }
	  }
	  break;

        case EnsureVisibleRType:
          HandleEnsureVisibleRequest(editor, &req->ensurevisible);
          break;
          
        case EnsureManyVisibleRType:
          HandleEnsureManyVisibleRequest(editor, &req->ensuremanyvisible);
          break;
          
        case ModifyBufferRType:
          HandleModifyBufferRequest(editor, &req->modifybuffer);
          break;
          
        case ProposeChoicesRType:
	  HandleProposeChoicesRequest (editor, &req->generic.proposechoices);
          break;
          
        case ChoiceExecutedRType:
	  {
	    struct reply_wait* rw = find_wait_reply (req->head.serial);
	    CChoiceExecutedRequest* ce = &req->generic.choiceexecuted;
	    if (rw)
	      {
		rw->answered_p = 1;
		rw->status = ce->head.data;
		BLOCK_INPUT;
		rw->message = CMakeVstring (editor->conn, 0);
		UNBLOCK_INPUT;
	      }
	  }
          break;
          
        case KillBufferRType:
          HandleKillBufferRequest(editor, &req->killbuffer);
          break;
          
	case ModifiedBufferRType:
	  {
	    struct reply_wait* rw = find_wait_reply (req->head.serial);
	    if (rw)
	      {
		rw->answered_p = 1;
		if (rw->objectId == req->modifiedbuffer.bufferId)
		  rw->status = req->modifiedbuffer.state;
		else
		  rw->status = CMBufferLocked;
	      }
	  }
	  break;

        case SetModifiedFlagRType:
          HandleSetModifiedFlagRequest(editor, &req->setmodifiedflag);
          break;
          
        case RemoveExtentsRType:
          HandleRemoveExtentsRequest (editor, &req->removeextents);
          break;
          
        case RenumberExtentsRType:
          /* HandleDuplicateExtentRequest(editor, req); */
          break;
          
#if 0
        case DialogRType:
          /* HandleDialogRequest(editor, req, CurrentBuffer(editor)); */
          break;
#endif
          
        case SaveBufferRType:
          HandleSaveBufferRequest (editor, &req->savebuffer);
          break;
          
        case SheetRType:{
	  EId buffer_id = req->generic.sheet.bufferId;
	  if (!buffer_id)
	    buffer_id = buffer_id_of_sheet (req->generic.sheet.sheetId);
	  if (buffer_id)
	    HandleBufferSheetRequest (editor, &req->generic.sheet, buffer_id);
	  else
	    {
	      CSheetRSubtype type = (CSheetRSubtype)req->head.data;
	      if (type == CSDelete || type ==CSHide)
		select_screen (selected_screen);
	      BLOCK_INPUT;
	      HandleSheetRequest (editor->conn, &req->generic.sheet,
				  selected_screen->display.x->widget);
	      UNBLOCK_INPUT;
	    }
	}
          break;
	  
        case SetControlRType:
	  BLOCK_INPUT;
          HandleSetControlRequest (editor->conn, (CGenericRequest*) req);
	  UNBLOCK_INPUT;
          break;

 	case OpenPostitRType:
  	case KillPostitRType:
 	  HandlePostitRequest (editor, (CGenericRequest*)req);
 	  break;

	case ShowBusyRType:
	  HandleShowBusyRequest (editor, (CGenericRequest*)req);
	  break;

	case LoggingRType:
	  HandleLoggingRequest (editor, (CLoggingRequest*)req);
	  break;

        default:
          Post1("ProcessEnergizeRequest: can't handle request of type %d", 
                req->head.reqType);
        }

    }

  return make_number (res);
}

static int inside_ProcessEnergizeRequest1;

/* this must be called ONLY by unwind_protect in ProcessEnergizeRequest1 */
static Lisp_Object
post_handle_request (Lisp_Object ignored)
{
  if (inside_ProcessEnergizeRequest1 <= 0)
    abort ();
  inside_ProcessEnergizeRequest1--;
  if (energize_connection && energize_connection->conn)
    CSkipRequest (energize_connection->conn);
  return Qnil;
}

static Lisp_Object
pop_conn (Lisp_Object arg)
{
  Connection *old_conn = (Connection *) lisp_to_word (arg);
  if (! old_conn)
    abort ();
  if (! energize_connection)
    return Qnil;
  if (energize_connection->conn == old_conn)
    abort ();

  if (CRequestDelayedP (energize_connection->conn))
    /* We don't call the CWait* functions any more so this shouldn't happen.
       But if it does someday, then we need to either copy the remaining
       bits from new_conn to old_conn, or loop processing requests until
       new_conn is drained.
     */
    abort ();

  DeleteConnection (energize_connection->conn);
  energize_connection->conn = old_conn;

  return Qnil;
}

static Lisp_Object
ProcessEnergizeRequest1 ()
{ 
  Lisp_Object result;
  int count = specpdl_depth;

  if (inside_ProcessEnergizeRequest1)
    {
      /* When the energize process filter is called recursively, push a new
	 connection object.  The read-pointer of the connection buffer could
	 be in the middle of a request.  However, we know that the fd itself
	 is always pointing between requests.  So making a new connection is
	 a way of skipping past the one request we were in the process of
	 reading when we allowed process output to be handled recursively.
       */
      Connection *old_conn = energize_connection->conn;
      Connection *new_conn =
	EditorSideConnection ((void *) energize_connection,
			      old_conn->fdin, old_conn->fdout);
      energize_connection->conn = new_conn;
      record_unwind_protect (pop_conn, word_to_lisp ((unsigned int) old_conn));
    }

  /* this must come after pop_conn() to get the right connection object */
  record_unwind_protect (post_handle_request, Qnil);

  inside_ProcessEnergizeRequest1++;

  result = ProcessJustOneEnergizeRequest ();
  notify_delayed_requests ();

  /* decrements inside_ProcessEnergizeRequest1 and possibly replaces
     energize_connection->conn with old_conn.
   */
  unbind_to (count, Qnil);

  return result;
}


/******** Initialize Energize-related state and set up connection ********/

static void
setup_connection (Editor *ed, unsigned int id1, unsigned int id2) 
{
  CEditorRequest *req = CWriteEditorRequest(ed->conn, QueryConnectionRType);
  
  /* these 2 slots are ignored */
  req->generic.queryconnection.major = 0;
  req->generic.queryconnection.minor = 0;

  req->generic.queryconnection.cadillacId1 = id1; /* #### */
  req->generic.queryconnection.cadillacId2 = id2;
  req->generic.queryconnection.nProtocols = 1;
  /* first numerical arg is major protocol number, second is minor */
  CWriteProtocol(ed->conn, 0, 8, "editor");
  CWriteLength (ed->conn);
  CWriteRequestBuffer (ed->conn);
}

/* this is used as the readMethod of the energize connection, so that 
   the connection library won't do some buffering that messes us up.
   It does this buffering only if conn->readMethod == read, so using
   another function turns it off.
 */
static int
my_read (int fd, char *buf, int nb)
{
  return read (fd, buf, nb);
}

static Connection *
EditorSideConnection (Editor *editor, int fdin, int fdout)
{
  Connection *conn = NewConnection ((void *)editor, fdin, fdout);
  if (conn)
    conn->readMethod = my_read;
  return conn;
}

DEFUN ("handle-energize-request", Fhandle_energize_request,
       Shandle_energize_request,
       2, 2, 0,
       "Filter called when a request is available from Energize.")
     (proc, string)
     Lisp_Object proc, string;
{
  if (!NILP (string))
    CHECK_STRING (string, 0);
  
  if (!energize_connection || !energize_connection->conn)
    {
      /* no need for a message here, Energize is dead */
      return make_number (0);
    }
  if (!energize_connection || (energize_connection->proc != proc))
    {
      Post("Got an Energize request but not from current connection ");
      return make_number (0);
    }
  
  if (!NILP (string))
    add_in_connection_input_buffer (energize_connection->conn,
				    (char *) XSTRING (string)->data,
				    XSTRING (string)->size);
  
  return ProcessEnergizeRequest1 ();
}


/* Opens a network connection to Energize.
 * server is a string.  It can end up with :<uid> or :<username>
 * in which case the uid is added to the TCP port to get the connection */
static void
ConnectToEnergize (char *server_str, char *arg)
{
  struct Lisp_Process *proc;
  Lisp_Object lp;
  Lisp_Object fil;
  char *host;
  unsigned int port;
  long flags;
  int id1;
  int id2;

  if (CGetPortNumber (server_str, &host, &port))
    {
      
      lp = Fopen_network_stream (build_string ("energize"),
                                 Qnil,
                                 build_string (host),
                                 make_number (port));
      if (lp)
        {
	  /* Don't ask the user for confirmation when exiting Emacs */
	  Fprocess_kill_without_query (lp, Qnil);
          proc = XPROCESS (lp);
          energize_connection = xnew (Editor);
          energize_connection->conn = EditorSideConnection (energize_connection, XFASTINT (proc->infd),
                                                XFASTINT (proc->infd));
          energize_connection->proc = lp;
          energize_connection->binfo_hash = make_hashtable (10);
	  energize_connection->image_table = 0;
          energize_connection->gc_save = Qnil;
          energize_connection->major = 0;
          energize_connection->minor = 0;
          peo = allocate_edit_options (10);
          request_serial_number = 0;
	  global_reply_wait = 0;

          if ((flags = fcntl (energize_connection->conn->fdin, F_GETFL, 0)) == -1)
	    abort(); /* perror ("get connection flags"); */
	  
#ifdef O_NONBLOCK
	  if (fcntl (energize_connection->conn->fdin, F_SETFL, flags & ~O_NONBLOCK) == -1)
#else
	  if (fcntl (energize_connection->conn->fdin, F_SETFL, flags & ~O_NDELAY) == -1)
#endif
            abort(); /* perror ("set connection flags"); */
          
          XSET (fil, Lisp_Subr, &Shandle_energize_request);
          proc->filter = fil;
          proc->filter_does_read = Qt;

	  Venergize_kernel_busy = Qnil;
/*	  needs_to_recompute_menubar_when_kernel_not_busy = 0;*/
          
          id1 = 0;
          id2 = 0;
          if (arg)
            sscanf (arg, "%x,%x", &id1, &id2);
          
	  energize_buffers_list = Qnil;

          setup_connection (energize_connection, id1, id2);
        }
      else
	error ("couldn't connect to Energize server");
    }
  else
    error ("couldn't determine Energize server port number");
}


/* Close the connection to energize.
 * Kills all the energize related buffer */
static void 
CloseConnection () 
{
  Editor *ed = energize_connection;

  if (ed)
    /* make this function as paranoid as we can */
    {
      BLOCK_INPUT;
      /* cleanup the busy state */
      show_all_menubars_busy (False);
      Venergize_kernel_busy = Qnil;
      /* destroy all pop_up boxes */
      lw_destroy_all_pop_ups ();
      UNBLOCK_INPUT;

      debuggerpanel_sheet = 0;
      desired_debuggerpanel_exposed_p = 0;

      if (ed->conn)
	DeleteConnection (ed->conn);
      ed->conn = 0;
      
      if (ed->binfo_hash)
        {
          int count = specpdl_ptr - specpdl;
	  
          /* we are flushing everything, so we just ignore any change
             hooks and don't make an effort to delete extents since they
             are all going away */
          specbind (Qenergize_buffer_modified_hook, Qnil);
          specbind (intern ("inhibit-quit"), Qt);
	  call0 (intern ("de-energize-all-buffers"));
          unbind_to (count, Qnil);
	  
          free_hashtable (ed->binfo_hash);
          ed->binfo_hash = 0;
        }
      
      free_edit_options (peo);
      
      if (ed->proc) Fdelete_process (ed->proc);
      ed->proc = Qnil;
      
      energize_buffers_list = Qnil;

      /* now kill buffers created to satisfy requests on old connection */
      xfree (ed);
    }
  
  /* marked as close */
  energize_connection = 0;
}


DEFUN ("connect-to-energize-internal", 
       Fconnect_to_energize_internal, Sconnect_to_energize_internal, 0, 2, 0,
       "Usage: (connect-to-energize-internal <server-name> <energizearg>)\n\
Energizearg representing two 32 bit Energize ids that will be passed\n\
to the Energize server when opening the Energize connection.\n\
Only one connection can be open at a time.")

  (server_name, energize_arg)
  Lisp_Object server_name, energize_arg;
{
  unsigned char *server; 
  unsigned char *arg;
  
  if (!NILP(energize_arg))
    {
      CHECK_STRING (energize_arg, 1);
      arg = XSTRING (energize_arg)->data;
    }
  else
    arg = 0;
  
  if (!NILP(server_name))
    {
      CHECK_STRING (server_name, 1);
      server = XSTRING (server_name)->data;
    }
  else
    server = 0;
  
  /* since we are going ahead with this, make sure that we are
     really and truly disconnected first */
  Fclose_connection_to_energize ();

  ConnectToEnergize ((char *)server, (char *)arg);
  return Qnil;
}

DEFUN ("close-connection-to-energize", Fclose_connection_to_energize, 
       Sclose_connection_to_energize, 0, 0, 0,
       "Close the open Energize connection, if any.")
     ()
{
  if (!energize_connection) return Qnil;

  CloseConnection();
  return Qnil;
}


/* buffer modified routines */
static void
SendBufferModificationState (Editor *editor, BufferInfo *binfo, int flag)
{
  Connection *conn = editor->conn;
  EId bufferId = binfo->id;
  
  if (conn)
    {
      int state = (flag)?(CMBufferSetModified):(CMBufferSetUnmodified);
      CWriteModifiedBufferHeader (conn, bufferId, state);
      CWriteRequestBuffer (conn);
    }
}

/* returns 1 if buffer is locked (non-editable),
**  0 if it isn't (editable), and -1 if it can't tell */
static int
CheckBufferLock (Editor *editor, BufferInfo *binfo)
{
  Connection *conn = editor->conn;
  struct reply_wait rw;
  
  /* If permision already granted by kernel dont' ask again */
  if (binfo->editable)
    return 0;

  /* If can't ask say we do not know */
  if (!conn)
    return -1;

  /* Ask the kernel */
  CWriteModifiedBufferHeader (conn, binfo->id, CMBufferQueryLock);
  conn->header->serial = ++request_serial_number;
  CWriteRequestBuffer (conn);
  
  rw.serial = request_serial_number;
  rw.objectId = binfo->id;
  rw.status = -1;
  
  if (!wait_for_reply (&rw))
    return -1;
  
  if (rw.status == CMBufferLocked)
    {
      /* Buffer is locked by kernel so we cannot edit it */
      return 1;
    }
  else if (rw.status == CMBufferUnlocked)
    {
      /* Buffer is unlocked by kernel: edit permission granted! */
      binfo->editable = 1;
      return 0;
    }
  else
    {
      /* This should never happen */
      return -1;
    }	 
}


/* Ask the kernel if BUFFER is currently locked -- waits for answer */
static Lisp_Object 
buffer_locked_p (Lisp_Object buffer)
{
  BufferInfo *binfo;

  if (!energize_connection)
    return Qnil;
  
  if (NILP (buffer))
    XSET (buffer, Lisp_Buffer, current_buffer);

  CHECK_BUFFER (buffer, 0);
  
  binfo = get_buffer_info_for_emacs_buffer (buffer, energize_connection);

  if (!binfo)
    {
      /* Not an Energize buffer, return Qnil: can edit buffer */
      return Qnil;
    }
  else 
    {
      /* Energize buffer, check if editable */
      return CheckBufferLock (energize_connection, binfo) == 0 ? Qnil : Qt;
    }
}



/* Called by map_extents function called by Fsend_buffer_modified_request
 */
static int
notify_extent_modified (Lisp_Object extent_obj, void *arg)
{
  /* arg is a binfo_and_state */
  binfo_and_state *bans = (binfo_and_state*)arg;
  Energize_Extent_Data *ext;
  BufferInfo *binfo = bans->binfo;
  Connection *conn = binfo->editor->conn;
  EId *extent_id;
  
  if (!EXTENTP (extent_obj) ||  
      !(EXTENT_FLAGS (XEXTENT(extent_obj)) & EF_WARN_MODIFY))
    return 0;

  if ((ext = extent_to_data (extent_obj))
      && ext->extentType == CEGeneric
      && ext->u.generic.gData
      && ext->u.generic.gData->cl
      && (ext->u.generic.gData->cl->flags & CCWarnModified)
      && ext->u.generic.gData->modified_state != bans->state)
    {
      if (bans->tell_energize)
	{
	  CWriteModifiedExtentsHeader (conn, binfo->id, bans->state, 1);
	  extent_id = CPut (conn, EId);
	  *extent_id = ext->id;
	  CWriteLength (conn);
	  CWriteRequestBuffer (conn);
	}
      ext->u.generic.gData->modified_state = bans->state;
    }
  return 0;
}

static int
ceiwme_lower_mf (EXTENT extent, void *arg)
{
  if (EXTENT_FLAGS (extent) & EF_WARN_MODIFY)
    *((EXTENT *) arg) = extent;
  return 0;
}

static int
ceiwme_upper_mf (EXTENT extent, void *arg)
{
  if (EXTENT_FLAGS (extent) & EF_WARN_MODIFY)
    {
      *((EXTENT *) arg) = extent;
      return 1;
    }
  else
    return 0;
}

static void
coerce_endpoints_to_be_inside_warn_on_modify_extents (from_ptr, to_ptr, b)
     int *from_ptr;
     int *to_ptr;
     struct buffer *b;
{
  EXTENT lower_extent = 0;
  EXTENT upper_extent = 0;
  int lower_bound = *from_ptr;
  int upper_bound = *to_ptr;
  Lisp_Object tmp;
  
  /* make sure that from <= to */
  { 
    int tmp_int = max (lower_bound, upper_bound);
    *from_ptr = lower_bound = min (lower_bound, upper_bound);
    *to_ptr = upper_bound = tmp_int;
  }
  
  if (!BUFFER_NOTIFY_BACKGROUND_BIT_SET_P(b)) 
    return;
  
  map_extents (BUF_BEG(b), lower_bound, 0, ceiwme_lower_mf, 
               (void *) &lower_extent, b, 1);
  if (!lower_extent)
    {
      lower_bound = BUF_BEG (b);
      map_extents (upper_bound, BUF_Z(b), 0, ceiwme_upper_mf, 
                   (void *) &upper_extent, b, 1);
      if (!upper_extent)
        upper_bound = BUF_Z (b);
      else
        {
          int xstart;
          XSET (tmp, Lisp_Extent, upper_extent);
          xstart = XINT (Fextent_start_position (tmp));
          upper_bound = max (upper_bound, xstart);
        }
    }
  /* I forget why, but if we found an lower bound, we don't need to find
     an upper bound */
  else
    {
      int xstart;
      XSET (tmp, Lisp_Extent, lower_extent);
      xstart = XINT (Fextent_start_position (tmp));
      lower_bound = min (lower_bound, xstart);
    }

  *from_ptr = lower_bound;
  *to_ptr = upper_bound;
  return;
}

static void
mark_all_extents_as_unmodified (BufferInfo *binfo){
  binfo_and_state bans;
  bans.binfo = binfo;
  bans.state = FALSE;
  bans.tell_energize = FALSE;
  
  map_extents (BUF_BEG (XBUFFER (binfo->emacs_buffer)),
	       BUF_Z (XBUFFER (binfo->emacs_buffer)),
	       notify_extent_modified, 0, &bans, 
               XBUFFER(binfo->emacs_buffer), 1);
}

/* Send the BufferModified events for the current buffer.
 * Handles both global buffer modified and extents modified. */
DEFUN ("send-buffer-modified-request", Fsend_buffer_modified_request,
       Ssend_buffer_modified_request,
       3, 3, 0,
       "Send a BufferModified request for the current buffer.")
     (state, from, to)
     Lisp_Object state, from, to; /* dont use ANSI arglists in DEFUNs */
{
  int modifiedp = NILP (state)? 0 : 1;
  Lisp_Object buffer;
  BufferInfo *binfo;
  int from_int = XINT (from);
  int to_int = XINT (to);
  
  if (!energize_connection || !energize_connection->conn) return Qnil;
  
  XSET (buffer, Lisp_Buffer, current_buffer);

  Fenergize_barf_if_buffer_locked ();

  if (binfo = get_buffer_info_for_emacs_buffer (buffer, energize_connection))
    {
      /* now make sure that from and to
	 are inside some warn_on_modify extents somewhere */
      coerce_endpoints_to_be_inside_warn_on_modify_extents
        (&from_int, &to_int, current_buffer);
      XSET (from, Lisp_Int, from_int);
      XSET (to, Lisp_Int, to_int);
  
      if (binfo->modified_state != modifiedp)
        {
          SendBufferModificationState (energize_connection, binfo, modifiedp);
          binfo->modified_state = modifiedp;
        }
      if (modifiedp)
	{
	  binfo_and_state bans;
	  bans.binfo = binfo;
	  bans.state = TRUE;
	  bans.tell_energize = TRUE;
	  map_extents (from, to, notify_extent_modified, 0, &bans,
		       XBUFFER(binfo->emacs_buffer), 1);
	}
      else
	mark_all_extents_as_unmodified (binfo);
    }
  return Qnil;
}

DEFUN ("energize-barf-if-buffer-locked", Fenergize_barf_if_buffer_locked,
       Senergize_barf_if_buffer_locked, 0, 0, 0,
       "Error if the buffer is locked.")
     ()
{
  Lisp_Object buffer;
  XSET (buffer, Lisp_Buffer, current_buffer);
  
  if (!energize_connection || !energize_connection->conn)
    return Qnil;

  while (!NILP (buffer_locked_p (buffer)))
    {
      notify_delayed_requests ();
      Fsignal (Qbuffer_locked_by_kernel, (Fcons (buffer, Qnil)));
    }  
  return Qnil;
}


DEFUN ("energize-send-region", Fenergize_send_region,
       Senergize_send_region,
       2, 2, 0,
       "Send region as user input")
     (start, end)
     Lisp_Object start, end;
{
  BufferInfo *binfo;
  Lisp_Object b;
  int start1;
  CEditorRequest *req;
  
  if (!energize_connection || !energize_connection->conn) 
    error ("Not connected to Energize");
  
  XSET (b, Lisp_Buffer, current_buffer);
  if (binfo = get_buffer_info_for_emacs_buffer (b, energize_connection))
    {
      validate_region (&start, &end);
      
      if (XINT (start) < GPT && XINT (end) > GPT) move_gap (start);
      
      start1 = XINT (start);
      req = CWriteEditorRequest (energize_connection->conn, UserTypedSomethingRType);
      req->usertypedsomething.bufferId = binfo->id;
      CWriteVstringLen 
        (energize_connection->conn, (char *) CHAR_ADDRESS (start1), XINT (end) - start1);
      CWriteLength (energize_connection->conn);
      CWriteRequestBuffer (energize_connection->conn);
      return Qnil;
    }
  return Qnil;
}

DEFUN ("connected-to-energize-p", Fconnected_to_energize_p,
       Sconnected_to_energize_p,
       0, 0, 0, 
"Returns nil if no connection to Energize.")
     ()
{
  if (!energize_connection || !energize_connection->conn || !energize_connection->binfo_hash || 
      !energize_connection->proc || (XTYPE (energize_connection->proc != Lisp_Process)))
    return Qnil;
  else 
    return Qt;
}

DEFUN ("energize-user-input-buffer-mark", Fenergize_user_input_buffer_mark,
       Senergize_user_input_buffer_mark, 0, 0, 0,
       "Returns the mark associated to the current (user input) buffer")
     ()
{
  BufferInfo *binfo;
  Lisp_Object b;
  
  if (!energize_connection) return Qnil;
  
  XSET (b, Lisp_Buffer, current_buffer);
  if ((binfo = get_buffer_info_for_emacs_buffer (b, energize_connection)))
    return binfo->output_mark;
  else
    return Qnil;
}

DEFUN ("energize-query-buffer", Fenergize_query_buffer,
       Senergize_query_buffer, 1, 2, 0,
       "(energize-query-buffer filename just-ask)\n\
Ask Energize to create a buffer containing the file filename.\n\
Returns the buffer or NIL if Energize cannot create the buffer.\n\
If second argument just-ask is T, just ask if Energize\n\
already knows about the file and returns T if yes, NIL otherwise.")
     (filename, just_ask)
     Lisp_Object filename, just_ask;
{
  struct Lisp_String *filename_str;
  CEditorRequest *creq;
  char *dir_sep;
  struct reply_wait rw;
  
  if (!energize_connection || !energize_connection->conn)
    return Qnil;

  filename = Fexpand_file_name (filename, Qnil);
  filename_str = XSTRING (filename);

  dir_sep = (char *) strrchr ((char *)filename_str->data, '/');
  
  creq = CWriteEditorRequest (energize_connection->conn, QueryBufferRType);
  creq->head.data = !NILP (just_ask);
  creq->head.serial = ++request_serial_number;
  CWriteVstringLen (energize_connection->conn, (char *) filename_str->data,
		    (dir_sep)? (dir_sep - (char *) filename_str->data): 0);
  CWriteVstringLen (energize_connection->conn, 
		    (char *) filename_str->data, filename_str->size);
  CWriteLength (energize_connection->conn);
  CWriteRequestBuffer (energize_connection->conn);
  
  rw.serial = request_serial_number;
  rw.objectId = 0;
  
  if (!wait_for_reply (&rw))
    return Qnil;

  if (rw.status)
    {
      if (rw.objectId)
	{
	  BufferInfo* binfo = get_buffer_info_for_id (rw.objectId, energize_connection);
	  return binfo ? binfo->emacs_buffer : Qt;
	}
      else
	return Qt;
    }
  else
    return Qnil;
}


DEFUN ("energize-psheets-visible-p", Fenergize_psheets_visible_p,
       Senergize_psheets_visible_p, 0, 1, 0,
       "Whether the (optional) screen currently has open psheets.")
     (screen)
     Lisp_Object screen;
{
  if (NILP (screen))
    XSET (screen, Lisp_Screen, selected_screen);
  CHECK_SCREEN (screen, 0);
  if (XSCREEN (screen)->display.x->current_psheets)
    return Qt;
  return Qnil;
}

DEFUN ("energize-buffer-has-psheets-p", Fenergize_buffer_has_psheets_p,
       Senergize_buffer_has_psheets_p, 0, 1, 0,
       "Whether the buffer has psheets associated with it.")
     (buf)
     Lisp_Object buf;
{
  int count;
  if (NILP (buf))
    buf = Fcurrent_buffer ();
  CHECK_BUFFER (buf, 0);
  if (get_psheets_for_buffer (buf, &count))
    return Qt;
  return Qnil;
}

DEFUN ("energize-protocol-level", Fenergize_protocol_level,
       Senergize_protocol_level, 0, 0, 0,
       "Returns the Energize protocol level.")
     ()
{
  return
    energize_connection
      ? Fcons (energize_connection->major, energize_connection->minor)
	: Qnil;
}


static int
get_energize_connection_and_buffer_id (Lisp_Object buffer, void **conn_ptr,
				       long *buffer_id_ptr)
{
  BufferInfo *binfo;

  if (!energize_connection || !energize_connection->conn) return 0;
  
  binfo = get_buffer_info_for_emacs_buffer (buffer, energize_connection);
  
  *conn_ptr = (void *) energize_connection->conn;
  *buffer_id_ptr = (long) binfo ? binfo->id : 0;
  return 1;
}

static int
get_energize_connection_and_current_buffer_id (void **conn_ptr,
					       long *buffer_id_ptr)
{
  Lisp_Object lisp_buffer;
  XSET (lisp_buffer, Lisp_Buffer, current_buffer);

  return get_energize_connection_and_buffer_id (lisp_buffer, conn_ptr,
						buffer_id_ptr);
}

int *
get_psheets_for_buffer (Lisp_Object buffer, int *count_ptr)
{
  BufferInfo *binfo;

  if (!energize_connection || !energize_connection->conn) return 0;
  
  binfo = get_buffer_info_for_emacs_buffer (buffer, energize_connection);
  if (!binfo) return 0;

  if (count_ptr) *count_ptr = binfo->n_p_sheets;
  return binfo->p_sheet_ids;
}

void
notify_that_sheet_has_been_hidden (EId id)
{
  EId buffer_id = buffer_id_of_sheet (id);
  if (!buffer_id)
    return;

  if (buffer_id && energize_connection && energize_connection->conn)
    {
      SendSheetStateChange (energize_connection->conn, buffer_id, id, 0);
      CWriteRequestBuffer (energize_connection->conn);
    }
}


/* edit-mode dialog box 
 */

extern int dbox_up_p;
extern unsigned int popup_id_tick;
extern Lisp_Object Fpopup_dialog_box (Lisp_Object);

static struct editmode {
  int ok, external, view, edit, window, split;
  char *other;
} editmode;

static void
edit_mode_callback (Widget widget, LWLIB_ID id, XtPointer client_data)
{
  widget_value *data;
  char *name = (char *) client_data;

  if ((int) client_data == -1) name = "cancel";	/* WM_DELETE_WINDOW */

  if (!strcmp (XtName (widget), "otherText")) /* screw it */
    ;
  else if (!strcmp (name, "externalBox"))
    {
      /* make the text slot be active only if "other" is selected */
      BLOCK_INPUT;
      data = malloc_widget_value ();
      data->name = "externalOther";
      if (! lw_get_some_values (id, data)) abort ();
      data->enabled = data->selected;
      data->name = "otherText";
      lw_modify_all_widgets (id, data, True);
      free_widget_value (data);
      UNBLOCK_INPUT;
    }
  else if (!strcmp (name, "cancel"))
    {
      editmode.ok = -1;
      lw_destroy_all_widgets (id);
    }
  else if (!strcmp (name, "help"))
    {
      Lisp_Object v = Fmake_vector (3, Qt);
      XVECTOR (v)->contents [0] = build_string ("ok");
      XVECTOR (v)->contents [1] = list1 (intern ("ignore"));
      Fpopup_dialog_box (list2 (build_string ("dbx_editmode_help"), v));
    }
  else if (!strcmp (name, "ok"))
    {
      BLOCK_INPUT;
      editmode.ok = 1;
      data = malloc_widget_value ();
      data->name = "externalEmacs";
      if (! lw_get_some_values (id, data)) abort ();
      if (data->selected) editmode.external = 0;
      data->name = "externalViXterm";
      if (! lw_get_some_values (id, data)) abort ();
      if (data->selected) editmode.external = 1;
      data->name = "externalViCmdtool";
      if (! lw_get_some_values (id, data)) abort ();
      if (data->selected) editmode.external = 2;
      data->name = "externalOther";
      if (! lw_get_some_values (id, data)) abort ();
      if (data->selected) editmode.external = 3;
      data->name = "otherText";
      if (! lw_get_some_values (id, data)) abort ();
      editmode.other = data->value;

      data->name = "emacsView";
      if (! lw_get_some_values (id, data)) abort ();
      if (data->selected) editmode.view = 0;
      data->name = "viView";
      if (! lw_get_some_values (id, data)) abort ();
      if (data->selected) editmode.view = 1;
      data->name = "lessView";
      if (! lw_get_some_values (id, data)) abort ();
      if (data->selected) editmode.view = 2;

      data->name = "editEmacs";
      if (! lw_get_some_values (id, data)) abort ();
      if (data->selected) editmode.edit = 0;
      data->name = "editVi";
      if (! lw_get_some_values (id, data)) abort ();
      if (data->selected) editmode.edit = 1;

      data->name = "windowOne";
      if (! lw_get_some_values (id, data)) abort ();
      if (data->selected) editmode.window = 0;
      data->name = "windowSeveral";
      if (! lw_get_some_values (id, data)) abort ();
      if (data->selected) editmode.window = 1;
      data->name = "windowMany";
      if (! lw_get_some_values (id, data)) abort ();
      if (data->selected) editmode.window = 2;

      data->name = "splitScreens";
      if (! lw_get_some_values (id, data)) abort ();
      editmode.split = !!data->selected;

      free_widget_value (data);
      lw_destroy_all_widgets (id);
      UNBLOCK_INPUT;
    }
  else
    {
      abort ();
    }
}

static int
editmode_done (void *arg)
{
  return (editmode.ok != 0);
}

DEFUN ("energize-edit-mode-prompt", Fenergize_edit_mode_prompt,
       Senergize_edit_mode_prompt, 6, 6, 0, "")
     (external, edit_mode, view_mode, other_text, window, split)
     Lisp_Object external, edit_mode, view_mode, other_text, window, split;
{
  int dbox_id;
  struct screen *s = selected_screen;
  widget_value *data;
  Widget parent, dbox;

  if (!SCREEN_IS_X (s)) error ("not an X screen");
  parent = s->display.x->widget;

  CHECK_FIXNUM (external, 0);
  CHECK_FIXNUM (edit_mode, 0);
  CHECK_FIXNUM (view_mode, 0);
  CHECK_FIXNUM (window, 0);
  CHECK_FIXNUM (split, 0);
  CHECK_STRING (other_text, 0);

  editmode.ok = 0;
  editmode.external = XINT (external);
  editmode.view = XINT (view_mode);
  editmode.edit = XINT (edit_mode);
  editmode.window = XINT (window);
  editmode.split = XINT (split);
  editmode.other = 0;

  BLOCK_INPUT;
  data = malloc_widget_value ();
  data->name = "editmode";
  data->value = "editmode";
  data->enabled = 1;

  dbox_id = ++popup_id_tick;
  dbox = lw_create_widget ("editmode", "editmode", dbox_id, data, parent,
			   1, 0, edit_mode_callback, 0);
  data->value = 0;

  data->name = "button1"; data->call_data = data->value = "ok";
  lw_modify_all_widgets (dbox_id, data, True);
  data->name = "button2"; data->call_data = data->value = "cancel";
  lw_modify_all_widgets (dbox_id, data, True);
  data->name = "button3"; data->call_data = data->value = "help";
  lw_modify_all_widgets (dbox_id, data, True);
  data->name = data->call_data = "externalBox";
  lw_modify_all_widgets (dbox_id, data, True);
  data->name = "otherText"; data->call_data = "otherText";
  lw_modify_all_widgets (dbox_id, data, True);
  data->name = "message"; data->value = "editmode";
  lw_modify_all_widgets (dbox_id, data, True);

  data->selected = 1;
  switch (editmode.external)
    {
    case 0: data->name = "externalEmacs"; break;
    case 1: data->name = "externalViXterm"; break;
    case 2: data->name = "externalViCmdtool"; break;
    case 3: data->name = "externalOther"; break;
    default: abort ();
    }
  lw_modify_all_widgets (dbox_id, data, True);
  switch (editmode.view)
    {
    case 0: data->name = "emacsView"; break;
    case 1: data->name = "viView"; break;
    case 2: data->name = "lessView"; break;
    default: abort ();
    }
  lw_modify_all_widgets (dbox_id, data, True);
  switch (editmode.edit)
    {
    case 0: data->name = "editEmacs"; break;
    case 1: data->name = "editVi"; break;
    default: abort ();
    }
  lw_modify_all_widgets (dbox_id, data, True);
  switch (editmode.window)
    {
    case 0: data->name = "windowOne"; break;
    case 1: data->name = "windowSeveral"; break;
    case 2: data->name = "windowMany"; break;
    default: abort ();
    }
  lw_modify_all_widgets (dbox_id, data, True);
  
  data->name = "otherText";
  data->selected = 0;
  data->value = (char *) XSTRING (other_text)->data;
  data->enabled = (editmode.external == 3);
  lw_modify_all_widgets (dbox_id, data, True);

  data->name = "splitScreens";
  data->enabled = 1;
  data->selected = editmode.split;
  data->value = 0;
  lw_modify_all_widgets (dbox_id, data, True);

  free_widget_value (data);

  dbox_up_p++;
  lw_pop_up_all_widgets (dbox_id);
  UNBLOCK_INPUT;

  wait_delaying_user_input (editmode_done, 0);

  if (editmode.ok == -1)
    return Fcons (external,
		  list5 (edit_mode, view_mode, other_text, window, split));
  else if (editmode.ok == 1)
    return Fcons (make_number (editmode.external),
		  list5 (make_number (editmode.view),
			 make_number (editmode.edit),
			 build_string (editmode.other ? editmode.other : ""),
			 make_number (editmode.window),
			 make_number (editmode.split)));
  else
    abort ();
}

extern Time mouse_timestamp;
extern Time global_mouse_timestamp;

static LWLIB_ID search_id;
static int last_search_up_p;

static void
hide_search_dialog (Widget w, LWLIB_ID id)
{
#if 0
  /* I'd like to do this, but the widget occasionally gets FUCKED */
  XUnmapWindow (XtDisplay (w), XtWindow (w));
#else
  lw_destroy_all_widgets (id);
#endif
}


static void
search_callback (Widget widget, LWLIB_ID id, XtPointer client_data)
{
  Widget parent = widget;
  Lisp_Object event;
  widget_value *data;
  char *name = (char *) client_data;
  Lisp_Object search, replace;
  Lisp_Object case_sensitive_p, regexp_p, direction;

  if ((int) client_data == -1) name = "done";	/* WM_DELETE_WINDOW */

  while (parent && XtClass (parent) != xmDialogShellWidgetClass)
    parent = XtParent (parent);
  if (! parent) abort ();

  if (!strcmp (name, "done"))
    {
      hide_search_dialog (parent, id);
      return;
    }
  else if (!strcmp (name, "gotoStart"))
    {
      event = Fallocate_event ();
      XEVENT (event)->event_type = menu_event;
      XEVENT (event)->event.eval.function = Qcall_interactively;
      XEVENT (event)->event.eval.object = intern ("beginning-of-buffer");
      mouse_timestamp = global_mouse_timestamp;
      enqueue_command_event (event);
      return;
    }
  else if (!strcmp (name, "gotoEnd"))
    {
      event = Fallocate_event ();
      XEVENT (event)->event_type = menu_event;
      XEVENT (event)->event.eval.function = Qcall_interactively;
      XEVENT (event)->event.eval.object = intern ("end-of-buffer");
      mouse_timestamp = global_mouse_timestamp;
      enqueue_command_event (event);
      return;
    }
  else if (!strcmp (name, "scrollForward"))
    {
      event = Fallocate_event ();
      XEVENT (event)->event_type = menu_event;
      XEVENT (event)->event.eval.function = Qcall_interactively;
      XEVENT (event)->event.eval.object = intern ("scroll-up");
      mouse_timestamp = global_mouse_timestamp;
      enqueue_command_event (event);
      return;
    }
  else if (!strcmp (name, "scrollBack"))
    {
      event = Fallocate_event ();
      XEVENT (event)->event_type = menu_event;
      XEVENT (event)->event.eval.function = Qcall_interactively;
      XEVENT (event)->event.eval.object = intern ("scroll-down");
      mouse_timestamp = global_mouse_timestamp;
      enqueue_command_event (event);
      return;
    }
#if 0
  else if (!strcmp (name, "help"))
    {
      Lisp_Object v = Fmake_vector (3, Qt);
      XVECTOR (v)->contents [0] = build_string ("ok");
      XVECTOR (v)->contents [1] = list1 (intern ("ignore"));
      Fpopup_dialog_box (list2 (build_string ("dbx_search_help"), v));
      return;
    }
#endif

  BLOCK_INPUT;
  data = malloc_widget_value ();
  data->name = "searchText";
  if (! lw_get_some_values (id, data)) abort ();
  search = build_string (data->value);
  data->name = "replaceText";
  if (! lw_get_some_values (id, data)) abort ();
  replace = build_string (data->value);
  data->name = "regexpSearch";
  if (! lw_get_some_values (id, data)) abort ();
  regexp_p = (data->selected ? Qt : Qnil);
  data->name = "caseSearch";
  if (! lw_get_some_values (id, data)) abort ();
  case_sensitive_p = (data->selected ? Qt : Qnil);

  data->name = "directionForward";
  if (! lw_get_some_values (id, data)) abort ();
  direction = data->selected ? Qt : Qnil;

  if (!strcmp (name, "search"))
    replace = Qnil;
  else if (!strcmp (name, "replace"))
    ;
  else if (!strcmp (name, "replace_all"))
    {
      replace = list1 (replace);
/*      hide_search_dialog (parent, id); */
    }
  else
    abort ();

  free_widget_value (data);
  UNBLOCK_INPUT;

  event = Fallocate_event ();
  XEVENT (event)->event_type = menu_event;
  XEVENT (event)->event.eval.function = intern ("energize-search-internal");
  XEVENT (event)->event.eval.object =
    (NILP (replace)
     ? list4 (case_sensitive_p, regexp_p, direction, search)
     : list5 (case_sensitive_p, regexp_p, direction, search, replace));

  mouse_timestamp = global_mouse_timestamp;
  enqueue_command_event (event);
}


DEFUN ("energize-search", Fenergize_search, Senergize_search, 0, 0, "",
       "Pop up the search-and-replace dialog box.")
     ()
{
  int dbox_id;
  struct screen *s = selected_screen;
  widget_value *data;
  Widget parent, dbox;

  if (!SCREEN_IS_X (s)) error ("not an X screen");
  parent = s->display.x->widget;

  BLOCK_INPUT;
  data = malloc_widget_value ();

  dbox_id = (search_id ? search_id : (++popup_id_tick));
  dbox = lw_create_widget ("search", "search", dbox_id, NULL, parent,
			   1, 0, search_callback, 0);
  data->enabled = 1;
  data->value = 0;

  data->name = "button1"; data->value = data->call_data = "search";
  lw_modify_all_widgets (dbox_id, data, True);
  data->name = "button2"; data->value = data->call_data = "replace";
  lw_modify_all_widgets (dbox_id, data, True);
  data->name = "button3"; data->value = data->call_data = "replace_all";
  lw_modify_all_widgets (dbox_id, data, True);
  data->name = "button4"; data->value = data->call_data = "done";
  lw_modify_all_widgets (dbox_id, data, True);

  data->value = 0;
  data->name = data->call_data = "gotoStart";
  lw_modify_all_widgets (dbox_id, data, True);
  data->name = data->call_data = "gotoEnd";
  lw_modify_all_widgets (dbox_id, data, True);
  data->name = data->call_data = "scrollBack";
  lw_modify_all_widgets (dbox_id, data, True);
  data->name = data->call_data = "scrollForward";
  lw_modify_all_widgets (dbox_id, data, True);

  data->value = 0;
  data->name = data->call_data = "caseSearch";
  data->selected = NILP (current_buffer->case_fold_search);
  lw_modify_all_widgets (dbox_id, data, True);

  data->name = data->call_data = "directionForward";
  data->selected = 1;
  lw_modify_all_widgets (dbox_id, data, True);
  data->name = data->call_data = "directionBackward";
  data->selected = 0;
  lw_modify_all_widgets (dbox_id, data, True);
  
  free_widget_value (data);

  lw_pop_up_all_widgets (dbox_id);
  last_search_up_p = 0;
  if (search_id)
    {
      Widget w = lw_get_widget (dbox_id, parent, True);
      if (! w) abort ();
      XMapRaised (XtDisplay (w), XtWindow (w));
    }
  else
    {
      search_id = dbox_id;
      dbox_up_p++;
    }
  UNBLOCK_INPUT;

  return Qnil;
}



/*************** Definition of Emacs Lisp-callable functions ***************/

void
syms_of_editorside() 
{
  energize_connection = 0;
  inside_ProcessEnergizeRequest1 = 0;

  image_cache = make_strings_hashtable (50);

  staticpro (&energize_buffers_list);
  energize_buffers_list = Qnil;

  defsubr(&Ssend_buffer_modified_request);
  defsubr(&Senergize_list_menu);
  defsubr(&Senergize_execute_menu_item);
  defsubr(&Senergize_execute_command_internal);
  defsubr(&Sconnect_to_energize_internal);
  defsubr(&Sconnected_to_energize_p);
  defsubr(&Sclose_connection_to_energize);
  defsubr(&Shandle_energize_request);
  defsubr(&Senergize_buffer_p);
  defsubr(&Senergize_buffer_type);
  defsubr(&Sset_energize_buffer_type_internal);
  defsubr(&Senergize_buffer_id);
  defsubr(&Senergize_request_kill_buffer);
  defsubr(&Senergize_send_region);
  defsubr(&Senergize_user_input_buffer_mark);
  defsubr(&Senergize_update_menubar);
  defsubr(&Senergize_extent_menu_p);
  defsubr(&Senergize_query_buffer);
  defsubr(&Senergize_barf_if_buffer_locked);
  defsubr(&Senergize_psheets_visible_p);
  defsubr(&Senergize_buffer_has_psheets_p);
  defsubr(&Senergize_protocol_level);
  defsubr(&Senergize_edit_mode_prompt);
  defsubr(&Senergize_search);

  search_id = 0;

  DEFVAR_BOOL ("   inside-parse-buffer", &inside_parse_buffer,
               "internal variable used to control extent deletion.");
  inside_parse_buffer = 0;
  Qinside_parse_buffer = intern ("   inside-parse-buffer");

  DEFVAR_LISP ("energize-create-buffer-hook", &Venergize_create_buffer_hook,
               "Hook called when buffer is created by energize; takes \n\
BUFFER as its only argument.");
  Venergize_create_buffer_hook = Qnil;
  
  DEFVAR_LISP ("energize-buffer-modified-hook", 
               &Venergize_buffer_modified_hook,
               "Hook to call when Energize buffer is modified.");
  Venergize_buffer_modified_hook = Qnil;
  Qenergize_buffer_modified_hook = intern ("energize-buffer-modified-hook");
  
  DEFVAR_LISP ("energize-kernel-modification-hook", 
               &Venergize_kernel_modification_hook,
               "Hook called when a buffer is being modified by energize;\n\
takes no arguments.");
  Venergize_kernel_modification_hook = Qnil;
  
  DEFVAR_BOOL ("ignore-kernel", 
               &ignore_kernel,
               "Set when the kernel should be ignored -- for debugging.");
  ignore_kernel = 0;
  
  DEFVAR_LISP ("energize-kernel-busy", &Venergize_kernel_busy,
               "True if the Energize kernel is busy.");
  Venergize_kernel_busy = Qnil;
  Qenergize_kernel_busy = intern ("energize-kernel-busy");

  DEFVAR_LISP ("energize-kernel-busy-hook", &Venergize_kernel_busy_hook,
               "Hook called when the Energize kernel becomes busy or non busy.");
  Venergize_kernel_busy_hook = Qnil;

  DEFVAR_LISP ("energize-menu-update-hook", &Venergize_menu_update_hook,
               "Hook called when the Energize kernel updates the menubar.");
  Venergize_menu_update_hook = Qnil;

  DEFVAR_LISP ("energize-attributes-mapping", &Venergize_attributes_mapping,
	       "A-list to map kernel attributes indexes to Emacs attributes");
  Venergize_attributes_mapping = Qnil;

/*  DEFVAR_INT ("energize-font-lock-p", &energize_font_lock_p,
               "Set to true enable to use Energize with font-lock");
  energize_font_lock_p = 1;
 */

  Qbefore_change_function = intern ("before-change-function");

  Qafter_change_function = intern ("after-change-function");

  Qfirst_change_function = intern ("first-change-function");

  Qbuffer_locked_by_kernel = intern ("buffer-locked-by-kernel");

  Qdefault_directory = intern ("default-directory");

  Qbuffer_file_name = intern ("buffer-file-name");

  Qenergize_user_input_buffer_mark = 
    intern ("energize-user-input-buffer-mark");

  Qenergize_user_input_mode = intern ("energize-user-input-mode");

  Qenergize_make_many_buffers_visible
    = intern ("energize-make-many-buffers-visible");

  Qenergize_extent_data = intern ("energize-extent-data");

  Qbuffer_undo_list = intern ("buffer-undo-list");

  Fput (Qbuffer_locked_by_kernel, Qerror_conditions,
	Fcons (Qbuffer_locked_by_kernel, Fcons (Qerror, Qnil)));
  Fput (Qbuffer_locked_by_kernel, Qerror_message,
	build_string ("Buffer is currently locked by kernel"));
}

#endif /* ENERGIZE */
