/* NeXTstep communication module
   Copyright (C) 1989, 1993, 1994 Free Software Foundation, Inc.

This file is part of GNU Emacs.

GNU Emacs is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Emacs; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

#import <appkit/appkit.h>
#import <objc/Object.h>
#import <remote/NXProxy.h>
#import <machkit/NXPort.h>
#import <drivers/event_status_driver.h>
#import <mach/mach.h>
#import <mach/message.h>
#import <servers/netname.h>
#import <math.h>

#include "config.h"
#include "lisp.h"
#include "blockinput.h"
#include "dispextern.h"
#include "nsterm.h"
#include "systime.h"

#include "termhooks.h"
#include "termopts.h"
#include "termchar.h"
#include "gnu.h"
#include "frame.h"
#include "disptab.h"
#include "buffer.h"
#include "window.h"
#include "multi-frame.h"

#include "nswraps.h"

#define KEY_NS_POWER_OFF      ((1<<28)|(0<<16)|1)
#define KEY_NS_OPEN_FILE      ((1<<28)|(0<<16)|2)
#define KEY_NS_OPEN_TEMP_FILE ((1<<28)|(0<<16)|3)
#define KEY_NS_DRAG_FILE      ((1<<28)|(0<<16)|4)
#define KEY_NS_DRAG_COLOR     ((1<<28)|(0<<16)|5)
#define KEY_NS_DRAG_ASCII     ((1<<28)|(0<<16)|6)
#define KEY_NS_CHANGE_FONT    ((1<<28)|(0<<16)|7)
#define KEY_NS_OPEN_FILE_LINE ((1<<28)|(0<<16)|8)
#define KEY_NS_CHANGE_GDB     ((1<<28)|(0<<16)|9)

Lisp_Object ns_input_file,ns_input_font,ns_input_fontsize,ns_input_line;
Lisp_Object ns_input_color,ns_input_ascii;

int ns_gdb_frame;
Lisp_Object ns_gdb_status;

id ns_current_display=nil;
id ns_gdb_controller=nil,ns_gdb_interrupt_controller=nil;

/* Under NS, nonzero means that ns_read_file_name is executed whenever
   read_file_name is called */
int ns_use_open_panel;

/* Under NS, nonzero means that ns_yes_or_no_p is executed whenever
   yes_or_no_p or y_or_n_p are called */
int ns_use_yes_no_panel;

Lisp_Object ns_alternate_is_meta,ns_iso_latin,ns_cursor_blink_rate,ns_shrink_space;
NXAtom *ns_send_types=0,*ns_return_types=0,*ns_drag_types=0;

struct frame *ns_highlight_frame = 0;
struct frame *ns_focus_frame = 0;
struct frame *ns_focus_event_frame = 0;

static BOOL send_appdefined = NO;
static NXEvent last_appdefined_event;
static DPSTimedEntry timed_entry = 0;
static DPSTimedEntry cursor_blink_entry = 0;
static fd_set *select_readfds = 0;
static fd_set *select_writefds = 0;
static fd_set *select_exceptfds = 0;
static int select_nfds;
static int lockfocused=0;
static int ns_window_num=0;

extern void ns_free_frame_menubar ();

static void ns_frame_rehighlight(void);
static void ns_adjust_size(struct frame *f);
static void redraw_previous_char ();
static void redraw_following_char ();
static int fast_find_position ();
static void note_mouse_highlight ();
static void clear_mouse_face ();
static void show_mouse_face ();

static NXRect last_mouse_glyph;
static unsigned long last_mouse_movement_time;
static struct frame *last_mouse_frame = 0;
static int mouse_face_beg_row, mouse_face_beg_col;
static int mouse_face_end_row, mouse_face_end_col;
static int mouse_face_past_end;
static Lisp_Object mouse_face_window;
static int mouse_face_face_id;
static FRAME_PTR mouse_face_mouse_frame;
static int mouse_face_mouse_x, mouse_face_mouse_y;
static int mouse_face_defer;
static int mouse_face_deferred_gc;
static int disable_mouse_highlight;

/* Convert modifiers in a NeXTSTEP event to emacs style modifiers.  */
#define EV_MODIFIERS(e) \
((((e)->flags & NX_HELPMASK) ? hyper_modifier : 0) \
 |((EQ(ns_alternate_is_meta,Qt) && ((e)->flags & NX_ALTERNATEMASK)) ? meta_modifier:0) \
 |((EQ(ns_alternate_is_meta,Qleft) && ((e)->flags & NX_ALTERNATEMASK) && ((e)->flags & NX_NEXTLALTKEYMASK)) ? meta_modifier:0) \
 |((EQ(ns_alternate_is_meta,Qright) && ((e)->flags & NX_ALTERNATEMASK) && ((e)->flags & NX_NEXTRALTKEYMASK)) ? meta_modifier:0) \
 |(((e)->flags & NX_SHIFTMASK) ? shift_modifier:0) \
 |(((e)->flags & NX_CONTROLMASK) ? ctrl_modifier:0) \
 |(((e)->flags & NX_COMMANDMASK) ? super_modifier:0))

#define EV_UDMODIFIERS(e) \
((((e)->type == NX_LMOUSEDOWN) ? down_modifier : 0) \
 |(((e)->type == NX_RMOUSEDOWN) ? down_modifier : 0) \
 |(((e)->type == NX_LMOUSEDRAGGED) ? down_modifier : 0) \
 |(((e)->type == NX_RMOUSEDRAGGED) ? down_modifier : 0) \
 |(((e)->type == NX_LMOUSEUP)   ? up_modifier   : 0) \
 |(((e)->type == NX_RMOUSEUP)   ? up_modifier   : 0))

#define EV_BUTTON(e) \
((((e)->type == NX_LMOUSEDOWN) || ((e)->type == NX_LMOUSEUP)) ? 0 : \
 (((e)->type == NX_RMOUSEDOWN) || ((e)->type == NX_RMOUSEUP)) ? 1 : 2)

/* Convert the time field in a NeXTSTEP event to a timestap in milliseconds.
   XXX this is not portable to non NeXT NeXTSTEP systems yet.
   1000/68=250/17.  Avoid over/underflow */
#define EV_TIMESTAMP(e) (((e)->time/17)*250)

/* This is a piece of code which is common to all the event handeling
   methods.  Maybe it should even be a function.  */
#define EV_TRAILER(e)    \
  { \
  XSET (events->frame_or_window, Lisp_Frame, emacsframe); \
  events->timestamp = EV_TIMESTAMP (e); \
  events++; \
  eventsleft--; \
  if (send_appdefined) ns_send_appdefined (-1); \
  }


/* Screen max coordinate
 Using larger coordinates causes movewindow/placewindow to abort */
#define SCREENMAX 10000

/* Little utility macro */
#define BOUND(min,x,max) (((x)<(min)) ? (min) : (((x)>(max)) ? (max) : (x)))

static int curs_x,curs_y,flexlines,highlight;

static struct input_event *events=0;
static int eventsleft=0;

extern struct frame *updating_frame;

static int keytypeno=0;
static unsigned char *keytypes=0;
static unsigned char **keycode=0;
unsigned char *ns2isomap=0;
unsigned char *iso2nsmap=0;

static void keymap_init(void)
   {
   unsigned char *mapping;

   DPSSetDeadKeysEnabled (DPSGetCurrentContext (), NILP(ns_alternate_is_meta));

      {
      NXEventHandle handle=NXOpenEventStatus();
      NXKeyMapping map;

      if (!handle)
         error ("NXOpenEventStatus: %s", strerror (errno));

      map.size = NXKeyMappingLength(handle);
      map.mapping = (char *) xmalloc(map.size);
      if (!NXGetKeyMapping(handle,&map))
         error ("NXGetKeyMapping: %s", strerror (errno));

      NXCloseEventStatus(handle);
      if ((map.mapping[0]!=0) || (map.mapping[1]!=0))
         error ("Unknown keyboard map format.");

      mapping=(unsigned char *)(map.mapping+2);
      }

      {
      int i;
      keytypeno=*mapping++;
      keytypes=mapping;
      for(i=0;i<keytypeno;i++)
         mapping+=2+mapping[1];
      }

      {
      int kc;
      int kcs = *mapping++;

      keycode=(unsigned char **) xmalloc(sizeof(*keycode)*kcs);

      for(kc=0;kc<kcs;kc++)
         {
         char mask;
         keycode[kc]=mapping;

         if ((mask=*mapping++)!=-1)
            {
            int i=2;
            while(mask)
               {
               if (mask&0x1) i<<=1;
               mask>>=1;
               }
            mapping+=i;
            }
         }
      }

      {
      float tmp[256];
      int n;

      ns2isomap=(void *) xmalloc(sizeof(*ns2isomap)*256);
      nswrap_transtable("NextStepEncoding","ISOLatin1Encoding",tmp);
      for (n=0;n<256;n++) ns2isomap[n]=tmp[n] ? tmp[n] : n;
      /* Unfortunately this hack is necessary due to a bug
         in either NextStepEncoding or ISOLatin1Encoding */
      ns2isomap[(unsigned char) '-']=(unsigned char)'-';

      iso2nsmap=(void *) xmalloc(sizeof(*iso2nsmap)*256);
      nswrap_transtable("ISOLatin1Encoding","NextStepEncoding",tmp);
      for (n=0;n<256;n++) iso2nsmap[n]=tmp[n] ? tmp[n] : n;
      /* Unfortunately this hack is necessary due to a bug
         in either NextStepEncoding or ISOLatin1Encoding */
      iso2nsmap[(unsigned char) '-']=(unsigned char)'-';
      }
   }

static unsigned short char_from_key(unsigned short kc,int mod)
   {
   unsigned char *map=keycode[kc];
   int pos=0;

   if (map[0]&0x40) pos=(pos<<1) + ((mod&NX_HELPMASK)!=0);
   if (map[0]&0x20) pos=(pos<<1) + ((mod&NX_NUMERICPADMASK)!=0);
   if (map[0]&0x10) pos=(pos<<1) + ((mod&NX_COMMANDMASK)!=0);
   if (map[0]&0x08) pos=(pos<<1) + ((mod&NX_ALTERNATEMASK)!=0);
   if (map[0]&0x04) pos=(pos<<1) + ((mod&NX_CONTROLMASK)!=0);
   if (map[0]&0x02) pos=(pos<<1) + ((mod&NX_SHIFTMASK)!=0);
   if (map[0]&0x01) pos=(pos<<1) + ((mod&NX_ALPHASHIFTMASK)!=0);

   return /* (map[1+pos*2]<<8) + */ map[2+pos*2];
   }

static BOOL char_is_type(unsigned short kc,int mod)
   {
   unsigned char *m,*n;
   int t,i;
   for(mod >>= 16,t=-1;mod;t++,mod>>=1);
   if (t==-1) return NO;
   for(i=0,m=keytypes;i<keytypeno;i++,m+=2+m[1])
      if (m[0]==t) break;
   if (i>=keytypeno) return NO;
   for(i=0,n=m+2;n<m+2+m[1];n++) if (*n==kc) return YES;
   return NO;
   }

void ns_chars_to_rect(struct frame *f,int x1,int y1,int x2,int y2, NXRect *r)
   {
   r->origin.x=x1*f->display.ns->face->width;
   r->origin.y=y1*f->display.ns->line_height;
   r->size.width=(x2-x1+1)*f->display.ns->face->width;
   r->size.height=(y2-y1+1)*f->display.ns->line_height;
   }

/* Given a pixel position P on the frame F, return glyph co-ordinates in
   (*X, *Y). */
void ns_pixel_to_glyph_coords (struct frame *f, int px, int py,
                               int *x, int *y, void *bounds, int noclip)
   {
   *x=(f->display.ns->face->width <=0)
            ? 0 : floor(((float)px) / f->display.ns->face->width);
   *y=(f->display.ns->line_height<=0)
            ? 0 : floor(((float)py) / f->display.ns->line_height);

   if (bounds)
      abort();

   if (!noclip)
      {
      *x = BOUND(0,*x,f->width);
      *y = BOUND(0,*y,f->height);
      }
   }

void ns_glyph_to_pixel_coords(struct frame *f,int x,int y,int *pix_x,int *pix_y)
   {
   *pix_x=(int)rint(x*f->display.ns->face->width);
   *pix_y=(int)rint(y*f->display.ns->line_height);
   }

static void ns_rect_to_glyph_coords(struct frame *f, NXRect *r,int maximize,
                                     int *x, int *y, int *cols, int *rows)
   {
   if (f->display.ns->face->width<=0)
      {
      *x=0;
      *cols=f->width;
      }
   else if (maximize)
      {
      *x=floor(r->origin.x/f->display.ns->face->width+0.001);
      *cols=ceil((r->origin.x+r->size.width)/f->display.ns->face->width-0.001);
      }
   else
      {
      *x=ceil(r->origin.x/f->display.ns->face->width-0.001);
      *cols=floor((r->origin.x+r->size.width)/f->display.ns->face->width+0.001);
      }

   if (f->display.ns->line_height<=0)
      {
      *y=0;
      *rows=f->height;
      }
   else if (maximize)
      {
      *y=floor(r->origin.y/f->display.ns->line_height+0.001);
      *rows=ceil((r->origin.y+r->size.height)/f->display.ns->line_height-0.001);
      }
   else
      {
      *y=ceil(r->origin.y/f->display.ns->line_height-0.001);
      *rows=floor((r->origin.y+r->size.height)/f->display.ns->line_height+0.001);
      }

   *cols=*cols-*x;
   *rows=*rows-*y;
   }

void ns_focus_on_frame (struct frame *f)
   {
   id view=f->display.ns->view;
   check_ns();
/*   [[view window] makeKeyAndOrderFront:NXApp]; */
   }

void ns_unfocus_frame (struct frame *f)
   {
   id view=f->display.ns->view;
   check_ns();
/*   [[view window] orderBack:NXApp]; */ /* XXX unfocus ! */
   }

void ns_raise_frame (struct frame *f)
   {
   id view=f->display.ns->view;
   check_ns();
   [[view window] orderFront:NXApp];
   }

void ns_lower_frame (struct frame *f)
   {
   id view=f->display.ns->view;
   check_ns();
   [[view window] orderBack:NXApp];
   }

void ns_make_frame_visible (struct frame *f)
   {
   id view=f->display.ns->view;
   check_ns();
   [[view window] orderFront:NXApp];
   }

void ns_make_frame_invisible (struct frame *f)
   {
   id view=f->display.ns->view;
   check_ns();
   [[view window] orderOut:NXApp];
   }

void ns_iconify_frame (struct frame *f)
   {
   id view=f->display.ns->view;
   check_ns();
   [[view window] miniaturize:NXApp];
   }

void ns_destroy_window (struct frame *f)
   {
   id view=f->display.ns->view;
   check_ns();
   if (f==ns_focus_frame)
      ns_focus_frame=0;
   if (f==ns_highlight_frame)
      ns_highlight_frame=0;
   if (f == mouse_face_mouse_frame)
      {
      mouse_face_beg_row = mouse_face_beg_col = -1;
      mouse_face_end_row = mouse_face_end_col = -1;
      mouse_face_window = Qnil;
      }
   xfree (f->display.ns->face);
   xfree (f->display.ns);
   [[view window] close];
   ns_window_num--;

#if 0
      {
      Lisp_Object rest;
      struct frame *f;
      
      for (rest=Vframe_list; CONSP(rest); rest=XCONS(rest)->cdr)
         if (FRAME_NS_P(f=XFRAME(XCONS(rest)->car)))
            [[f->display.ns->view window] setCloseButton:NXApp to:(ns_window_num>1)];
      }
#endif
   }

int ns_get_color (const char *name, NXColor *col)
   {
   NXColor new;
   id colorlistlist=[NXColorList availableColorLists];
   int n=[colorlistlist count],i,finished=0;
   unsigned int t1,t2;
   const char *c;

   if (name[0]=='R' && name[1]=='G' && name[2]=='B')
      {
      for(t1=t2=0,c=name+3;1;c++)
         {
         if (*c>='0' && *c<='9')      t2=16*t2+((t1>>28)&0xff),t1=16*t1+*c-'0';
         else if (*c>='a' && *c<='f') t2=16*t2+((t1>>28)&0xff),t1=16*t1+*c-('a'-10);
         else if (*c>='A' && *c<='F') t2=16*t2+((t1>>28)&0xff),t1=16*t1+*c-('A'-10);
         else break;
         }
      if ((*c=='\0') && (t2==0))
         {
         *col=NXConvertRGBAToColor(((t1>>24)&0xff)/255.0,((t1>>16)&0xff)/255.0,
                                   ((t1>> 8)&0xff)/255.0,(t1&0xff)/255.0);
         return 0;
         }
      }

   if (name[0]=='H' && name[1]=='S' && name[2]=='B')
      {
      for(t1=t2=0,c=name+3;1;c++)
         {
         if (*c>='0' && *c<='9')      t2=16*t2+((t1>>28)&0xff),t1=16*t1+*c-'0';
         else if (*c>='a' && *c<='f') t2=16*t2+((t1>>28)&0xff),t1=16*t1+*c-('a'-10);
         else if (*c>='A' && *c<='F') t2=16*t2+((t1>>28)&0xff),t1=16*t1+*c-('A'-10);
         else break;
         }
      if ((*c=='\0') && (t2==0))
         {
         *col=NXConvertHSBAToColor(((t1>>24)&0xff)/255.0,((t1>>16)&0xff)/255.0,
                                   ((t1>> 8)&0xff)/255.0,(t1&0xff)/255.0);
         return 0;
         }
      }

   if (name[0]=='G' && name[1]=='R' && name[2]=='A' && name[3]=='Y')
      {
      for(t1=t2=0,c=name+4;1;c++)
         {
         if (*c>='0' && *c<='9')      t2=16*t2+((t1>>28)&0xff),t1=16*t1+*c-'0';
         else if (*c>='a' && *c<='f') t2=16*t2+((t1>>28)&0xff),t1=16*t1+*c-('a'-10);
         else if (*c>='A' && *c<='F') t2=16*t2+((t1>>28)&0xff),t1=16*t1+*c-('A'-10);
         else break;
         }
      if ((*c=='\0') && (t1>>16==0) && (t2==0))
         {
         *col=NXConvertGrayAlphaToColor(((t1>>8)&0xff)/255.0,(t1&0xff)/255.0);
         return 0;
         }
      }

   if (name[0]=='C' && name[1]=='M' && name[2]=='Y' && name[3]=='K')
      {
      for(t1=t2=0,c=name+4;1;c++)
         {
         if (*c>='0' && *c<='9')      t2=16*t2+((t1>>28)&0xff),t1=16*t1+*c-'0';
         else if (*c>='a' && *c<='f') t2=16*t2+((t1>>28)&0xff),t1=16*t1+*c-('a'-10);
         else if (*c>='A' && *c<='F') t2=16*t2+((t1>>28)&0xff),t1=16*t1+*c-('A'-10);
         else break;
         }
      if ((*c=='\0') && (t2>>8==0))
         {
         *col=NXConvertCMYKAToColor((t2&0xff)/255.0,((t1>>24)&0xff)/255.0,
                                    ((t1>>16)&0xff)/255.0,((t1>> 8)&0xff)/255.0,
                                    (t1&0xff)/255.0);
         return 0;
         }
      }

   for(i=0;!finished && (i<n);i++)
      {
      finished=1;
      NX_DURING
         if (i<n)
            new=[[colorlistlist objectAt:i] colorNamed:name];
      NX_HANDLER
         switch (NXLocalHandler.code)
            {
          case NX_colorUnknown:
            finished=0;
            break;
          default:
            NX_RERAISE();
            }
      NX_ENDHANDLER
      }

   if (finished) *col=new;
   return !finished;
   }

NXColor ns_get_color_default (const char *name, NXColor dflt)
   {
   NXColor col;

   if (ns_get_color(name, &col))
      return dflt;
   else
      return col;
   }

int ns_lisp_to_color (Lisp_Object color, NXColor *col)
   {
   if (XTYPE(color) == Lisp_String)
      return ns_get_color (XSTRING(color)->data, col);
   else if (XTYPE(color) == Lisp_Symbol)
      return ns_get_color (XSYMBOL(color)->name->data, col);
   return 1;
   }

Lisp_Object ns_color_to_lisp (NXColor col)
   {
   float red,green,blue,alpha,gray;
   char buf[1024];
   const char *str;

   if (str=NXColorName(col))
      return build_string(str);
   
   NXConvertColorToRGBA(col,&red,&green,&blue,&alpha);
   if (red==green && red==blue)
      {
      NXConvertColorToGrayAlpha(col,&gray,&alpha);
      sprintf(buf,"GRAY%02.2x%02.2x",(int)rint(gray*0xff),(int)rint(alpha*0xff));
      return build_string(buf);
      }

   sprintf(buf,"RGB%02.2x%02.2x%02.2x%02.2x",(int)rint(red*0xff),
           (int)rint(green*0xff),(int)rint(blue*0xff),(int)rint(alpha*0xff));
   return build_string(buf);
   }

/* If hl==-1, write out buffer unconditionally
      hl== 0, add glyphs described by glyphs, len
      hl== 1, add glyphs described by glyphs, len in face=1
      hl== 2, clear region described by len, x, y
      hl== 3, add glyphs described by glyphs, len in face=mouse_face_face_id */

void ns_dumpglyphs(struct frame *nf,int x,int y, GLYPH *glyphs,int len,int hl)
   {
   static int faceid=-1,no=0;
   static struct frame *f=0;
   static NXRect r[256];
   static unsigned int l[256];
   static unsigned char *t[256];
   static unsigned char buf[4096];
   static unsigned char *p=buf;
   int tlen = GLYPH_TABLE_LENGTH;
   Lisp_Object *tbase = GLYPH_TABLE_BASE;
   int nfaceid,g;

 start:
   switch(hl)
      {
    case -1:
      if (no==0) return;
      nfaceid=-1;
      break;
    case 0:
      g=*glyphs;
      GLYPH_FOLLOW_ALIASES (GLYPH_TABLE_BASE, GLYPH_TABLE_LENGTH, g);
      nfaceid=FAST_GLYPH_FACE(g);
      break;
    case 1:
      nfaceid=1;
      break;
    case 2:
      nfaceid=0;
      break;
    case 3:
      nfaceid=mouse_face_face_id;
      break;
      }

   if ((no>0)&&
       ((nf!=f) || (nfaceid!=faceid) || (no>=sizeof(t)/sizeof(t[0])) ||
        (p+len-buf>=sizeof(buf)/sizeof(buf[0]))))
      {
      id view=f->display.ns->view;
      int i;
      struct ns_face *face;

      face=f->display.ns->computed_faces[((faceid<-1)||
                                          (faceid>=f->display.ns->n_computed_faces)||
                                          (f->display.ns->computed_faces[faceid]==0))
                                         ? 0 : faceid];

      if (f!=updating_frame || !lockfocused)
         {
         [view lockFocus];
         if (f==updating_frame) lockfocused=1;
         }

      NXSetColor(face->background_color);
      NXRectFillList(r,no);
      NXSetColor(face->foreground_color);
      if (face->font==0) ns_load_font(face);
      [face->font set];
      for(i=0;i<no;i++) if (l[i]>0)
         nswrap_moveshow(r[i].origin.x,r[i].origin.y+f->display.ns->line_height
                         -face->descender,t[i],l[i]);
      if (face->underline)
         {
         PSsetlinewidth(face->underwidth);
         for(i=0;i<no;i++) if (l[i]>0)
            nswrap_moveline(r[i].origin.x,r[i].origin.y+f->display.ns->line_height
                            -face->underpos,floor(r[i].size.width-1));
         PSstroke();
         }
      if (f!=updating_frame)
         {
         [view unlockFocus];
         [[view window] flushWindow];
         DPSFlush();
         }
      no=0;
      }

   if (no==0)
      {
      p=buf;
      faceid=nfaceid;
      f=nf;
      }

   switch(hl)
      {
    case -1:
      break;
    case  0:
      for(t[no]=p;(len>0);len--)
         {
         g=*glyphs;
         GLYPH_FOLLOW_ALIASES (GLYPH_TABLE_BASE, GLYPH_TABLE_LENGTH, g);
         if (FAST_GLYPH_FACE(g)!=nfaceid) break;
         *p=FAST_GLYPH_CHAR(g); *glyphs++;
         if (!NILP(ns_iso_latin)) *p=iso2nsmap[*p];
         p++;
         }
      ns_chars_to_rect(f,x,y,x+(p-t[no])-1,y,&r[no]);
      l[no]=p-t[no];
      x+=l[no];
      no++;
      break;
    case 1:
      for(t[no]=p;len>0;len--)
         {
         g=*glyphs;
         GLYPH_FOLLOW_ALIASES (GLYPH_TABLE_BASE, GLYPH_TABLE_LENGTH, g);
         *p=FAST_GLYPH_CHAR(g); glyphs++;
         if (!NILP(ns_iso_latin)) *p=iso2nsmap[*p];
         p++;
         }
      ns_chars_to_rect(f,x,y,x+(p-t[no])-1,y,&r[no]);
      l[no]=p-t[no];
      x+=l[no];
      no++;
      break;
    case 2:
      ns_chars_to_rect(f,x,y,x+len-1,y,&r[no]);
      t[no]=p;
      l[no]=0;
      len=0;
      no++;
      break;
    case 3:
      for(t[no]=p;len>0;len--)
         {
         g=*glyphs;
         GLYPH_FOLLOW_ALIASES (GLYPH_TABLE_BASE, GLYPH_TABLE_LENGTH, g);
         *p=FAST_GLYPH_CHAR(g); glyphs++;
         if (!NILP(ns_iso_latin)) *p=iso2nsmap[*p];
         p++;
         }
      ns_chars_to_rect(f,x,y,x+(p-t[no])-1,y,&r[no]);
      l[no]=p-t[no];
      x+=l[no];
      no++;
      break;
      }

   if (len>0) goto start;
   }

void ns_dumpcursor(struct frame *f,int nx,int ny)
   {
   NXRect r,s;
   id view=f->display.ns->view;

   if (f->phys_cursor_x==nx && f->phys_cursor_y == ny &&
       f->display.ns->current_cursor==f->display.ns->desired_cursor &&
       NXEqualColor(f->display.ns->current_cursor_color,
                    f->display.ns->desired_cursor_color))
      return;

   if (f!=updating_frame || !lockfocused)
      {
      [view lockFocus];
      if (f==updating_frame) lockfocused=1;
      }

   if (f->phys_cursor_x >= 0 && f->phys_cursor_x < f->width &&
       f->phys_cursor_y >= 0 && f->phys_cursor_y < f->height)
      {
      ns_chars_to_rect(f,f->phys_cursor_x,f->phys_cursor_y,
                       f->phys_cursor_x,f->phys_cursor_y,&r);
      if (NXEqualColor(f->display.ns->current_cursor_color,NX_COLORCLEAR))
         {
         switch(f->display.ns->current_cursor)
            {
          case no_highlight:
            break;
          case filled_box:
            NXHighlightRect(&r);
            break;
          case hollow_box:
            s=r;
            NXHighlightRect(&s);
            s.size.width-=2;
            s.size.height-=2;
            s.origin.x+=1;
            s.origin.y+=1;
            NXHighlightRect(&s);
            break;
          case bar:
            s=r;
            s.origin.y += 0.75 * s.size.height;
            s.size.height *= 0.25;
            NXHighlightRect(&s);
            break;
          case line:
            s=r;
            s.size.width = 2;
            NXHighlightRect(&s);
            break;
            }
         }
      else
         {
         struct ns_face *face=f->display.ns->computed_faces[FAST_GLYPH_FACE(f->phys_cursor_glyph)];
         unsigned char c=FAST_GLYPH_CHAR(f->phys_cursor_glyph);

         NXSetColor(face->background_color);
         NXRectFill(&r);
         NXSetColor(face->foreground_color);
         if (face->font==0) ns_load_font(face);
         [face->font set];
         if (!NILP(ns_iso_latin)) c=iso2nsmap[c];
         nswrap_moveshow(r.origin.x,r.origin.y+f->display.ns->line_height
                         -face->descender,&c,1);
         if (face->underline)
            {
            PSsetlinewidth(face->underwidth);
            nswrap_moveline(r.origin.x,r.origin.y+f->display.ns->line_height
                            -face->underpos,r.size.width-1);
            PSstroke();
            }
         }
      }

   f->phys_cursor_x = nx;
   f->phys_cursor_y = ny;
   f->display.ns->current_cursor = f->display.ns->desired_cursor;
   f->display.ns->current_cursor_color = f->display.ns->desired_cursor_color;

   if (f->phys_cursor_x >= 0 && f->phys_cursor_x < f->width &&
       f->phys_cursor_y >= 0 && f->phys_cursor_y < f->height)
      {
      ns_chars_to_rect(f,f->phys_cursor_x,f->phys_cursor_y,
                       f->phys_cursor_x,f->phys_cursor_y,&r);
      if (NXEqualColor(f->display.ns->current_cursor_color,NX_COLORCLEAR))
         {
         switch(f->display.ns->current_cursor)
            {
          case no_highlight:
            break;
          case filled_box:
            NXHighlightRect(&r);
            break;
          case hollow_box:
            s=r;
            NXHighlightRect(&s);
            s.size.width-=2;
            s.size.height-=2;
            s.origin.x+=1;
            s.origin.y+=1;
            NXHighlightRect(&s);
            break;
          case bar:
            s=r;
            s.origin.y += 0.75 * s.size.height;
            s.size.height *= 0.25;
            NXHighlightRect(&s);
            break;
          case line:
            s=r;
            s.size.width = 2;
            NXHighlightRect(&s);
            break;
            }
         }
      else
         {
         struct frame_glyphs *current_glyphs = FRAME_CURRENT_GLYPHS (f);
         struct ns_face *face;
         unsigned char c;

         f->phys_cursor_glyph
            = ((current_glyphs->enable[f->phys_cursor_y]
                && f->phys_cursor_x < current_glyphs->used[f->phys_cursor_y])
               ? current_glyphs->glyphs[f->phys_cursor_y][f->phys_cursor_x]
               : SPACEGLYPH);
         face=f->display.ns->computed_faces[FAST_GLYPH_FACE(f->phys_cursor_glyph)];
         c=FAST_GLYPH_CHAR(f->phys_cursor_glyph);

         NXSetColor(face->background_color);
         NXRectFill(&r);
         NXSetColor(f->display.ns->current_cursor_color);
         switch(f->display.ns->current_cursor)
            {
          case no_highlight:
            break;
          case filled_box:
            NXRectFill(&r);
            break;
          case hollow_box:
            NXFrameRect(&r);
            break;
          case bar:
            s=r;
            s.origin.y += 0.75 * s.size.height;
            s.size.height *= 0.25;
            NXRectFill(&s);
            break;
          case line:
            s=r;
            s.size.width = 2;
            NXRectFill(&s);
            break;
            }
	 if (NXEqualColor(f->display.ns->current_cursor_color,
                          face->foreground_color))
            NXSetColor(face->background_color);
	 else
            NXSetColor(face->foreground_color);

         if (face->font==0) ns_load_font(face);
         [face->font set];

         if (!NILP(ns_iso_latin)) c=iso2nsmap[c];
         nswrap_moveshow(r.origin.x,r.origin.y+f->display.ns->line_height
                         -face->descender,&c,1);
         if (face->underline)
            {
            PSsetlinewidth(face->underwidth);
            nswrap_moveline(r.origin.x,r.origin.y+f->display.ns->line_height
                            -face->underpos,r.size.width-1);
            PSstroke();
            }
         }
      }

   if (f!=updating_frame)
      {
      [view unlockFocus];
      [[view window] flushWindow];
      DPSFlush();
      }
   }

static int ns_cursor_to(int row, int col)
   {
   struct frame *f=(updating_frame ? updating_frame : selected_frame);
   id view=f->display.ns->view;

   curs_x = col;
   curs_y = row;

   if (f!=updating_frame)
      {
      ns_dumpglyphs(f,0,0,0,0,-1);
      ns_dumpcursor(f,curs_x,curs_y);
      }

   return 0;
   }

void ns_set_offset (struct frame *f, int xoff, int yoff, int change_grav)
   {
   const NXScreen *screen;
   id view=f->display.ns->view;
   f->display.ns->left=xoff;
   f->display.ns->top=yoff;
   if (view != nil && (screen=[[view window] screen])!=0)
      [[view window] moveTopLeftTo:f->display.ns->left
                                  :screen->screenBounds.size.height
                                   -f->display.ns->top];
   }


static void ns_reset_clip(id view)
   {
   int i;
   id sv,v;
   NXRect r,s;

   [view lockFocus];
   PSinitclip();
   PSnewpath();
   [view getBounds:&r];
   nswrap_rect(r.origin.x,r.origin.y,r.size.width,r.size.height);

   if (sv=[[view superview] subviews]) for(i=[sv count]-1;i>=0;i--)
      {
      v=[sv objectAt:i];
      if (v==view) continue;
      [v getFrame:&s];
      [view convertRectFromSuperview:&s];
      if (!NXIntersectionRect(&r,&s)) continue;
      if (s.origin.y==0)
         {
         s.origin.y-=1;
         s.size.height+=1;
         }
      nswrap_rect_rev(s.origin.x,s.origin.y,s.size.width,s.size.height);
      }

   PSclip();
   PSnewpath();
   PSgstate();
   DPSDefineUserObject([view gState]);
   [view unlockFocus];
   }

static void ns_adjust_size(struct frame *f)
   {
   int x, y, rows, cols;
   id view=f->display.ns->view;
   id win=[view window];
   NXRect r;

   [win invalidateCursorRectsForView:view];
   [win discardTrackingRect:0];
   [win getFrame:&r];
   r.origin.x=r.origin.y=0;
   [win setTrackingRect:&r inside:NO owner:view tag:0 left:NO right:NO];
   ns_reset_clip(view);
   [view getBounds:&r];
   ns_rect_to_glyph_coords(f,&r,0,&x,&y,&cols,&rows);
   change_frame_size (f, rows, cols, 0, 1);
   SET_FRAME_GARBAGED (f);
   }

void ns_set_window_size (struct frame *f, int change_grav, int cols, int rows)
   {
   id view=f->display.ns->view;
   id window;
   int vbextra=FRAME_HAS_VERTICAL_SCROLL_BARS(f) ?
               rint(VERTICAL_SCROLL_BAR_WIDTH*f->display.ns->face->width) : 0;
   const NXScreen *screen;
   NXRect r,wr;

   if (view!=nil)
      {
      window=[view window];
      screen=[window screen];
      [view getFrame:&r];
      [window getFrame:&wr];

      ns_dumpcursor(f,-1,-1);

      r.origin.x=f->display.ns->internal_border_width;
      r.origin.y=f->display.ns->internal_border_width;
      r.size.width =wr.size.width -(f->display.ns->internal_border_width*2+
                                    f->display.ns->border_width) - vbextra;
      r.size.height=wr.size.height-(f->display.ns->internal_border_width*2+
                                    f->display.ns->border_height);
      [view setFrame:&r];

      wr.size.width +=cols*f->display.ns->face->width-r.size.width;
      wr.size.height+=rows*f->display.ns->line_height-r.size.height;

      if (screen)
         {
         wr.origin.x=f->display.ns->left;
         wr.origin.y=screen->screenBounds.size.height-(f->display.ns->top
                                                       +wr.size.height);
         }

      wr.origin.x=BOUND(-SCREENMAX,wr.origin.x,SCREENMAX);
      wr.origin.y=BOUND(-SCREENMAX,wr.origin.y,SCREENMAX);
      [window placeWindow:&wr];

      ns_adjust_size(f);
      }
   else
      {
      change_frame_size(f, rows, cols, 0, 0);
      SET_FRAME_GARBAGED(f);
      }
   }

void ns_set_mouse_pixel_position (struct frame *f, int pix_x, int pix_y)
   {
   id view=f->display.ns->view;

   ns_raise_frame(f);
   [view lockFocus];
   PSsetmouse((float)pix_x,(float)pix_y);
   [view unlockFocus];
   DPSFlush();
   }

int ns_set_mouse_position (struct frame *f, int x, int y)
   {
   NXRect r;

   ns_chars_to_rect(f,x,y,x,y,&r);
   ns_set_mouse_pixel_position(f,r.origin.x+r.size.width/2,
                               r.origin.y+r.size.height/2);
   return 0;
   }

/* Take proper action when the mouse has moved to position X, Y on frame F
   as regards highlighting characters that have mouse-face properties.
   Also dehighlighting chars where the mouse was before.  */

static void note_mouse_highlight (struct frame *f, int x, int y)
   {
   /* XXX */
   }

static void show_mouse_face (int hl)
   {
   /* XXX */
   }

static void clear_mouse_face (void)
   {
   /* XXX */
   }

int ns_new_font (struct frame *f)
   {
   struct ns_face face;
   NXColor col;
   Lisp_Object tem;

   tem=get_frame_param(f, Qfont);
   CHECK_STRING(tem, 0);
   face.name=NXUniqueString(XSTRING(tem)->data);

   tem=get_frame_param(f, Qfontsize);
   CHECK_NUMBER_OR_FLOAT(tem, 0);
   face.size=XFLOATINT(tem);

   tem=get_frame_param(f, Qforeground_color);
   if (NILP(tem) || ns_lisp_to_color(tem,&col))
      face.foreground_color=NX_COLORBLACK;
   else
      face.foreground_color=col;

   tem=get_frame_param(f, Qbackground_color);
   if (NILP(tem) || ns_lisp_to_color(tem,&col))
      face.background_color=NX_COLORWHITE;
   else
      face.background_color=col;

   tem=get_frame_param(f, Qunderline);
   if (NILP(tem))
      face.underline=0;
   else
      face.underline=1;

   if (ns_load_font(&face))
      return 1;

   *f->display.ns->face=face;
   ns_frame_update_line_height(f);
   ns_set_window_size(f, 0, f->width, f->height);
   return 0;
   }

static int ns_clear_frame(void)
   {
   struct frame *f=(updating_frame ? updating_frame : selected_frame);
   id view=f->display.ns->view;
   int i;
   NXRect r;

   if (f!=updating_frame || !lockfocused)
      {
      [view lockFocus];
      if (f==updating_frame) lockfocused=1;
      }

   ns_dumpcursor(f,-1,-1);
   curs_x=curs_y=0;

   [[view window] display];

   if (f!=updating_frame)
      {
      ns_dumpcursor (f,f->cursor_x,f->cursor_y);
      [view unlockFocus];
      [[view window] flushWindow];
      DPSFlush();
      }
   return 0;
   }

static int ns_clear_end_of_line(int first_unused)
   {
   struct frame *f=(updating_frame ? updating_frame : selected_frame);
   id view=f->display.ns->view;
   NXRect r;

   if (curs_y < 0 || curs_y >= f->height) return 0;
   if (first_unused <= 0) return 0;
   if (first_unused > f->width) first_unused = f->width;

   if (curs_y == f->phys_cursor_y &&
       curs_x <= f->phys_cursor_x && f->phys_cursor_x < first_unused)
      ns_dumpcursor(f,-1,-1);
   ns_dumpglyphs(f,curs_x,curs_y,0,first_unused-curs_x,2);

   if (f!=updating_frame)
      {
      ns_dumpglyphs(f,0,0,0,0,-1);
      ns_dumpcursor(f,f->cursor_x,f->cursor_y);
      }
   return 0;
   }

static int ns_ins_del_lines(int vpos, int n)
   {
   struct frame *f=(updating_frame ? updating_frame : selected_frame);
   id view=f->display.ns->view;
   int height;
   NXPoint p;
   NXRect r,s;

   curs_x = 0;
   curs_y = vpos;

   if (curs_y >= flexlines) return 0;

   if (n>0)
      {
      height=f->display.ns->line_height*n;
      ns_chars_to_rect(f,curs_x,curs_y,f->width-1,flexlines-1,&r);
      s=r;
      p=r.origin;

      r.size.height-=height;
      p.y          +=height;
      s.size.height =height;
      }
   else if (n<0)
      {
      height=f->display.ns->line_height*(-n);
      ns_chars_to_rect(f,curs_x,curs_y,f->width-1,flexlines-1,&r);
      s=r;
      p=r.origin;

      r.size.height-=height;
      r.origin.y   +=height;
      s.origin.y    =s.origin.y+s.size.height-height;
      s.size.height =height;
      }

   if (f!=updating_frame || !lockfocused)
      {
      [view lockFocus];
      if (f==updating_frame) lockfocused=1;
      }

   ns_dumpcursor(f,-1,-1);
   if (r.size.height>0) NXCopyBits(NXNullObject,&r,&p);

   if (s.size.height>0)
      {
      NXSetColor(f->display.ns->face->background_color);
      NXRectFill(&s);
      }

   if (f!=updating_frame)
      {
      ns_dumpcursor(f,f->cursor_x,f->cursor_y);
      [view unlockFocus];
      [[view window] flushWindow];
      DPSFlush();
      }

   return 0;
   }

static int ns_change_line_highlight(int new_highlight, int vpos,
                                     int first_unused_hpos)
   {
   highlight = new_highlight;
   curs_x = 0;
   curs_y = vpos;
   ns_clear_end_of_line (updating_frame->width);
   return 0;
   }

static int ns_insert_glyphs(GLYPH *start, int len)
   {
   abort();
   return 0;
   }

static int ns_write_glyphs(GLYPH *start, int len)
   {
   struct frame *f=(updating_frame ? updating_frame : selected_frame);

   if (f!=updating_frame)
      {
      curs_x = f->cursor_x;
      curs_y = f->cursor_y;
      }

   if (curs_y == f->phys_cursor_y && curs_x <= f->phys_cursor_x
       && curs_x + len > f->phys_cursor_x)
      ns_dumpcursor(f,-1,-1);
   ns_dumpglyphs(f,curs_x,curs_y,start,len,highlight);

   if (f!=updating_frame)
      {
      ns_dumpglyphs(f,0,0,0,0,-1);
      ns_dumpcursor(f,f->cursor_x+len,f->cursor_y);
      }
   else
      curs_x += len;

   return 0;
   }

static int ns_delete_glyphs(GLYPH *start,int len)
   {
   abort();
   return 0;
   }

static int ns_ring_bell(void)
   {
   NXBeep();
   return 0;
   }

static int ns_reset_terminal_modes(void)
   {
   return 0;
   }

static int ns_set_terminal_modes(void)
   {
   return 0;
   }

static int ns_update_begin(struct frame *f)
   {
   flexlines = f->height;
   highlight = 0;
   lockfocused=0;
   if (f == mouse_face_mouse_frame)
      {
      /* Don't do highlighting for mouse motion during the update.  */
      mouse_face_defer = 1;
      if (!NILP (mouse_face_window))
         {
         int firstline, lastline, i;
         struct window *w = XWINDOW (mouse_face_window);

         /* Find the first, and the last+1, lines affected by redisplay.  */
         for (firstline = 0; firstline < f->height; firstline++)
	    if (FRAME_DESIRED_GLYPHS (f)->enable[firstline])
               break;

         lastline = f->height;
         for (i = f->height - 1; i >= 0; i--)
	    {
            if (FRAME_DESIRED_GLYPHS (f)->enable[i])
               break;
            else
               lastline = i;
	    }

         /* Can we tell that this update does not affect the window
            where the mouse highlight is?  If so, no need to turn off.  */
         if (! (firstline > (XFASTINT (w->top) + window_internal_height (w))
                || lastline < XFASTINT (w->top)))
	    /* Otherwise turn off the mouse highlight now.  */
	    clear_mouse_face ();
         }
      }
   return 0;
   }

static int ns_update_end(struct frame *f)
   {
   ns_dumpglyphs(f,0,0,0,0,-1);
   ns_dumpcursor(f,curs_x,curs_y);
   if (lockfocused)
      {
      id view=f->display.ns->view;
      [view unlockFocus];
      [[view window] flushWindow];
      DPSFlush();
      lockfocused=0;
      }
   return 0;
   }

static int ns_frame_up_to_date(struct frame *f)
   {
   if (mouse_face_deferred_gc || (f == mouse_face_mouse_frame))
      {
      note_mouse_highlight (mouse_face_mouse_frame,
			    mouse_face_mouse_x, mouse_face_mouse_y);
      mouse_face_deferred_gc = 0;
      }
   }

static int ns_set_terminal_window(int n)
   {
   struct frame *f=(updating_frame ? updating_frame : selected_frame);

   flexlines = ((n > 0) && (n <= f->height)) ? n : f->height;
   return 0;
   }

void ns_send_appdefined (int value)
   {
   int i;

   /* Only post this event if we haven't already posted one.  This will end
      the [NXApp run] main loop after having processed all events queued at
      this moment.  */
   if (!send_appdefined)
      abort ();
   else
      {
      NXEvent nxev;
      nxev.type = NX_APPDEFINED;
      nxev.window = BEANS + value;

      /* Post an application defined event on the event queue.  When this is
         recieved the [NXApp run] will return, thus having processed all
         events which are currently queued.  */
      if (DPSPostEvent (&nxev, NO)) abort ();

      /* We only need one NX_APPDEFINED event to stop NXApp from running.  */
      send_appdefined = NO;

      if (timed_entry)
         {
         DPSRemoveTimedEntry (timed_entry);
         timed_entry = 0;
         }

      if (cursor_blink_entry)
         {
         DPSRemoveTimedEntry (cursor_blink_entry);
         cursor_blink_entry = 0;
         }

      if (select_readfds)
         {
         for (i = 0; i < select_nfds; i++) if (FD_ISSET (i, select_readfds))
            DPSRemoveFD (i);
         select_readfds = 0;
         }

      if (select_writefds)
         {
         for (i = 0; i < select_nfds; i++) if (FD_ISSET (i, select_writefds))
            DPSRemoveFD (i);
         select_writefds = 0;
         }

      if (select_exceptfds)
         {
         for (i = 0; i < select_nfds; i++) if (FD_ISSET (i, select_exceptfds))
            DPSRemoveFD (i);
         select_exceptfds = 0;
         }
      }
   }

/* Post an event to ourself and keep reading events until we read it back
   again.  In effect process all events which were waiting.  */
static int ns_read_socket (int sd, struct input_event *bufp,
                           int numchars, int waitp, int expected)
   {
   int nevents;

   if (send_appdefined) abort ();

   /* We must always send one NX_APPDEFINED event to ourself, otherwise
      [NXApp run] will never exit.  */
   send_appdefined = YES;

   if (!waitp)
      {
      /* Post an application defined event on the event queue.  When this is
         recieved the [NXApp run] will return, thus having processed all
         events which are currently queued, if any.  */
      ns_send_appdefined (-1);
      }

   events = bufp;
   eventsleft = numchars;

   [(Application *)NXApp run];

   nevents = numchars - eventsleft;
   eventsleft = 0;
   events = 0;
   return nevents;
   }

static int ns_reassert_line_highlight(int new, int vpos)
   {
   highlight = new;
   return 0;
   }

static void ns_mouse_position (struct frame **f, Lisp_Object *bar_window,
                               enum scroll_bar_part *part,
                               Lisp_Object *x, Lisp_Object *y,
                               unsigned long *time)
   {
   id view;
   NXPoint position;
   int xchar, ychar;

   mouse_moved = 0;

   if (last_mouse_frame) *f=last_mouse_frame;
   view=(*f)->display.ns->view;

   [[view window] getMouseLocation:&position];
   [view convertPoint:&position fromView:nil];
   ns_pixel_to_glyph_coords(*f, (int)rint(position.x), (int)rint(position.y),
                            &xchar, &ychar, 0, 1);
   ns_chars_to_rect(*f, xchar, ychar, xchar, ychar, &last_mouse_glyph);
   if (bar_window) *bar_window = Qnil;
   if (part) *part = 0;
   if (x) XSET (*x, Lisp_Int, (int)rint(position.x));
   if (y) XSET (*y, Lisp_Int, (int)rint(position.y));
   if (time) *time = last_mouse_movement_time;
   }

static void ns_frame_rehighlight(void)
   {
   struct frame *f=0;

   if (ns_focus_frame)
      {
      f = ((XGCTYPE (FRAME_FOCUS_FRAME (ns_focus_frame)) == Lisp_Frame)
           ? XFRAME (FRAME_FOCUS_FRAME (ns_focus_frame))
           : ns_focus_frame);
      if (!FRAME_LIVE_P (f))
         {
         FRAME_FOCUS_FRAME (ns_focus_frame) = Qnil;
         f = ns_focus_frame;
         }
      }

  if (f != ns_highlight_frame)
     {
     if (f)
        [[f->display.ns->view window] makeKeyAndOrderFront:NXApp];
/*     else
        resign key window */
     }
   }

static void ns_frame_raise_lower(struct frame *f, int raise)
   {
   if (raise)
      ns_raise_frame(f);
   else
      ns_lower_frame(f);
   }

static void ns_set_vertical_scroll_bar(struct window *window,
                                       int portion, int whole, int position)
   {
   Lisp_Object win;
   NXRect r,s;
   struct frame *f= XFRAME (WINDOW_FRAME (window));
   id view=f->display.ns->view;
   int top = XINT (window->top);
   int left = WINDOW_VERTICAL_SCROLL_BAR_COLUMN (window);
   int height = WINDOW_VERTICAL_SCROLL_BAR_HEIGHT (window);
   id bar;

   XSET (win, Lisp_Window, window);
   ns_chars_to_rect(f,left,top,left+VERTICAL_SCROLL_BAR_WIDTH-1,
                     top+height-1,&r);
   [view convertRectToSuperview:&r];

   if (r.size.width*2.5>=r.size.height)
      {
      if (!NILP (window->vertical_scroll_bar))
         {
         bar = XNS_SCROLL_BAR(window->vertical_scroll_bar);
         [bar free];
         }
      return;
      }

   if (NILP (window->vertical_scroll_bar))
      {
      bar=[[EmacsScroller alloc] initFrame:&r window:win];
      VOID_TO_LISP (window->vertical_scroll_bar, bar);
      }
   else
      {
      bar= XNS_SCROLL_BAR(window->vertical_scroll_bar);
      [bar setFrame:&r];
      }

   [bar setPosition:position portion:portion whole:whole];
   }

/* The following three hooks are used when we're doing a thorough
   redisplay of the frame.  We don't explicitly know which scroll bars
   are going to be deleted, because keeping track of when windows go
   away is a real pain - "Can you say set-window-configuration, boys
   and girls?"  Instead, we just assert at the beginning of redisplay
   that *all* scroll bars are to be removed, and then save a scroll bar
   from the fiery pit when we actually redisplay its window.  */

/* Arrange for all scroll bars on FRAME to be removed at the next call
   to `*judge_scroll_bars_hook'.  A scroll bar may be spared if
   `*redeem_scroll_bar_hook' is applied to its window before the judgement.  */
static void ns_condemn_scroll_bars(struct frame *f)
   {
   int i;
   id view;
   id subviews=[[f->display.ns->view superview] subviews];
   for (i=[subviews count]-1; i>=0; i--)
      {
      view=[subviews objectAt:i];
      if (![view isKindOf:[EmacsScroller class]]) continue;
      [view condemn];
      }
   }

/* Unmark WINDOW's scroll bar for deletion in this judgement cycle.
   Note that WINDOW isn't necessarily condemned at all.  */
static void ns_redeem_scroll_bar(struct window *window)
   {
   id bar;
   if (!NILP(window->vertical_scroll_bar))
      {
      bar=XNS_SCROLL_BAR (window->vertical_scroll_bar);
      [bar reprieve];
      }
   }

/* Remove all scroll bars on FRAME that haven't been saved since the
   last call to `*condemn_scroll_bars_hook'.  */
static void ns_judge_scroll_bars(struct frame *f)
   {
   int i;
   id view;
   id subviews=[[f->display.ns->view superview] subviews];
   for (i=[subviews count]-1; i>=0; i--)
      {
      view=[subviews objectAt:i];
      if (![view isKindOf:[EmacsScroller class]]) continue;
      [view judge];
      }
   }

static Lisp_Object append2(Lisp_Object list, Lisp_Object item)
   {
   Lisp_Object array[2];
   array[0]=list;
   array[1]=Fcons(item,Qnil);
   return Fnconc(2,&array[0]);
   }

int ns_check_available (void)
   {
   int ret=1;

   if (ret)
      {
      NXEventHandle handle=NXOpenEventStatus();
      if (handle)
         NXCloseEventStatus(handle);
      else
         ret=0;
      }

   return ret;
   }

/* Start the Application and get things rolling.  */
void ns_term_init (char *display_name)
   {
   extern Lisp_Object Fset_input_mode(Lisp_Object,Lisp_Object,
                                      Lisp_Object,Lisp_Object);

   [EmacsApp new];
   if ((ns_current_display = NXApp) == 0)
      fatal ("NeXTstep on %s not responding.\n", display_name);

      {
      const char *value=NXGetDefaultValue([NXApp appName],"Menus");
      if (!value || strcasecmp(value,"NO"))
         ns_set_frame_menubar(selected_frame,1);
      }

      {
      const char *value, *parameter;
      if (value=NXGetDefaultValue([NXApp appName],parameter="ShrinkSpace"))
         {
         double f;
         if (strcasecmp(value,"YES")==0)
            ns_shrink_space=make_float(0.75);
         else if (strcasecmp(value,"NO")==0)
            ns_shrink_space=make_float(1.0);
         else if ((f=atof(value))>0.0)
            ns_shrink_space=make_float(f);
         else fprintf(stderr, "Bad value for default \"%s\": \"%s\"\n",
                      parameter,value);
         }

      if (value=NXGetDefaultValue([NXApp appName],parameter="AlternateIsMeta"))
         {
         if (strcasecmp(value,"YES")==0)
            ns_alternate_is_meta=Qt;
         else if (strcasecmp(value,"NO")==0)
            ns_alternate_is_meta=Qnil;
         else if (strcasecmp(value,"LEFT")==0)
            ns_alternate_is_meta=Qleft;
         else if (strcasecmp(value,"RIGHT")==0)
            ns_alternate_is_meta=Qright;
         else fprintf(stderr, "Bad value for default \"%s\": \"%s\"\n",
                      parameter,value);
         }

      if (value=NXGetDefaultValue([NXApp appName],parameter="UseOpenPanel"))
         {
         if (strcasecmp(value,"YES")==0)
            ns_use_open_panel=YES;
         else if (strcasecmp(value,"NO")==0)
            ns_use_open_panel=NO;
         else fprintf(stderr, "Bad value for default \"%s\": \"%s\"\n",
                      parameter,value);
         }

      if (value=NXGetDefaultValue([NXApp appName],parameter="UseYesNoPanel"))
         {
         if (strcasecmp(value,"YES")==0)
            ns_use_yes_no_panel=YES;
         else if (strcasecmp(value,"NO")==0)
            ns_use_yes_no_panel=NO;
         else fprintf(stderr, "Bad value for default \"%s\": \"%s\"\n",
                      parameter,value);
         }

      if (value=NXGetDefaultValue([NXApp appName],parameter="ISOLatin"))
         {
         if (strcasecmp(value,"YES")==0)
            ns_iso_latin=Qt;
         else if (strcasecmp(value,"NO")==0)
            ns_iso_latin=Qnil;
         else fprintf(stderr, "Bad value for default \"%s\": \"%s\"\n",
                      parameter,value);
         }

      if (value=NXGetDefaultValue([NXApp appName],parameter="CursorBlinkRate"))
         {
         double f;
         if (strcasecmp(value,"NO")==0)
            ns_cursor_blink_rate=Qnil;
         else if (strcasecmp(value,"YES")==0)
            ns_cursor_blink_rate=make_float(0.5);
         else if ((f=atof(value))>0)
            ns_cursor_blink_rate=make_float(f);
         else fprintf(stderr, "Bad value for default \"%s\": \"%s\"\n",
                      parameter,value);
         }
      }

   keymap_init();
   
      {
      id cl;
      char *fname=alloca(XSTRING(Vdata_directory)->size+10);
      
      strcpy(fname,XSTRING(Vdata_directory)->data);
      strcat(fname,"Emacs.clr");
      cl=[[NXColorList alloc] initWithName:"Emacs" fromFile:fname];
      if (cl==nil)
         fatal ("Could not find %s.\n", fname);
      [[NXColorList availableColorLists] addObject:cl];
      }

      {
      char c[1024];
      PSnextrelease(1024,c);
      Vwindow_system_version = build_string(c);
      }

   clear_frame_hook = ns_clear_frame;
   clear_end_of_line_hook = ns_clear_end_of_line;
   ins_del_lines_hook = ns_ins_del_lines;
   change_line_highlight_hook = ns_change_line_highlight;
   insert_glyphs_hook = ns_insert_glyphs;
   write_glyphs_hook = ns_write_glyphs;
   delete_glyphs_hook = ns_delete_glyphs;
   update_begin_hook = ns_update_begin;
   update_end_hook = ns_update_end;
   ring_bell_hook = ns_ring_bell;
   reset_terminal_modes_hook = ns_reset_terminal_modes;
   set_terminal_modes_hook = ns_set_terminal_modes;
   set_terminal_window_hook = ns_set_terminal_window;
   read_socket_hook = ns_read_socket;
   frame_up_to_date_hook = ns_frame_up_to_date;
   cursor_to_hook = ns_cursor_to;
   reassert_line_highlight_hook = ns_reassert_line_highlight;
   mouse_position_hook = ns_mouse_position;
   frame_rehighlight_hook = ns_frame_rehighlight;
   frame_raise_lower_hook = ns_frame_raise_lower;
   set_vertical_scroll_bar_hook = ns_set_vertical_scroll_bar;
   condemn_scroll_bars_hook = ns_condemn_scroll_bars;
   redeem_scroll_bar_hook = ns_redeem_scroll_bar;
   judge_scroll_bars_hook = ns_judge_scroll_bars;

   scroll_region_ok = 1;
   char_ins_del_ok = 0;
   line_ins_del_ok = 1;
   fast_clear_end_of_line = 1;
   memory_below_frame = 0;
   baud_rate = 38400;

   change_keyboard_wait_descriptor(-1);

   /* No interupt input under NS */
   Fset_input_mode (Qnil, Qnil, Qt, Qnil);
   
   [(Application *)NXApp run];
   }

void syms_of_nsterm ()
   {
   DEFVAR_LISP("ns-input-file", &ns_input_file,
               "The file specified in the last NS event.");
   ns_input_file=Qnil;

   DEFVAR_LISP("ns-input-ascii", &ns_input_ascii,
               "The data received in the last NS drag event..");
   ns_input_ascii=Qnil;

   DEFVAR_LISP("ns-input-font", &ns_input_font,
               "The font specified in the last NS event.");
   ns_input_font=Qnil;

   DEFVAR_LISP("ns-input-fontsize", &ns_input_fontsize,
               "The fontsize specified in the last NS event.");
   ns_input_fontsize=Qnil;

   DEFVAR_LISP ("ns-input-line", &ns_input_line,
                "The line specified in the last NS event.");
   ns_input_line=Qnil;

   DEFVAR_LISP ("ns-input-color", &ns_input_color,
                "The color specified in the last NS event.");
   ns_input_color=Qnil;

   DEFVAR_LISP ("ns-gdb-status", &ns_gdb_status,
                "Status of program running under debugger.");
   ns_gdb_status=Qnil;

   DEFVAR_LISP ("ns-alternate-is-meta", &ns_alternate_is_meta,
   "This variable describes what the effect of the alternate key is under NS.\n\
nil means that the alternate key is not interpreted by Emacs at all,\n\
t means that the alternate key is used as meta key,\n\
left means that the left alternate key only is interpreted as meta key,\n\
right means that the right alternate key only is interpreted as meta key.\n\
(This variable should only be read, never set.)");
   ns_alternate_is_meta=Qt;

   DEFVAR_LISP ("ns-iso-latin", &ns_iso_latin,
   "If non-nil use the ISO Latin 8859/1 encoding.  Otherwise use the NS encoding.\n\
(This variable should only be read, never set.)");
   ns_iso_latin=Qnil;

   DEFVAR_LISP ("ns-shrink-space", &ns_shrink_space,
   "Amount by which spacing between lines is compressed.\n\
(This variable should only be read, never set.)");
   ns_shrink_space=make_float(0.75);

   DEFVAR_LISP ("ns-cursor-blink-rate", &ns_cursor_blink_rate,
   "Rate at which the Emacs cursor blinks (in seconds).\n\
Set to nil to disable blinking.");
   ns_cursor_blink_rate=Qnil;

   DEFVAR_BOOL ("ns-use-open-panel", &ns_use_open_panel,
     "*Non-nil means to use ns-read-file-name whenever read-file-name is called.");
   ns_use_open_panel=NO;

   DEFVAR_BOOL ("ns-use-yes-no-panel", &ns_use_yes_no_panel,
     "*Non-nil means to use ns-yes-or-no-p whenever yes-or-no-p or y-or-n-p are called.");
   ns_use_yes_no_panel=NO;
   }

static void timeout_handler (DPSTimedEntry timedEntry, double now, void *userData)
   {
   /* The timeout specified to ns_select has passed.  */
   ns_send_appdefined (-2);
   }

static void cursor_blink_handler (DPSTimedEntry cursorBlinkEntry, double now,
                                  void *userData)
   {
   if (!ns_highlight_frame) return;
   if (ns_highlight_frame->display.ns->current_cursor==no_highlight)
      {
      Lisp_Object tem=get_frame_param(ns_highlight_frame, Qcursor_type);
      ns_highlight_frame->display.ns->desired_cursor=ns_lisp_to_cursor_type(tem);
      }
   else
      {
      ns_highlight_frame->display.ns->desired_cursor=no_highlight;
      }

   ns_dumpglyphs(ns_highlight_frame,0,0,0,0,-1);
   ns_dumpcursor(ns_highlight_frame,ns_highlight_frame->cursor_x,
                 ns_highlight_frame->cursor_y);
   }

/* One of the file selectors which has been added with a DPSAddFD by
   ns_select has finished a read, has data waiting on it or has finished a
   write.  Let ns_select know by sending it an event.  */
static void fd_handler (int fd, void *data)
   {
   ns_send_appdefined (fd);
   }

/* This is where events get dispatched to the application.  This is done
   whilst waiting for all input.  */
int ns_select (int nfds, fd_set *readfds, fd_set *writefds,
               fd_set *exceptfds, struct timeval *timeout)
   {
   int i, j, tfds;
   double time;
   NXEvent *ev;
   struct timezone tz;
   struct timeval curtime;

   if (ns_current_display == nil)
      return select (nfds, readfds, writefds, exceptfds, timeout);

   select_nfds = nfds;
   select_readfds = readfds;
   select_writefds = writefds;
   select_exceptfds = exceptfds;
   tfds = 0;

   if (readfds) for (i = 0; i < nfds; i++) if (FD_ISSET (i, readfds))
      {
      if (i >= tfds) tfds = i + 1;
      DPSAddFD (i, fd_handler, 0, NX_BASETHRESHOLD + 1);
      }

   if (writefds) for (i = 0; i < nfds; i++) if (FD_ISSET (i, writefds))
      {
      if (i >= tfds) tfds = i + 1;
      DPSAddFD (i, fd_handler, 0, NX_BASETHRESHOLD + 1);
      }

   if (exceptfds) for (i = 0; i < nfds; i++) if (FD_ISSET (i, exceptfds))
      {
      if (i >= tfds) tfds = i + 1;
      DPSAddFD (i, fd_handler, 0, NX_BASETHRESHOLD + 1);
      }
   nfds = tfds;

   if (timeout)
      {
      time = ((double) timeout->tv_sec) + ((double) timeout->tv_usec) / 1000000.0;
      gettimeofday (&curtime, &tz);
      timeout->tv_sec += curtime.tv_sec;
      timeout->tv_usec += curtime.tv_usec;
      /* Set a DPSTimedEntry as timeout.  */
      timed_entry = DPSAddTimedEntry (time, timeout_handler,
                                      0, NX_BASETHRESHOLD + 1);
      }

   if (NUMBERP(ns_cursor_blink_rate))
      {
      if (ns_highlight_frame &&
          ns_highlight_frame->display.ns->current_cursor==no_highlight)
         {
         Lisp_Object tem=get_frame_param(ns_highlight_frame, Qcursor_type);
         ns_highlight_frame->display.ns->desired_cursor=ns_lisp_to_cursor_type(tem);
         ns_dumpglyphs(ns_highlight_frame,0,0,0,0,-1);
         ns_dumpcursor(ns_highlight_frame,ns_highlight_frame->cursor_x,
                       ns_highlight_frame->cursor_y);
         }
      cursor_blink_entry = DPSAddTimedEntry (XFLOATINT(ns_cursor_blink_rate),
                                             cursor_blink_handler, 0,
                                             NX_BASETHRESHOLD + 1);
      }

   /* Let Application dispatch events until it recieves an event of the type
      NX_APPDEFINED, which should only be sent by fd_handler or
      timeout_handler.  */
   gobble_input (timeout ? 1 : 0);
   ev = &last_appdefined_event;

   /* select does not yet have to return modified timeout information, does it?  */
#if 0
   if (timeout)
      {
      gettimeofday (&curtime, &tz);
      timeout->tv_usec -= curtime.tv_usec;
      timeout->tv_sec -= curtime.tv_sec;
      while (timeout->tv_usec < 0)
         {
         timeout->tv_usec += 1000000;
         timeout->tv_sec--;
         }
      while (timeout->tv_usec > 1000000)
         {
         timeout->tv_usec -= 1000000;
         timeout->tv_sec++;
         }
      if (timeout->tv_sec < 0)
         {
         timeout->tv_sec = 0;
         timeout->tv_usec = 0;
         }
      }
#endif

   if (ev->type != NX_APPDEFINED) abort ();

   i = ev->window - BEANS;
   if (i == -2)
      {
      /* The NX_APPDEFINED event we recieved was the result of a timeout.  */
      return 0;
      }
  else if (i == -1)
     {
     if (!timeout)
        return 0;
     else
        {
        /* The NX_APPDEFINED event we recieved was the result of at least
           one real input event arriving.  */
        errno = EINTR;
        return -1;
        }
     }
  else if (i < 0 || i >= nfds)
     {
     abort ();
     }

   if (readfds)
      if (FD_ISSET (i, readfds))
         {
         FD_ZERO (readfds);
         FD_SET (i,readfds);
         }
      else
         FD_ZERO (readfds);

   if (writefds)
      if (FD_ISSET (i, writefds))
         {
         FD_ZERO (writefds);
         FD_SET (i,writefds);
         }
      else
         FD_ZERO (writefds);

   if (exceptfds)
      if (FD_ISSET (i, exceptfds))
         {
         FD_ZERO (exceptfds);
         FD_SET (i,exceptfds);
         }
      else
         FD_ZERO (exceptfds);

   return 1;
   }

@implementation  EmacsListener:Listener
static NXRemoteMethod *remoteMethods = 0;

+ initialize
   {
   if ([self class] != [EmacsListener class])
      return self;

   if (!remoteMethods)
      {
      remoteMethods = (NXRemoteMethod *)malloc(4*sizeof(NXRemoteMethod));
      remoteMethods[0].key = @selector(openFile:onHost:atTrueLine:);
      remoteMethods[0].types = "cci";
      remoteMethods[1].key = @selector(openFile:onHost:fromTrueLine:to:);
      remoteMethods[1].types = "ccii";
      remoteMethods[2].key = @selector(connectTo:);
      remoteMethods[2].types = "s";
      remoteMethods[3].key = 0;
      }
   return self;
   }

-(void)connectTo:(port_t)port
   {
   ns_gdb_controller=[NXConnection connectToPort:[NXPort newFromMachPort:port]];

   [[ns_gdb_controller connectionForProxy] runFromAppKit];
   [ns_gdb_controller setProtocolForProxy:@protocol(GdbController)];
NX_DURING
   [ns_gdb_controller setClient:self];
NX_HANDLER
   ns_gdb_controller=nil;
NX_ENDHANDLER
   }

- (int)performRemoteMethod:(NXRemoteMethod *)method paramList:(NXParamValue *)paramList
   {
   switch (method - remoteMethods)
      {
    case 0:
      [self openFile:paramList[0].bval.p onHost:paramList[1].bval.p atTrueLine:paramList[2].ival];
      return -1;
    case 1:
      [self openFile:paramList[0].bval.p onHost:paramList[1].bval.p fromTrueLine:paramList[2].ival to:paramList[3].ival];
      return -1;
    case 2:
      [self connectTo:paramList[0].pval];
      return -1;
    default:
      return [super performRemoteMethod:method paramList:paramList];
      }
   }

- (NXRemoteMethod *)remoteMethodFor:(SEL)aSel
   {
   NXRemoteMethod *rm = NXRemoteMethodFromSel(aSel,remoteMethods);
   return rm ? rm : [super remoteMethodFor:aSel];
   }

-(oneway void)openFile:(char *)fileName onHost:(char *)host atTrueLine:(int)line
   {
   EmacsApp *t;
   if (t = NXResponsibleDelegate(self, @selector(openFile:onHost:atTrueLine:)))
      [t openFile:fileName onHost:host atTrueLine:line];
   }

-(oneway void)openFile:(char *)fileName onHost:(char *)host fromTrueLine:(int)startLine to:(int)endLine
   {
   EmacsApp *t;
   if (t = NXResponsibleDelegate(self, @selector(openFile:onHost:fromTrueLine:to:)))
      [t openFile:fileName onHost:host fromTrueLine:startLine to:endLine];
   }

-(oneway void)setInterruptHandler:handler
   {
   ns_gdb_interrupt_controller=handler;
   }

-(oneway void)setController:(id <GdbController>)anObject
   {
   ns_gdb_controller=anObject;
   }

-(oneway void)disconnect
   {
   ns_gdb_controller=nil;
   ns_gdb_interrupt_controller=nil;
   ns_gdb_status=Qnil;
   ns_gdb_frame=0;
   }

-(oneway void)frameChanged:(int)selectedFrame
   {
   ns_gdb_frame=selectedFrame;
   update_mode_lines++;
   }

-(oneway void)programIsRunning
   {
   ns_gdb_status=build_string("running");
   update_mode_lines++;
   }

-(oneway void)programStopped
   {
   ns_gdb_status=build_string("stopped");
   update_mode_lines++;
   }

-(oneway void)programIsDead
   {
   ns_gdb_status=build_string("dead");
   update_mode_lines++;
   }

-(oneway void)programIsLoaded
   {
   ns_gdb_status=build_string("loaded");
   update_mode_lines++;
   }

-(BOOL)isNewUser
   {
   return YES;
   }
@end

@implementation EmacsApp
- (int)runModalFor:theWindow
   {
   if (running)
      return [super runModalFor:theWindow];
   else
      {
      int ret;
      running++;
      ret=[super runModalFor:theWindow];
      running--;
      return ret;
      }
   }

- (int)runModalSession:(NXModalSession *)session
   {
   if (running)
      return [super runModalSession:session];
   else
      {
      int ret;
      running++;
      ret=[super runModalSession:session];
      running--;
      return ret;
      }
   }

-(int)openFile:(char *)fileName onHost:(char *)host atTrueLine:(int)line
   {
   return [self openFile:fileName onHost:host fromTrueLine:line to:line];
   }

-(int)openFile:(char *)fileName onHost:(char *)host fromTrueLine:(int)startLine to:(int)endLine;
   {
   struct frame *emacsframe=selected_frame;
   NXEvent *e=[NXApp currentEvent];

   if (eventsleft<=0) return NO;
   ns_input_file=append2(ns_input_file,build_string(fileName));

   if (startLine==endLine)
      ns_input_line=(startLine>=0) ? make_number(startLine) : Qnil;
   else
      ns_input_line=Fcons(make_number(startLine),make_number(endLine));

   events->kind=non_ascii_keystroke;
   events->modifiers=0;
   events->code=KEY_NS_OPEN_FILE_LINE;
   EV_TRAILER(e);
   return YES;
   }

-(int)app:sender openFile:(const char *) file type:(const char *) aType
   {
   struct frame *emacsframe=selected_frame;
   NXEvent *e=[NXApp currentEvent];

   if (eventsleft<=0) return NO;
   ns_input_file=append2(ns_input_file,build_string(file));
   events->kind=non_ascii_keystroke;
   events->modifiers=0;
   events->code=KEY_NS_OPEN_FILE;
   EV_TRAILER(e);
   return YES;
   }

-(int)app:sender openTempFile:(const char *)file type:(const char *) aType;
   {
   struct frame *emacsframe=selected_frame;
   NXEvent *e=[NXApp currentEvent];

   if (eventsleft<=0) return NO;
   ns_input_file=append2(ns_input_file,build_string(file));
   events->kind=non_ascii_keystroke;
   events->modifiers=0;
   events->code=KEY_NS_OPEN_TEMP_FILE;
   EV_TRAILER(e);
   return YES;
   }

-(BOOL) appAcceptsAnotherFile:sender
   {
   return YES;
   } /* -appAcceptsAnotherFile:*/


/* Set up our listener *before* we start up */
-appWillInit:sender
   {
   /* XXX Actually NXFilenamePboardType should be in there.  Unfortunately
      it is broken badly by some serious problems in the services code.  Disabling
      it breaks some services.  Unfortunately leaving it in there breaks a lot
      more at this time. XXX */
   
   const NXAtom send_types[]   = { NXAsciiPboardType, /* NXFilenamePboardType,
                                      NXTabularTextPboardType, */ 0 };
   const NXAtom return_types[] = { NXAsciiPboardType, /* NXFilenamePboardType,
                                      NXTabularTextPboardType, */ 0 };
   const NXAtom drag_types[] = { NXAsciiPboardType, NXTabularTextPboardType,
                                    NXFilenamePboardType, NXColorPboardType,
                                    NXFontPboardType, 0 };

   ns_send_types  = (void *)xmalloc(sizeof(send_types));
   bcopy(send_types,  ns_send_types,  sizeof(send_types));
   ns_return_types= (void *)xmalloc(sizeof(return_types));
   bcopy(return_types,ns_return_types,sizeof(return_types));
   ns_drag_types= (void *)xmalloc(sizeof(drag_types));
   bcopy(drag_types,ns_drag_types,sizeof(drag_types));

   emacsListener = [[EmacsListener alloc] init];
   [NXApp setAppListener:emacsListener];
   return self;
   }

/* Stop ourself from running as soon as we have finished initialization.  We
   will actually run in ns_select's call to NXGetOrPeekEvent ().  */
-appDidInit:sender
   {
   [self setJournalable:NO];
   [self setImportAlpha:YES];   
   [self stop:self];
   return self;
   }

/* The function ns_select should remove all these events.  */
-applicationDefined:(NXEvent *) e
   {
   last_appdefined_event = *e;
   [self stop: self];
   return self;
   }

-app:sender powerOffIn:(int)ms andSave:(int)aFlag
   {
   /* XXX This does not work yet XXX */
   struct frame *emacsframe=selected_frame;
   NXEvent *e=[NXApp currentEvent];

   if (eventsleft<=0) return self;
   events->kind=non_ascii_keystroke;
   events->modifiers=0;
   events->code=KEY_NS_POWER_OFF;
   EV_TRAILER(e);
   return self;
   }

- menuDown:sender
   {
   if (selected_frame && selected_frame->display.ns->view)
      return [selected_frame->display.ns->view menuDown:sender];
   return nil;
   }

#if 0
-pasteboardChangedOwner:sender
   {
   struct frame *emacsframe=selected_frame;
   NXEvent *e=[NXApp currentEvent];

   if (eventsleft<=0) return self;
   events->kind=selection_clear_event;
   events->modifiers=0;
   events->code=0;
   events->x=(int)sender;
   events->y=0;
#if 1
   EV_TRAILER(e);
#else
   ns_handle_selection_clear(events);
#endif
   return self;
   }

-pasteboard:sender provideData:(NXAtom)type
   {
   struct frame *emacsframe=selected_frame;
   NXEvent *e=[NXApp currentEvent];

   if (eventsleft<=0) return self;
   events->kind=selection_request_event;
   events->modifiers=0;
   events->code=0;
   events->x=(int)sender;
   events->y=(int)type;
#if 1
   EV_TRAILER(e);
#else
   ns_handle_selection_request(events);
#endif
   return self;
   }
#endif
@end

@implementation EmacsView
- changeFont:sender
   {
   NXEvent *e=[NXApp currentEvent];
   struct ns_face *face=emacsframe->display.ns->face;
   id newFont;
   float size;

   if (eventsleft<=0) return self;

   if (newFont=[sender convertFont:face->font])
      {
      events->kind=non_ascii_keystroke;
      events->modifiers=0;
      events->code=KEY_NS_CHANGE_FONT;

      size=[newFont pointSize];
      if (size==rint(size))
         ns_input_fontsize=make_number((int)rint(size));
      else
         ns_input_fontsize=make_float(size);
      ns_input_font=build_string([newFont name]);
      EV_TRAILER(e);
      }
   return self;
   }

- (BOOL)acceptsFirstResponder
   {
   return YES;
   }

- becomeFirstResponder
   {
   return [super becomeFirstResponder];
   }

- resignFirstResponder
   {
   return [super resignFirstResponder];
   }

- resetCursorRects
   {
   NXRect visible;
   if ([self getVisibleRect:&visible])
      [self addCursorRect:&visible cursor:NXIBeam];
   return self;
   }

-keyDown:(NXEvent *) theEvent
   {
   int code;
   enum event_kind kind;
   int flags;

   if (eventsleft <= 0)
      return self;

   PSobscurecursor();

   code=theEvent->data.key.charCode;
   kind=non_ascii_keystroke;
   flags=theEvent->flags;

   events->modifiers=0;
   if (flags & NX_HELPMASK)
      {
      events->modifiers |= hyper_modifier;
      }
   if (flags & NX_COMMANDMASK)
      {
      events->modifiers |= super_modifier;
      }
   if (!NILP(ns_alternate_is_meta) &&
       ((EQ(ns_alternate_is_meta,Qt) && (flags & NX_ALTERNATEMASK))||
        (EQ(ns_alternate_is_meta,Qleft) && (flags & NX_ALTERNATEMASK) && (flags & NX_NEXTLALTKEYMASK))||
        (EQ(ns_alternate_is_meta,Qright) && (flags & NX_ALTERNATEMASK) && (flags & NX_NEXTRALTKEYMASK))))
      {
      events->modifiers |= meta_modifier;
      flags&=~(NX_ALTERNATEMASK|NX_NEXTLALTKEYMASK|NX_NEXTRALTKEYMASK);
      code = char_from_key(theEvent->data.key.keyCode,flags);
      }
   if ((flags & NX_CONTROLMASK) &&
       (code == char_from_key(theEvent->data.key.keyCode,flags&~NX_CONTROLMASK)))
      {
      events->modifiers |= ctrl_modifier;
      flags&=~NX_CONTROLMASK;
      }
   if ((flags & NX_SHIFTMASK) &&
       (code == char_from_key(theEvent->data.key.keyCode,flags&~NX_SHIFTMASK)))
      {
      events->modifiers |= shift_modifier;
      flags&=~NX_SHIFTMASK;
      }

   if ((flags & NX_NUMERICPADMASK) &&
       (char_is_type(theEvent->data.key.keyCode,NX_NUMERICPADMASK)))
      {
      code |= (1<<28)|(2<<16);
      }
   else if ((theEvent->data.key.charSet==NX_DINGBATSSET) ||
            (theEvent->data.key.charSet==254))
      {
      code |= (1<<28)|(1<<16);
      }
   else
      {
      if ((code<0x20)&&((theEvent->flags&NX_CONTROLMASK)==0))
         code |= (1<<28)|(3<<16);
      else if (code==0x7f)
         code |= (1<<28)|(3<<16);
      else
         kind=ascii_keystroke;
      }

   if (kind==ascii_keystroke && !NILP(ns_iso_latin) && (code>=0) && (code<256))
      code=ns2isomap[code];
   events->kind=kind;
   events->code=code;
   EV_TRAILER (theEvent);
   return self;
   }

/* This is what happens when the user presses the mouse button.  */
-mouseDown:(NXEvent *) theEvent
   {
   int x, y;
   NXPoint position;

   if (eventsleft <= 0) return self;

   last_mouse_frame=emacsframe;
   switch (EV_UDMODIFIERS(theEvent))
      {
    case up_modifier:
      Vmouse_depressed=Qnil;
      break;
    case down_modifier:
      Vmouse_depressed=Qt;
      break;
      }

   position=theEvent->location;
   [self convertPoint:&position fromView:nil];
   events->kind = mouse_click;
   events->code = EV_BUTTON(theEvent);
   XSET (events->x, Lisp_Int, (int)rint(position.x));
   XSET (events->y, Lisp_Int, (int)rint(position.y));
   events->modifiers = EV_MODIFIERS (theEvent) | EV_UDMODIFIERS (theEvent);
   EV_TRAILER (theEvent);
   return self;
   }

/* This is what happens when the user releases the mouse button.  */
-mouseUp:(NXEvent *) theEvent
   {
   return [self mouseDown:theEvent];
   }

-rightMouseDown:(NXEvent *) theEvent
   {
   return [self mouseDown:theEvent];
   }

-rightMouseUp:(NXEvent *) theEvent
   {
   return [self mouseDown:theEvent];
   }

/* Tell emacs the mouse has moved.  */
-mouseMoved:(NXEvent *) e
   {
   NXPoint p=e->location;

   [self convertPoint:&p fromView:nil];

   /* Has the mouse moved off the glyph it was on at the last sighting?  */
   if ((emacsframe!=last_mouse_frame)||
       (p.x<last_mouse_glyph.origin.x)||
       (p.x>=(last_mouse_glyph.origin.x+last_mouse_glyph.size.width))||
       (p.y<last_mouse_glyph.origin.y)||
       (p.y>=(last_mouse_glyph.origin.y+last_mouse_glyph.size.height)))
      {
      mouse_moved = 1;
      last_mouse_movement_time = EV_TIMESTAMP (e);
      last_mouse_frame = emacsframe;
      note_mouse_highlight (emacsframe, p.x, p.y);
      if (send_appdefined) ns_send_appdefined (-1);
      }
   return self;
   }

-mouseDragged:(NXEvent *) e
   {
   return [self mouseMoved:e];
   }

-rightMouseDragged:(NXEvent *) e
   {
   return [self mouseMoved:e];
   }

-windowWillClose:sender
   {
   NXEvent *e=[NXApp currentEvent];

   if (ns_window_num<=1) return nil;
   if (eventsleft <= 0) return nil;
   events->kind=delete_window_event;
   events->modifiers=0;
   events->code=0;
   EV_TRAILER (e);
   /* Don't close this window, let this be done from lisp code.  */
   return nil;
   }

- windowWillResize:sender toSize:(NXSize *)frameSize
   {
   NXRect r;
   char *size;
   int rows,cols;
   int vbextra=FRAME_HAS_VERTICAL_SCROLL_BARS(emacsframe) ?
               rint(VERTICAL_SCROLL_BAR_WIDTH*emacsframe->display.ns->face->width):
               0;

   cols=rint((frameSize->width-
              emacsframe->display.ns->border_width-
              2*emacsframe->display.ns->internal_border_width-
              vbextra)/emacsframe->display.ns->face->width);
   if (cols<MINWIDTH) cols=MINWIDTH;
   frameSize->width=(cols*emacsframe->display.ns->face->width+
                     emacsframe->display.ns->border_width+
                     2*emacsframe->display.ns->internal_border_width+
                     vbextra);

   rows=rint((frameSize->height-
              emacsframe->display.ns->border_height-
              2*emacsframe->display.ns->internal_border_width)/
             emacsframe->display.ns->line_height);
   if (rows<MINHEIGHT) rows=MINHEIGHT;
   frameSize->height=(rows*emacsframe->display.ns->line_height+
                      emacsframe->display.ns->border_height+
                      2*emacsframe->display.ns->internal_border_width);

   [[self window] getFrame:&r];
   if (r.size.height == frameSize->height && r.size.width == frameSize->width)
      {
      if (old_title!=0)
         {
         [[self window] setTitle:old_title];
         xfree(old_title);
         old_title=0;
         }
      }
   else
      {
      if (old_title==0)
         {
         const char *t=[[self window] title];
         old_title=(char *) xmalloc(strlen(t)+1);
         strcpy(old_title,t);
         }
      size=alloca(strlen(old_title)+20);
      sprintf(size,"%s (%dx%d)",old_title,cols,rows);
      [[self window] setTitle:size];
      }
   return self;
   }

- windowDidResize:sender
   {
   if (old_title!=0)
      {
      [[self window] setTitle:old_title];
      xfree(old_title);
      old_title=0;
      }
   return self;
   }

- windowDidBecomeKey:sender
   {
   NXEvent *e=[NXApp currentEvent];
   int val;

   ns_highlight_frame=emacsframe;
   if ((val=ns_lisp_to_cursor_type(get_frame_param(emacsframe, Qcursor_type)))>=0)
      {
      emacsframe->display.ns->desired_cursor=val;
      ns_dumpglyphs(emacsframe,0,0,0,0,-1);
      ns_dumpcursor(emacsframe,emacsframe->cursor_x,emacsframe->cursor_y);
      }

   if (eventsleft <= 0) return nil;
   events->kind=frame_switch_event;
   events->modifiers=0;
   events->code=0;
   EV_TRAILER (e);
   return self;
   }

- windowDidResignKey:sender
   {
   emacsframe->display.ns->desired_cursor = hollow_box;
   ns_dumpglyphs(emacsframe,0,0,0,0,-1);
   ns_dumpcursor(emacsframe,emacsframe->cursor_x,emacsframe->cursor_y);
   if (ns_highlight_frame==emacsframe)
      ns_highlight_frame=0;
   return self;
   }

- windowWillMiniaturize:sender toMiniwindow:miniwindow
   {
   [window setMiniwindowImage:emacsframe->display.ns->miniimage];
   return self;
   }

- initFrameFromEmacs:(struct frame *)f
   {
   NXRect r,wr;
   Lisp_Object tem;
   id win,oview;
   int vbextra=FRAME_HAS_VERTICAL_SCROLL_BARS(f) ?
               rint(VERTICAL_SCROLL_BAR_WIDTH*f->display.ns->face->width) :0;

   r.origin.x   =f->display.ns->internal_border_width;
   r.origin.y   =f->display.ns->internal_border_width;
   r.size.width =rint(f->display.ns->face->width*f->width);
   r.size.height=rint(f->display.ns->line_height*f->height);
   [self initFrame:&r];

   f->display.ns->view=self;
   emacsframe=f;
   old_title=0;

   r.origin.x=r.origin.y=0;
   r.size.width +=2*f->display.ns->internal_border_width+vbextra;
   r.size.height+=2*f->display.ns->internal_border_width;

   win=[[Window alloc] initContent:&r
                       style:NX_RESIZEBARSTYLE
                       backing:(NILP(get_frame_param(f,Qbuffered)) ?
                                NX_RETAINED :NX_BUFFERED)
                       buttonMask:NX_MINIATURIZEBUTTONMASK|
                                  NX_CLOSEBUTTONMASK
                                  /* (ns_window_num>0 ? NX_CLOSEBUTTONMASK : 0) */
                       defer:NO];
   [win getFrame:&wr];
   [win setTrackingRect:&wr inside:NO owner:self tag:0 left:NO right:NO];

   f->display.ns->border_width=wr.size.width-r.size.width;
   f->display.ns->border_height=wr.size.height-r.size.height;
   [win addToEventMask:(NX_LMOUSEDOWNMASK|NX_LMOUSEUPMASK|
                        NX_RMOUSEDOWNMASK|NX_RMOUSEUPMASK|
                        NX_MOUSEENTERED|NX_MOUSEEXITED|
                        NX_LMOUSEDRAGGEDMASK|NX_RMOUSEDRAGGEDMASK|
                        NX_KEYDOWNMASK)];
   [win setFreeWhenClosed:YES];
   [win setDelegate:self];
   [win useOptimizedDrawing:YES];

   [[win contentView] addSubview:self];
   [[self superview] setAutoresizeSubviews:YES];

   if (ns_drag_types)
      {
      int i;
      for(i=0;ns_drag_types[i];i++);
      [self registerForDraggedTypes:ns_drag_types count:i];
      }

   tem=f->name;
   if (!NILP(tem)) [win setTitle:XSTRING(tem)->data];

       {
       const NXScreen *screen=[win screen];

       if (screen!=0)
          [win moveTopLeftTo:BOUND(-SCREENMAX,f->display.ns->left,SCREENMAX)
                            :BOUND(-SCREENMAX,screen->screenBounds.size.height
                                   -f->display.ns->top,SCREENMAX)];
       }

   [win makeFirstResponder:self];
   [win setBackgroundColor:emacsframe->display.ns->face->background_color];
   [win setBackgroundGray:NXGrayComponent(emacsframe->display.ns->face->background_color)];

   [self allocateGState];
   [self setAutosizing:NX_WIDTHSIZABLE|NX_HEIGHTSIZABLE];
   [self setOpaque:YES];
   [self setAutodisplay:YES];
   [self setFlipped:YES];
   [self setClipping:NO];

   ns_adjust_size(emacsframe);

   [win reenableDisplay];
   [win display];
   [win makeKeyAndOrderFront:NXApp];
   ns_window_num++;

#if 0
      {
      Lisp_Object rest;
      struct frame *f;
      
      for (rest=Vframe_list; CONSP(rest); rest=XCONS(rest)->cdr)
         if (FRAME_NS_P(f=XFRAME(XCONS(rest)->car)))
            [[f->display.ns->view window] setCloseButton:NXApp to:(ns_window_num>1)];
      }
#endif
   return self;
   }

- windowDidMove:sender
   {
   id win=[self window];
   NXRect r;
   const NXScreen *screen;

   if (!emacsframe->display.ns) return self;
   [win getFrame:&r];
   screen=[win screen];
   if (screen!=0)
      {
      emacsframe->display.ns->left=r.origin.x;
      emacsframe->display.ns->top=screen->screenBounds.size.height-
                                   (r.origin.y+r.size.height);
      }
   /* Terminate the event loop.  */
   if (send_appdefined) ns_send_appdefined (-1);
   return self;
   }

- windowDidDeminiaturize:sender
   {
   if (!emacsframe->display.ns) return self;
   emacsframe->async_visible   = 1;
   emacsframe->async_iconified = 0;
   /* Terminate the event loop.  */
   if (send_appdefined) ns_send_appdefined (-1);
   return self;
   }

- windowDidExpose:sender
   {
   if (!emacsframe->display.ns) return self;
   emacsframe->async_visible = 1;
   SET_FRAME_GARBAGED (emacsframe);
   /* Terminate the event loop.  */
   if (send_appdefined) ns_send_appdefined (-1);
   return self;
   }

- windowDidMiniaturize:sender
   {
   if (!emacsframe->display.ns) return self;
   emacsframe->async_iconified = 1;
   /* Terminate the event loop.  */
   if (send_appdefined) ns_send_appdefined (-1);
   return self;
   }

- mouseEntered:(NXEvent *)theEvent
   {
   if (emacsframe && emacsframe->auto_raise)
      {
      [[self window] makeKeyAndOrderFront:NXApp];
      }
   return self;
   }

- mouseExited:(NXEvent *)theEvent
   {
   if (emacsframe && emacsframe->auto_lower)
      {
      [[self window] orderBack:NXApp]; /* XXX unfocus ! */
      }
   return self;
   }

- menuDown:sender
   {
   static id list=nil;
   NXEvent *theEvent;
   NXPoint p;
   int x,y;
   id cell;

   if (list!=nil) [list empty];
   list=[sender getSelectedCells:list];
   if ([list count]<1) return self;

   if (eventsleft<=0) return self;
   theEvent=[NXApp currentEvent];
   ns_menu_path=Qnil;
   for(cell=[list objectAt:0];cell!=nil;cell=[cell supercell])
      ns_menu_path = Fcons([cell value],ns_menu_path);
   [[self window] getMouseLocation:&p];
   [self convertPoint:&p fromView:nil];

   events->kind = mouse_click;
   XSET (events->code, Lisp_Int, 0);
   events->modifiers=EV_MODIFIERS(theEvent)|down_modifier;
   XSET (events->x, Lisp_Int, (int)rint(p.x));
   XSET (events->y, Lisp_Int, -1);
   EV_TRAILER(theEvent);
   return self;
   }

- drawSelf:(const NXRect *)rects :(int)rectCount
   {
   if (!emacsframe->display.ns) return nil;

   emacsframe->async_visible = 1;
   emacsframe->async_iconified = 0;
   ns_adjust_size(emacsframe);

   /* Terminate the event loop.  */
   if (send_appdefined) ns_send_appdefined (-1);
   return self;
   }

/* NXDraggingDestination protocol methods.  Actually this is not really a
   protocol, but a category of Object.  O well...  */

-(NXDragOperation) draggingEntered:(id <NXDraggingInfo>) sender
   {
   return NX_DragOperationGeneric;
   }

-(BOOL)performDragOperation:(id <NXDraggingInfo>) sender
   {
   id pb;
   int i,x,y;
   NXAtom type;
   NXEvent *theEvent=[NXApp currentEvent];
   NXPoint position;

   position=theEvent->location;
   [self convertPoint:&position fromView:nil];
   ns_pixel_to_glyph_coords (emacsframe, (int)rint(position.x),
                             (int)rint(position.y), &x, &y, 0, 1);

   pb = [sender draggingPasteboard];
   for (i=0;ns_drag_types[i];i++);
   type = [pb findAvailableTypeFrom:ns_drag_types num:i];
   if (type==0)
      {
      return NO;
      }
   else if (type==NXFilenamePboardType)
      {
      int l;
      char *data;
      char *d,*e;
      if (![pb readType:type data:&data length:&l])
         return NO;
      for(d=data;(*d) && (eventsleft>0);d = *e ? e+1 : e)
         {
         for(e=d;*e!='\0' && *e!='\t';e++);
         events->kind=non_ascii_keystroke;
         events->code=KEY_NS_DRAG_FILE;
         XSET(events->x, Lisp_Int, x);
         XSET(events->y, Lisp_Int, y);
         ns_input_file=append2(ns_input_file,make_string(d,e-d));
         events->modifiers=EV_MODIFIERS(theEvent);
         EV_TRAILER(theEvent);
         }
      [pb deallocatePasteboardData:data length:l];
      return YES;
      }
   else if (type==NXAsciiPboardType || type==NXTabularTextPboardType)
      {
      int l;
      char *data;
      char *d,*e;
      if (![pb readType:type data:&data length:&l])
         return NO;
      for(d=data;(*d) && (eventsleft>0);d = *e ? e+1 : e)
         {
         for(e=d;*e!='\0' && *e!='\t';e++);
         events->kind=non_ascii_keystroke;
         events->code=KEY_NS_DRAG_ASCII;
         XSET(events->x, Lisp_Int, x);
         XSET(events->y, Lisp_Int, y);
         ns_input_ascii=make_string(d,e-d);
         events->modifiers=EV_MODIFIERS(theEvent);
         EV_TRAILER(theEvent);
         }
      [pb deallocatePasteboardData:data length:l];
      return YES;
      }
   else if (type==NXColorPboardType)
      {
      NXColor c=NXReadColorFromPasteboard(pb);
      events->kind=non_ascii_keystroke;
      events->code=KEY_NS_DRAG_COLOR;
      XSET(events->x, Lisp_Int, x);
      XSET(events->y, Lisp_Int, y);
      ns_input_color=ns_color_to_lisp(c);
      events->modifiers=EV_MODIFIERS(theEvent);
      EV_TRAILER(theEvent);
      return YES;
      }
   else
      {
      error("Invalid data type in dragging pasteboard.");
      return NO;
      }
   }

- validRequestorForSendType:(NXAtom)typeSent andReturnType:(NXAtom)typeReturned
   {
   int i;

   for (i=0;ns_send_types[i] && ns_send_types[i]!=typeSent;i++);
   for (i=0;ns_return_types[i] && ns_return_types[i]!=typeReturned;i++);
   if (typeSent!=ns_send_types[i] || typeReturned!=ns_return_types[i])
      return [super validRequestorForSendType:typeSent andReturnType:typeReturned];

   return self;
   }
@end

@implementation EmacsScroller
- initFrame:(const NXRect *)r window:(Lisp_Object)nwin
   {
   struct frame *f;
   id view;

   [super initFrame:r];
   [self setTarget:self];
   [self setAction:@selector(scrollerMoved:)];
   [self setArrowsPosition:NX_SCROLLARROWSNONE];
   [self setAutosizing:NX_MINXMARGINSIZABLE|NX_HEIGHTSIZABLE];
   [self setAutodisplay:YES];
   [self setContinuous:YES];
   [self setEnabled:YES];

   win=nwin;
   staticpro(&win);
   condemned=NO;

   f=XFRAME(XWINDOW (win)->frame);
   if (FRAME_LIVE_P(f))
      {
      view=f->display.ns->view;
      [[[view window] contentView] addSubview:self];
      }

   [self setFrame:r];
   return self;
   }

- setFrame:(const NXRect *)r
   {
   struct frame *f=XFRAME(XWINDOW (win)->frame);
   id view=FRAME_LIVE_P(f) ? f->display.ns->view : 0;
   NXRect s,t;

   [self getFrame:&s];
   if (r==0)
      {
      t.origin=s.origin;
      t.size.width=t.size.height=0;
      }
   else
      {
      t=*r;
      }
   [super setFrame:&t];
   if (NXEqualRect(&t,&s) || !view) return self;
   ns_reset_clip(view);

      {
      int i;
      id sview=[self superview];
      id subs=[sview subviews];
      id v;

      [sview lockFocus];
      NXSetColor(f->display.ns->face->background_color);
      NXRectFill(&s);
      [sview unlockFocus];
      [self display];

      for (i=[subs count]-1; i>=0; i--)
         {
         v=[subs objectAt:i];
         if (v==view || v==self) continue;
         [v getFrame:&t];
         if (!NXIntersectsRect(&t,&s)) continue;
         [v display];
         }
      }

   return self;
   }

- free
   {
   [self setFrame:0];
   if (!NILP(win)) XWINDOW (win)->vertical_scroll_bar = Qnil;
   staticunpro(&win);
   return [super free];
   }

- scrollerMoved:sender
   {
   NXEvent *e=[NXApp currentEvent];
   if (eventsleft <= 0) return nil;
   events->kind = scroller_event;
   if (curValue>0.999)
      {
      XSET (events->x, Lisp_Int, SHRT_MAX);
      XSET (events->y, Lisp_Int, SHRT_MAX);
      }
   else
      {
      XSET (events->x, Lisp_Int, SHRT_MAX*(curValue*(1.0-perCent)));
      XSET (events->y, Lisp_Int, SHRT_MAX);
      }
   events->code=0;
   events->modifiers=EV_MODIFIERS(e);
   events->frame_or_window=win;
   events->timestamp=EV_TIMESTAMP (e);
   events++;
   eventsleft--;
   if (send_appdefined) ns_send_appdefined (-1);
   return self;
   }

- setPosition:(int) position portion:(int) portion whole:(int) whole
   {
   if (portion>=whole)
      [self setFloatValue:0.0:1.0];
   else
      {
      double start=((double)position)/whole;
      double end=((double)position+portion)/whole;
      [self setFloatValue:start/(1.0-(end-start)):end-start];
      }
   }

- condemn
   {
   condemned=YES;
   return self;
   }

- reprieve
   {
   condemned=NO;
   return self;
   }

- judge
   {
   return condemned ? [self free] : self;
   }
@end

/* Stolen from John C. Randolph */
@implementation Window (windowButtons)
- toggleClose:sender
   {
   wFlags.buttonMask ^= NX_CLOSEBUTTONMASK;
   return [self updateBorder];
   }

- toggleMiniaturize:sender
   {
   wFlags.buttonMask ^= NX_MINIATURIZEBUTTONMASK;
   return [self updateBorder];
   }

- setCloseButton:sender to:(BOOL)bool
   {
   if (!(wFlags.buttonMask & NX_CLOSEBUTTONMASK)==bool)
      return [self toggleClose:sender];
   return self;
   }

- setMiniaturizeButton:sender to:(BOOL)bool
   {
   if (!(wFlags.buttonMask & NX_MINIATURIZEBUTTONMASK)==bool)
      return [self toggleMiniaturize:sender];
   return self;
   }

- showCloseButton:sender
   {
   if (!(wFlags.buttonMask & NX_CLOSEBUTTONMASK))
      return [self toggleClose:sender];
   return self;
   }

- showMiniaturizeButton:sender
   {
   if (!(wFlags.buttonMask & NX_MINIATURIZEBUTTONMASK))
      return [self toggleMiniaturize:sender];
   return self;
   }

- hideCloseButton:sender
   {
   if (wFlags.buttonMask & NX_CLOSEBUTTONMASK)
      return [self toggleClose:sender];
   return self;
   }

- hideMiniaturizeButton:sender
   {
   if (wFlags.buttonMask & NX_MINIATURIZEBUTTONMASK)
      return [self toggleMiniaturize:sender];
   return self;
   }

- updateBorder
   {
   [_borderView _setMask:wFlags.buttonMask];
   [_borderView tile];
   [_borderView display];
   return [self display];
   }
@end
