ftp.nice.ch/pub/emacs-for-ns/emacs-4.2beta7.tar.gz#/emacs-4.2beta7/src/nsterm.m

This is nsterm.m in view mode; [Download] [Up]

/* 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 "keyboard.h"
#include "multi-frame.h"
#include "paths.h"

#include "nswraps.h"

#define min(a,b) ((a)<(b) ? (a) : (b))
#define max(a,b) ((a)>(b) ? (a) : (b))

#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)

#define FRAME_TRACKNUM 0
#define WINDOW_TRACKNUM 1
#define MOUSE_FACE_TRACKNUM 2

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;

/* This is a chain of structures for all the NS displays currently in use.  */
struct ns_display_info *ns_display_list;

Lisp_Object ns_display_name_list;

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(struct frame *f);
static void ns_adjust_size(struct frame *f);
static void redraw_previous_char ();
static void redraw_following_char ();
static void note_mouse_movement (struct frame *frame, int x, int y);
static void note_mouse_highlight (struct frame *frame, int x, int y);
static int fast_find_position ();
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;
static NXRect mouse_face_mouse_rect;
static int mouse_face_mouse_row;
static id mouse_face_tracked_view;

/* Number of event we should fake in `getNextEvent:waitFor:threshold:'. */
BOOL fake_event_p;
NXEvent fake_event;

/* 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)    \
  { \
  XSETFRAME (events->frame_or_window, emacsframe); \
  events->timestamp = EV_TIMESTAMP (e); \
  events++; \
  eventsleft--; \
  if (send_appdefined) ns_send_appdefined (-1); \
  }

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;

// enable this to make the tracking rects used for mouse-face
// highlighting visible
#if 0
NXRect trackrec;
#define TRACKREC(r, v) {NXRect s=r; [v lockFocus]; NXHighlightRect(&s); \
  s.size.width-=2; s.size.height-=2; s.origin.x+=1; s.origin.y+=1; \
  NXHighlightRect(&s); trackrec=r; [v unlockFocus]; \
  [[v window] flushWindow];}
#else
#define TRACKREC(r, v)
#endif

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->output_data.ns->face->width;
   r->origin.y=y1*f->output_data.ns->line_height;
   r->size.width=(x2-x1+1)*f->output_data.ns->face->width;
   r->size.height=(y2-y1+1)*f->output_data.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->output_data.ns->face->width <=0)
            ? 0 : floor(((float)px) / f->output_data.ns->face->width);
   *y=(f->output_data.ns->line_height<=0)
            ? 0 : floor(((float)py) / f->output_data.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->output_data.ns->face->width);
   *pix_y=(int)rint(y*f->output_data.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->output_data.ns->face->width<=0)
      {
      *x=0;
      *cols=f->width;
      }
   else if (maximize)
      {
      *x=floor(r->origin.x/f->output_data.ns->face->width+0.001);
      *cols=ceil((r->origin.x+r->size.width)/f->output_data.ns->face->width-0.001);
      }
   else
      {
      *x=ceil(r->origin.x/f->output_data.ns->face->width-0.001);
      *cols=floor((r->origin.x+r->size.width)/f->output_data.ns->face->width+0.001);
      }

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

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

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

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

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

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

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

void ns_make_frame_invisible (struct frame *f)
   {
   id view=f->output_data.ns->view;
   check_ns();
   if (f->iconified || f->async_iconified)
     [[[view window] counterpart] orderOut:NXApp];
   [[view window] orderOut:NXApp];
   }

void ns_iconify_frame (struct frame *f)
   {
   id view=f->output_data.ns->view;
   check_ns();
   if ([[view window] windowNum] <= 0)
     {
       // the window is still deferred.  Make it very small, bring it
       // on screen and order it out.
       NXRect s = {100, 100, 0, 0};
       NXRect t;
       [[view window] getFrame:&t];
       [[view window] placeWindow:&s];
       [[view window] orderBack:NXApp];
       [[view window] orderOut:NXApp];
       [[view window] placeWindow:&t];
     }
   [[view window] miniaturize:NXApp];
   }

void ns_destroy_window (struct frame *f)
   {
   id view=f->output_data.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;
      }
   if (view == mouse_face_tracked_view)
     mouse_face_tracked_view = nil;
   xfree (f->output_data.ns->face);
   xfree (f->output_data.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->output_data.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->output_data.ns->view;
      int i;
      struct ns_face *face;

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

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

      NXSetColor(face->background_color);
      if (! face->stipple)
        {
          NXRectFillList(r,no);
        }
      else
        {
          NXPoint p;
          NXRect s;
          int minx = NX_X (&r[0]);
          int miny = NX_Y (&r[0]);
          int maxx = NX_MAXX (&r[0]);
          int maxy = NX_MAXY (&r[0]);
          id stipple = [face->stipple stippleRep];

          for (i = 1; i < no; i++)
            if (l[i] > 0)
              {
                int t;
                if ((t = NX_X (&r[i])) < minx) minx = t;
                if ((t = NX_Y (&r[i])) < miny) miny = t;
                if ((t = NX_MAXX (&r[i])) > maxx) maxx = t;
                if ((t = NX_MAXY (&r[i])) > maxy) maxy = t;
              }

          NXSetRect (&s, minx, miny, maxx - minx, maxy - miny);
          
          miny += face->pixmap_h;  // view is flipped
          maxy += face->pixmap_h;
          minx -= minx % face->pixmap_w;  // align with previous stipples
          miny -= miny % face->pixmap_h;

          NXRectClipList (r, no);
          if ([[stipple bestRepresentation] hasAlpha])
            {
              NXRectFill (&s);
              for (p.y = miny; p.y <= maxy; p.y += face->pixmap_h)
                for (p.x = minx; p.x <= maxx; p.x += face->pixmap_w)
                  [stipple composite:NX_SOVER toPoint:&p];
            }
          else
            {
             for (p.y = miny; p.y <= maxy; p.y += face->pixmap_h)
                for (p.x = minx; p.x <= maxx; p.x += face->pixmap_w)
                  [stipple composite:NX_COPY toPoint:&p];
            } 
          PSinitclip ();
        }
      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->output_data.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->output_data.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++;   // FIXME/cl was *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->output_data.ns->view;

   if (f->phys_cursor_x==nx && f->phys_cursor_y == ny &&
       f->output_data.ns->current_cursor==f->output_data.ns->desired_cursor &&
       NXEqualColor(f->output_data.ns->current_cursor_color,
                    f->output_data.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->output_data.ns->current_cursor_color,NX_COLORCLEAR))
         {
         switch(f->output_data.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->output_data.ns->computed_faces[FAST_GLYPH_FACE(f->phys_cursor_glyph)];
         unsigned char c=FAST_GLYPH_CHAR(f->phys_cursor_glyph);

         NXSetColor(face->background_color);
         if (! face->stipple)
           {
             NXRectFill(&r);
           }
         else
           {
             NXPoint p;
             int minx = NX_X (&r);
             int miny = NX_Y (&r) + face->pixmap_h;
             int maxx = NX_MAXX (&r);
             int maxy = NX_MAXY (&r) + face->pixmap_h;
             id stipple = [face->stipple stippleRep];

             minx -= minx % face->pixmap_w;  // align with previous stipples
             miny -= miny % face->pixmap_h;

             NXRectClip (&r);
             // unfortunately we have to stipple here because it's not
             // guaranteed, that the cursor is not on an edge
             if ([[stipple bestRepresentation] hasAlpha])
               {
                 NXRectFill (&r);
                 for (p.y = miny; p.y <= maxy; p.y += face->pixmap_h)
                   for (p.x = minx; p.x <= maxx; p.x += face->pixmap_w)
                     [stipple composite:NX_SOVER toPoint:&p];
               }
             else
               {
                 for (p.y = miny; p.y <= maxy; p.y += face->pixmap_h)
                   for (p.x = minx; p.x <= maxx; p.x += face->pixmap_w)
                     [stipple composite:NX_COPY toPoint:&p];
               }
             PSinitclip ();
           }
         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->output_data.ns->line_height
                         -face->descender,&c,1);
         if (face->underline)
            {
            PSsetlinewidth(face->underwidth);
            nswrap_moveline(r.origin.x,r.origin.y+f->output_data.ns->line_height
                            -face->underpos,r.size.width-1);
            PSstroke();
            }
         }
      }

   f->phys_cursor_x = nx;
   f->phys_cursor_y = ny;
   f->output_data.ns->current_cursor = f->output_data.ns->desired_cursor;
   f->output_data.ns->current_cursor_color = f->output_data.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->output_data.ns->current_cursor_color,NX_COLORCLEAR))
         {
         switch(f->output_data.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->output_data.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->output_data.ns->current_cursor_color);
         switch(f->output_data.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->output_data.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->output_data.ns->line_height
                         -face->descender,&c,1);
         if (face->underline)
            {
            PSsetlinewidth(face->underwidth);
            nswrap_moveline(r.origin.x,r.origin.y+f->output_data.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->output_data.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->output_data.ns->view;
   f->output_data.ns->left=xoff;
   f->output_data.ns->top=yoff;
   if (view != nil && (screen=[[view window] screen])!=0)
      [[view window]
          moveTopLeftTo:SCREENMAXBOUND(f->output_data.ns->left)
          :SCREENMAXBOUND(screen->screenBounds.size.height-
                          f->output_data.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];
   }

void ns_set_mouse_tracking (id view)
{
  NXRect r;
  if (mouse_face_tracked_view && mouse_face_tracked_view != view)
    {
      TRACKREC (trackrec, mouse_face_tracked_view);
      [[mouse_face_tracked_view window]
          discardTrackingRect:MOUSE_FACE_TRACKNUM];
    }
  r = mouse_face_mouse_rect;
  TRACKREC (r, view);
  [view convertRect:&r toView:nil];
  [[view window] setTrackingRect:&r inside:YES
                 owner:view tag:MOUSE_FACE_TRACKNUM left:NO
                 right:NO];
  mouse_face_tracked_view = view;
}

void ns_remove_mouse_tracking ()
{
  if (mouse_face_tracked_view)
    {
      TRACKREC (trackrec, mouse_face_tracked_view);
      [[mouse_face_tracked_view window]
          discardTrackingRect:MOUSE_FACE_TRACKNUM];
      mouse_face_tracked_view = nil;
    }
}

ns_adjust_tracking_rects (struct frame *f)
{
  NXRect r;
  id view=f->output_data.ns->view;
  id win=[view window];

  if (view == mouse_face_tracked_view)
    ns_remove_mouse_tracking ();

  [view getBounds:&r];
  [view convertRect:&r toView:nil];
  // set inside to NO: if we are inside, we'll immediately get an
  // event and everything will be ok
  [win setTrackingRect:&r inside:NO owner:view tag:WINDOW_TRACKNUM
       left:NO right:NO];

  [win getFrame:&r];
  r.origin.x=r.origin.y=0;
  [win setTrackingRect:&r inside:NO owner:view tag:FRAME_TRACKNUM
       left:NO right:NO];
}

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

   [win invalidateCursorRectsForView:view];

   // don't setup tracking rects as long as the window is deferred
   if ([win windowNum] > 0)
     ns_adjust_tracking_rects (f);
   
   [view getBounds:&r];
   ns_reset_clip(view);
   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->output_data.ns->view;
   id window;
   int vbextra=FRAME_HAS_VERTICAL_SCROLL_BARS(f) ?
               rint(FRAME_SCROLL_BAR_PIXEL_WIDTH (f) > 0
                    ? FRAME_SCROLL_BAR_PIXEL_WIDTH (f)
                    : (FRAME_SCROLL_BAR_COLS (f)
                       *f->output_data.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->output_data.ns->internal_border_width;
      r.origin.y=f->output_data.ns->internal_border_width;
      r.size.width =wr.size.width -(f->output_data.ns->internal_border_width*2+
                                    f->output_data.ns->border_width) - vbextra;
      r.size.height=wr.size.height-(f->output_data.ns->internal_border_width*2+
                                    f->output_data.ns->border_height);
      [view setFrame:&r];

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

      if (screen)
         {
         wr.origin.x=f->output_data.ns->left;
         wr.origin.y=screen->screenBounds.size.height-(f->output_data.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->output_data.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;
   }

static void
note_mouse_movement (struct frame *frame, int x, int y)
{
  /* Has the mouse moved off the glyph it was on at the last sighting?  */
  if ((frame!=last_mouse_frame)||
      (x<last_mouse_glyph.origin.x)||
      (x>=(last_mouse_glyph.origin.x+last_mouse_glyph.size.width))||
      (y<last_mouse_glyph.origin.y)||
      (y>=(last_mouse_glyph.origin.y+last_mouse_glyph.size.height)))
    {
      frame->mouse_moved = 1;
      last_mouse_frame = frame;
      // mouse-faces are updated elsewhere
      // note_mouse_highlight (frame, x, y);
    }
}

/* 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)
{
  int row, column, portion;
  Lisp_Object window;
  struct window *w;

  if (disable_mouse_highlight)
    return;

  mouse_face_mouse_x = x;
  mouse_face_mouse_y = y;
  mouse_face_mouse_frame = f;

  /* Find out which glyph the mouse is on.  */
  ns_pixel_to_glyph_coords (f, x, y, &column, &row, 0, 1);

  mouse_face_mouse_row = row;
  // set mouse_face_mouse_rect to cover current glyph for when we're
  // not in a mouse-face
  ns_chars_to_rect (f, column, row, column, row, &mouse_face_mouse_rect);
  
  if (mouse_face_defer)
    return;

  if (gc_in_progress)
    {
      mouse_face_deferred_gc = 1;
      return;
    }

  /* Which window is that in?  */
  window = window_from_coordinates (f, column, row, &portion);
  w = XWINDOW (window);

  /* If we were displaying active text in another window, clear that.  */
  if (! EQ (window, mouse_face_window))
    clear_mouse_face ();

  /* Are we in a window whose display is up to date?
     And verify the buffer's text has not changed.  */
  if (WINDOWP (window) && portion == 0 && row >= 0 && column >= 0
      && row < FRAME_HEIGHT (f) && column < FRAME_WIDTH (f)
      && EQ (w->window_end_valid, w->buffer)
      && w->last_modified == BUF_MODIFF (XBUFFER (w->buffer)))
    {
      int *ptr = FRAME_CURRENT_GLYPHS (f)->charstarts[row];
      int i, pos;

      /* Find which buffer position the mouse corresponds to.  */
      for (i = column; i >= 0; i--)
	if (ptr[i] > 0)
	  break;
      pos = ptr[i];
      /* Is it outside the displayed active region (if any)?  */
      if (pos <= 0)
	clear_mouse_face ();
      else if (! (EQ (window, mouse_face_window)
		  && row >= mouse_face_beg_row
		  && row <= mouse_face_end_row
		  && (row > mouse_face_beg_row
		      || column >= mouse_face_beg_col)
		  && (row < mouse_face_end_row
		      || column < mouse_face_end_col
		      || mouse_face_past_end)))
	{
	  Lisp_Object mouse_face, overlay, position;
	  Lisp_Object *overlay_vec;
	  int len, noverlays, ignor1;
	  struct buffer *obuf;
	  int obegv, ozv;

	  /* If we get an out-of-range value, return now; avoid an error.  */
	  if (pos > BUF_Z (XBUFFER (w->buffer)))
	    return;

	  /* Make the window's buffer temporarily current for
	     overlays_at and compute_char_face.  */
	  obuf = current_buffer;
	  current_buffer = XBUFFER (w->buffer);
	  obegv = BEGV;
	  ozv = ZV;
	  BEGV = BEG;
	  ZV = Z;

	  /* Yes.  Clear the display of the old active region, if any.  */
	  clear_mouse_face ();

	  /* Is this char mouse-active?  */
	  XSETINT (position, pos);

          len = 10;
          overlay_vec = (Lisp_Object *) xmalloc (len * sizeof (Lisp_Object));
          
          /* Put all the overlays we want in a vector in overlay_vec.
             Store the length in len.  */
          noverlays = overlays_at (XINT (pos), 1, &overlay_vec, &len,
                                   NULL, NULL);
          noverlays = sort_overlays (overlay_vec, noverlays, w);

          /* Find the highest priority overlay that has a mouse-face prop.  */
          overlay = Qnil;
          for (i = 0; i < noverlays; i++)
            {
              mouse_face = Foverlay_get (overlay_vec[i], Qmouse_face);
              if (!NILP (mouse_face))
                {
                  overlay = overlay_vec[i];
                  break;
                }
            }
          free (overlay_vec);
          /* If no overlay applies, get a text property.  */
          if (NILP (overlay))
            mouse_face =
              Fget_text_property (position, Qmouse_face, w->buffer);
          
	  /* Handle the overlay case.  */
	  if (! NILP (overlay))
	    {
	      /* Find the range of text around this char that
		 should be active.  */
	      Lisp_Object before, after;
	      int ignore;

	      before = Foverlay_start (overlay);
	      after = Foverlay_end (overlay);
	      /* Record this as the current active region.  */
	      fast_find_position (window, before, ozv,
				  &mouse_face_beg_col,
				  &mouse_face_beg_row);
	      mouse_face_past_end
		= !fast_find_position (window, after, ozv,
				       &mouse_face_end_col,
				       &mouse_face_end_row);
	      mouse_face_window = window;
	      mouse_face_face_id
		= ns_compute_char_face (f, w, pos, 0, 0,
                                        &ignore, pos + 1, 1);

	      /* Display it as active.  */
	      show_mouse_face (1);
	    }
	  /* Handle the text property case.  */
	  else if (! NILP (mouse_face))
	    {
	      /* Find the range of text around this char that
		 should be active.  */
	      Lisp_Object before, after, beginning, end;
	      int ignore;

	      beginning = Fmarker_position (w->start);
              end = XSETINT (end, (BUF_Z (XBUFFER (w->buffer))
                                   - XFASTINT (w->window_end_pos)));

	      before
		= Fprevious_single_property_change (make_number (pos + 1),
						    Qmouse_face,
						    w->buffer, beginning);
	      after
		= Fnext_single_property_change (position, Qmouse_face,
						w->buffer, end);
	      /* Record this as the current active region.  */
	      fast_find_position (window, before, ozv,
				  &mouse_face_beg_col,
				  &mouse_face_beg_row);
	      mouse_face_past_end
		= !fast_find_position (window, after, ozv,
				       &mouse_face_end_col,
				       &mouse_face_end_row);
	      mouse_face_window = window;
	      mouse_face_face_id
		= ns_compute_char_face (f, w, pos, 0, 0,
                                        &ignore, pos + 1, 1);

	      /* Display it as active.  */
	      show_mouse_face (1);
	    }
          /* Handle the no-mouse-face case */
          else
            {
              Lisp_Object tbefore, tafter, obefore, oafter;
              Lisp_Object beginning, end;
              int bcol, brow, ecol, erow, pcol, prow;
              int width = window_internal_width (w) + w->left;
              int height = window_internal_height (w) + w->top;
              
              beginning = Fmarker_position (w->start);
	      XSETINT (end, (BUF_Z (XBUFFER (w->buffer))
                             - XFASTINT (w->window_end_pos)));

              //was if ((pos + 1) >= XFASTINT (end))
              if (pos >= XFASTINT (end))
		{
                  fast_find_position (window, position, ozv,
                                      &bcol, &brow);
                  ecol = width;
                  erow = height;
		  
                  if (bcol > column)
                    {
                      bcol = w->left;
                      brow++;
                    }
                }
              else
		{
                  int after, before;
                  
                  tbefore =
                    Fprevious_single_property_change (make_number (pos + 1),
                                                      Qmouse_face,
                                                      w->buffer, beginning);
                  tafter =
                    Fnext_single_property_change (position, Qmouse_face,
                                                  w->buffer, end);

                  obefore = Fprevious_overlay_change (make_number (pos + 1));
                  oafter = Fnext_overlay_change (position);

                  if ((before = max (XFASTINT (tbefore),
                                     XFASTINT (obefore)))
                      == XFASTINT (beginning))
                    {
                      bcol = w->left;
                      brow = w->top;
                    }
                  else
                    {
                      fast_find_position (window, before, ozv,
                                          &bcol, &brow);

                      if (brow != row && bcol != w->left)
                        {
                          brow++;
                          bcol = w->left;
                        }
                    }
                  
                  if ((after = min (XFASTINT (tafter),
                                    XFASTINT (oafter))) == XFASTINT (end))
                    {
                      ecol = width;
                      erow = height;
                    }
                  else
                    {
                      fast_find_position (window, after, ozv,
                                          &ecol, &erow);

                      if (erow != row)
                        {
                          erow--;
                          ecol = width;
                        }
                      // we are in the row, but the region apparently
                      // ends before column.  This happens with
                      // invisible text, so we extend the region to the
                      // end of the line.
                      else if (ecol < column)
                        ecol = width;
                    }
                }
              ns_chars_to_rect (f, bcol, brow, ecol-1, erow,
                                &mouse_face_mouse_rect);
            }
	  BEGV = obegv;
	  ZV = ozv;
	  current_buffer = obuf;
	}
    }
}

/* Find the row and column of position POS in window WINDOW.
   Store them in *COLUMNP and *ROWP.
   This assumes display in WINDOW is up to date.
   If POS is above start of WINDOW, return coords
   of start of first screen line.
   If POS is after end of WINDOW, return coords of end of last screen line.

   Value is 1 if POS is in range, 0 if it was off screen.  */

static int
fast_find_position (window, pos, end, columnp, rowp)
     Lisp_Object window;
     int pos, end;
     int *columnp, *rowp;
{
  struct window *w = XWINDOW (window);
  FRAME_PTR f = XFRAME (WINDOW_FRAME (w));
  int i;
  int row = 0;
  int left = w->left;
  int top = w->top;
  int height = XFASTINT (w->height) - ! MINI_WINDOW_P (w);
  int width = window_internal_width (w);
  int *charstarts;
  int lastcol;
  int maybe_next_line = 0;

  /* Find the right row.  */
  for (i = 0;
       i < height;
       i++)
    {
      int linestart = FRAME_CURRENT_GLYPHS (f)->charstarts[top + i][left];
      if (linestart > pos)
	break;
      /* If the position sought is the end of the buffer,
	 don't include the blank lines at the bottom of the window.  */
      if (linestart == pos && pos == end) // was BUF_ZV (XBUFFER (w->buffer)))
	{
	  maybe_next_line = 1;
	  break;
	}
      if (linestart > 0)
	row = i;
    }

  /* Find the right column with in it.  */
  charstarts = FRAME_CURRENT_GLYPHS (f)->charstarts[top + row];
  lastcol = left;
  for (i = 0; i < width; i++)
    {
      if (charstarts[left + i] == pos)
	{
	  *rowp = row + top;
	  *columnp = i + left;
	  return 1;
	}
      else if (charstarts[left + i] > pos)
	break;
      else if (charstarts[left + i] > 0)
	lastcol = left + i;
    }

  /* If we're looking for the end of the buffer,
     and we didn't find it in the line we scanned,
     use the start of the following line.  */
  if (maybe_next_line)
    {
      row++;
      lastcol = left;
    }

  *rowp = row + top;
  *columnp = lastcol;
  return 0;
}

// FIXME/cl needs display_info arg
static void show_mouse_face (int hl)
{
  struct window *w = XWINDOW (mouse_face_window);
  int width = window_internal_width (w);
  FRAME_PTR f = XFRAME (WINDOW_FRAME (w));
  int i;
  int cursor_off = 0;
  int old_curs_x = curs_x;
  int old_curs_y = curs_y;
  NXRect r;

  /* Set these variables temporarily
     so that if we have to turn the cursor off and on again
     we will put it back at the same place.  */
  curs_x = f->phys_cursor_x;
  curs_y = f->phys_cursor_y;

  for (i = mouse_face_beg_row;
       i <= mouse_face_end_row; i++)
    {
      int column = (i == mouse_face_beg_row
		    ? mouse_face_beg_col
		    : w->left);
      int endcolumn = (i == mouse_face_end_row
		       ? mouse_face_end_col
		       : w->left + width);
      endcolumn = min (endcolumn, FRAME_CURRENT_GLYPHS (f)->used[i]);

      /* If the cursor's in the text we are about to rewrite,
	 turn the cursor off.  */
      if (i == curs_y
	  && curs_x >= column - 1
	  && curs_x <= endcolumn)
	{
          ns_dumpcursor (f, -1, -1);
	  cursor_off = 1;
	}

      ns_dumpglyphs (f, column, i,   // r.origin.x, r.origin.y,
                     FRAME_CURRENT_GLYPHS (f)->glyphs[i] + column,
                     endcolumn - column,
                     /* Highlight with mouse face if hl > 0.  */
                     hl > 0 ? 3 : 0 /*, 0*/);
      
      if (i == mouse_face_mouse_row)
        {
          ns_chars_to_rect (f, column, i, endcolumn-1, i,
                            &mouse_face_mouse_rect);
        }
      
    }
  
  ns_dumpglyphs(f,0,0,0,0,-1);
  /* If we turned the cursor off, turn it back on.  */
  if (cursor_off)
    ns_dumpcursor(f,f->cursor_x,f->cursor_y);

  curs_x = old_curs_x;
  curs_y = old_curs_y;

#if 0
  /* Change the mouse cursor according to the value of HL.  */
  if (hl > 0)
    XDefineCursor (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
		   f->output_data.x->cross_cursor);
  else
    XDefineCursor (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
		   f->output_data.x->text_cursor);
#endif
}

// FIXME/cl needs display_info arg
static void clear_mouse_face (void)
{
  if (! NILP (mouse_face_window))
    show_mouse_face (0);

  mouse_face_beg_row = mouse_face_beg_col = -1;
  mouse_face_end_row = mouse_face_end_col = -1;
  mouse_face_window = Qnil;
}

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;

   face.stipple = nil;  //FIXME/cl stipple frame param ?
   face.pixmap_w = face.pixmap_h = 0;
   
   if (ns_load_font(&face))
      return 1;

   *f->output_data.ns->face=face;

  /* Compute the scroll bar width in character columns.  */
  if (f->scroll_bar_pixel_width > 0)
    {
      int wid = face.width;
      f->scroll_bar_cols = (f->scroll_bar_pixel_width + wid-1) / wid;
    }
  else
    f->scroll_bar_cols = 2;
      
   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->output_data.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->output_data.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->output_data.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->output_data.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->output_data.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->output_data.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 the frame needs to be redrawn,
	 simply forget about any prior mouse highlighting.  */
      if (FRAME_GARBAGED_P (f))
        mouse_face_window = Qnil;

      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.
            Likewise, don't do anything if the frame is garbaged;
            in that case, the FRAME_CURRENT_GLYPHS that we would use
            are all wrong, and we will redisplay that line anyway.  */
         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->output_data.ns->view;
      [view unlockFocus];
      [[view window] flushWindow];
      DPSFlush();
      lockfocused=0;
      }
   if (f == mouse_face_mouse_frame)
     mouse_face_defer = 0;
   return 0;
   }

static int ns_frame_up_to_date(struct frame *f)
{
  if (mouse_face_deferred_gc || (f == mouse_face_mouse_frame))
    {
      NXRect r;
      id view = mouse_face_mouse_frame->output_data.ns->view;
      NXPoint position;

      [[view window] getMouseLocation:&position];
      [view convertPoint:&position fromView:nil];
      [view getBounds:&r];
      if ([view mouse:&position inRect:&r] && !f->iconified &&
          !f->async_iconified)
        {
          note_mouse_highlight (mouse_face_mouse_frame,
                                mouse_face_mouse_x,
                                mouse_face_mouse_y);
          ns_set_mouse_tracking (view);
        }
      else if (mouse_face_tracked_view == view)
        {
          // clear_mouse_face ();
          ns_remove_mouse_tracking ();
        }

      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 **fp, int insist,
			       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;

   Lisp_Object frame, tail;

   /* Clear the mouse-moved flag for every frame on this display.  */
   FOR_EACH_FRAME (tail, frame)
     if (FRAME_NS_DISPLAY (XFRAME (frame)) == FRAME_NS_DISPLAY (*fp))
       XFRAME (frame)->mouse_moved = 0;

   if (last_mouse_frame) *fp=last_mouse_frame;
   view=(*fp)->output_data.ns->view;

   [[view window] getMouseLocation:&position];
   [view convertPoint:&position fromView:nil];
   ns_pixel_to_glyph_coords(*fp, (int)rint(position.x), (int)rint(position.y),
                            &xchar, &ychar, 0, 1);
   ns_chars_to_rect(*fp, 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;
   }

// FIXME/cl multidisplay
static void ns_frame_rehighlight(struct frame *frame)
   {
   struct frame *f=0;

   if (ns_focus_frame)
      {
      f = (GC_FRAMEP (FRAME_FOCUS_FRAME (ns_focus_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->output_data.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->output_data.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;

   XSETWINDOW (win, window);
   if (FRAME_SCROLL_BAR_PIXEL_WIDTH (f) > 0)
     {
       ns_chars_to_rect (f, left, top, left, top+height-1, &r);
       r.size.width = FRAME_SCROLL_BAR_PIXEL_WIDTH (f);
     }
   else
     ns_chars_to_rect (f, left, top,
                       left + (FRAME_SCROLL_BAR_COLS (f) - 1),
                       top + height - 1, &r);
   [view convertRectToSuperview:&r];

   // if (r.size.width*2.5>=r.size.height)
   if (window->height < 3) // we want at least 3 lines to display a scrollbar
     {
       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->output_data.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->output_data.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;
   }

static int ns_initialized;

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

  if (!ns_initialized)
    {
      ns_initialize ();
      ns_initialized = 1;
    }

  [EmacsApp new];
  if ((ns_current_display = NXApp) == 0)
    return 0;

  dpyinfo = (struct ns_display_info *)
    xmalloc (sizeof (struct ns_display_info));

#ifdef MULTI_KBOARD
  dpyinfo->kboard = all_kboards;
#endif

  dpyinfo->next = ns_display_list;
  ns_display_list = dpyinfo;
  
  /* Put it on ns_display_name_list */
  ns_display_name_list = Fcons (Fcons (display_name, Qnil),
                                ns_display_name_list);
  dpyinfo->name_list_element = XCONS (ns_display_name_list)->car;
  
      {
      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();

#if 0
      {
      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];
      }
#else
      {
      id cl;
      Lisp_Object tem, tem1;
      extern Lisp_Object Vsource_directory;

      // first try data_dir, then invocation-dir
      // and finally source-directory/etc
      tem1 = tem =
        Fexpand_file_name (build_string ("Emacs.clr"), Vdata_directory);
      if (NILP (Ffile_exists_p (tem)))
        {
	  tem = Fexpand_file_name (build_string ("Emacs.clr"),
                                   Vinvocation_directory);
          if (NILP (Ffile_exists_p (tem)))
            {
              Lisp_Object newdir =
                Fexpand_file_name (build_string ("etc/"),
                                   Vsource_directory);
              tem = Fexpand_file_name (build_string ("Emacs.clr"),
                                       newdir);
            }
        }
      cl=[[NXColorList alloc] initWithName:"Emacs"
                              fromFile:XSTRING (tem)->data];
      if (cl==nil)
         fatal ("Could not find %s.\n", XSTRING (tem1)->data);
      [[NXColorList availableColorLists] addObject:cl];
      }
#endif

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

  mouse_face_mouse_frame = 0;
  mouse_face_deferred_gc = 0;
  mouse_face_beg_row = mouse_face_beg_col = -1;
  mouse_face_end_row = mouse_face_end_col = -1;
  mouse_face_face_id = 0;
  mouse_face_window = Qnil;
  mouse_face_mouse_x = mouse_face_mouse_y = 0;
  mouse_face_defer = 0;
  mouse_face_mouse_row = -1;
  mouse_face_tracked_view = nil;
  
   // FIXME/cl check
   // change_keyboard_wait_descriptor(-1);
   delete_keyboard_wait_descriptor (0);
   
   [(Application *)NXApp run];

   return dpyinfo;
}

/* Set up use of NS before we make the first connection.  */

ns_initialize ()
{
  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;

  /* No interupt input under NS */
  Fset_input_mode (Qnil, Qnil, Qt, Qnil);
}

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;

  staticpro (&ns_display_name_list);
  ns_display_name_list = Qnil;
  
   }

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->output_data.ns->current_cursor==no_highlight)
      {
      Lisp_Object tem=get_frame_param(ns_highlight_frame, Qcursor_type);
      ns_highlight_frame->output_data.ns->desired_cursor=ns_lisp_to_cursor_type(tem);
      }
   else
      {
      ns_highlight_frame->output_data.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->output_data.ns->current_cursor==no_highlight)
         {
         Lisp_Object tem=get_frame_param(ns_highlight_frame, Qcursor_type);
         ns_highlight_frame->output_data.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->output_data.ns->view)
      return [selected_frame->output_data.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

-(NXEvent *) getNextEvent: (int) mask
                 waitFor: (double) timeout
               threshold: (int) level
{
  if (!fake_event_p)
    return [super getNextEvent: mask waitFor: timeout threshold: level];

  fake_event_p = NO;
  return &fake_event;
}

@end

@implementation EmacsView
- changeFont:sender
   {
   NXEvent *e=[NXApp currentEvent];
   struct ns_face *face=emacsframe->output_data.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) && (flags & NX_SHIFTMASK))
     {
       events->modifiers |= ctrl_modifier;
       code |= 64;
       flags &= ~ (NX_CONTROLMASK | NX_SHIFTMASK);
     }
   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;
#if 0
   switch (EV_UDMODIFIERS(theEvent))
      {
    case up_modifier:
      break;
    case down_modifier:
      break;
      }
#endif

   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];
  last_mouse_movement_time = EV_TIMESTAMP (e);

  note_mouse_movement (emacsframe, p.x, p.y);
  if (emacsframe->mouse_moved && 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(FRAME_SCROLL_BAR_PIXEL_WIDTH (emacsframe) > 0
                    ? FRAME_SCROLL_BAR_PIXEL_WIDTH (emacsframe)
                    : (FRAME_SCROLL_BAR_COLS(emacsframe)
                       *emacsframe->output_data.ns->face->width)) : 0;

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

   rows=rint((frameSize->height-
              emacsframe->output_data.ns->border_height-
              2*emacsframe->output_data.ns->internal_border_width)/
             emacsframe->output_data.ns->line_height);
   if (rows<MINHEIGHT) rows=MINHEIGHT;
   frameSize->height=(rows*emacsframe->output_data.ns->line_height+
                      emacsframe->output_data.ns->border_height+
                      2*emacsframe->output_data.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->output_data.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->output_data.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
{
  const NXScreen *screen = [miniwindow screen];
  
  if (NUMBERP (emacsframe->output_data.ns->icon_top) &&
      NUMBERP (emacsframe->output_data.ns->icon_left))
    [miniwindow moveTopLeftTo:
                SCREENMAXBOUND(XFLOATINT (emacsframe->output_data.ns->icon_left))
                :SCREENMAXBOUND(screen->screenBounds.size.height -
                                XFLOATINT (emacsframe->output_data.ns->icon_top))];
  
  [self setMiniwindowImage];
  return self;
}

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

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

   f->output_data.ns->view=self;
   emacsframe=f;
   old_title=0;
   
   r.origin.x=r.origin.y=0;
   r.size.width +=2*f->output_data.ns->internal_border_width+vbextra;
   r.size.height+=2*f->output_data.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:YES];

   [win getFrame:&wr];
   f->output_data.ns->border_width=wr.size.width-r.size.width;
   f->output_data.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];

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

       {
       const NXScreen *screen=[win screen];

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

   [win makeFirstResponder:self];
   [win setBackgroundColor:emacsframe->output_data.ns->face->background_color];
   [win setBackgroundGray:NXGrayComponent(emacsframe->output_data.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];

   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->output_data.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->output_data.ns) return self;
   [win getFrame:&r];
   screen=[win screen];
   if (screen!=0)
      {
      emacsframe->output_data.ns->left=r.origin.x;
      emacsframe->output_data.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
   {
   NXEvent *e=[NXApp currentEvent];
   NXRect r;
   const NXScreen *screen = [window screen];

   if (!emacsframe->output_data.ns) return self;
   emacsframe->async_visible   = 1;
   emacsframe->async_iconified = 0;
   windows_or_buffers_changed++;
   // save the position where the user left the miniwindow
   [[window counterpart] getFrame:&r];
   XSETINT (emacsframe->output_data.ns->icon_top,
            screen->screenBounds.size.height - NX_MAXY (&r));
   XSETINT (emacsframe->output_data.ns->icon_left, NX_X (&r));
   if ([window windowNum] > 0)
     ns_adjust_tracking_rects (emacsframe);
   if (eventsleft <= 0) return nil;
   events->kind=frame_switch_event;
   events->modifiers=0;
   events->code=0;
   EV_TRAILER (e);
   return self;
   }

- windowDidExpose:sender
   {
   if (!emacsframe->output_data.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->output_data.ns) return self;
   if (emacsframe->output_data.ns->view == mouse_face_tracked_view)
     ns_remove_mouse_tracking ();
   emacsframe->async_iconified = 1;
   /* Terminate the event loop.  */
   if (send_appdefined) ns_send_appdefined (-1);
   return self;
   }

- mouseEntered:(NXEvent *)theEvent
{
  NXPoint p=theEvent->location;
  NXRect r;

  [self convertPoint:&p fromView:nil];
  last_mouse_movement_time = EV_TIMESTAMP (theEvent);

  switch (theEvent->data.tracking.trackingNum)
    {
    case FRAME_TRACKNUM:
      if (emacsframe && emacsframe->auto_raise
          && !emacsframe->iconified && !emacsframe->async_iconified)
        {
          [[self window] makeKeyAndOrderFront:NXApp];
        }
      break;
    case WINDOW_TRACKNUM:
      note_mouse_movement (emacsframe, p.x, p.y);

      note_mouse_highlight (emacsframe, p.x, p.y);
      ns_set_mouse_tracking (self);
      break;
    case MOUSE_FACE_TRACKNUM:
      // this can't happen, I guess... (cl)
      note_mouse_movement (emacsframe, p.x, p.y);

      note_mouse_highlight (emacsframe, p.x, p.y);
      ns_set_mouse_tracking (self);
      break;
    }
  return self;
}

- mouseExited:(NXEvent *)theEvent
{
  NXPoint p=theEvent->location;
  NXRect r;

  last_mouse_movement_time = EV_TIMESTAMP (theEvent);

  switch (theEvent->data.tracking.trackingNum)
    {
    case FRAME_TRACKNUM:
      if (emacsframe && emacsframe->auto_lower
          && !emacsframe->iconified && !emacsframe->async_iconified)
        {
          [[self window] orderBack:NXApp]; /* XXX unfocus ! */
        }
      break;
    case WINDOW_TRACKNUM:
      if (self == mouse_face_tracked_view)
        ns_remove_mouse_tracking ();
      if (emacsframe == mouse_face_mouse_frame)
        clear_mouse_face ();
      break;
    case MOUSE_FACE_TRACKNUM:
      [self convertPoint:&p fromView:nil];

      note_mouse_movement (emacsframe, p.x, p.y);

      note_mouse_highlight (emacsframe, p.x, p.y);
      [self getBounds:&r];
      if (emacsframe == mouse_face_mouse_frame &&
          [self mouse:&p inRect:&r] && !mouse_face_defer &&
          !mouse_face_deferred_gc)
        ns_set_mouse_tracking (self);
      else if (self == mouse_face_tracked_view)
        ns_remove_mouse_tracking ();
      break;
    }
  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->output_data.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;
   }

- setMiniwindowImage
{
  id image = [window miniwindowImage];
  if (image == emacsframe->output_data.ns->miniimage)
    return self;
  if (image && [image isKindOf:[EmacsImage class]])
    [image free];
  [window setMiniwindowImage:emacsframe->output_data.ns->miniimage];
  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->output_data.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->output_data.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->output_data.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;
   }

/* Asynchronous mouse tracking for scroller.  This allow us to dispatch
   mouseDragged events without going into a modal loop.  */
-mouseDown: (NXEvent *) e
{
  NXRect r;

  /* Fake a NX_LMOUSEUP event in the next call to
     `getNextEvent:waitFor:threshold:'.  This is made from our super's
     trackKnob: method which will then exit it's modal loop after calling
     our `scrollerMoved:' method.  */
  fake_event_p = YES;
  fake_event = *e;
  fake_event.type = NX_LMOUSEUP;

  /* Get the rect of the Scroller's knob in the screens coordinate system.  */
  [self calcRect: &r forPart: NX_KNOB];
  [self convertRect: &r toView: nil];

  if (NX_Y (&r) <= e->location.y && e->location.y <= NX_MAXY (&r))
    {
      /* Compute the relative offset in the knob of the mousedown.  */
      last_mouse_offset = (e->location.y - NX_Y (&r)) / NX_HEIGHT (&r);
    }
  else
    {
      /* XXX The mouse down is outside the knob, so pretend it's in the
         middle of the knob.  Since the knob will move there.  */
      last_mouse_offset = 0.5;
    }

  return [super mouseDown: e];
}

-mouseDragged: (NXEvent *) e
{
  NXEvent ev;
  NXRect r;

  /* Get the rect of the Scroller's knob in the screens coordinate system.  */
  [self calcRect: &r forPart: NX_KNOB];
  [self convertRect: &r toView: nil];

  /* First we generate a NX_LMOUSEDOWN event on the position the mouse last
     was.  */
  ev = fake_event;
  ev.type = NX_LMOUSEDOWN;

  /* Make the mousedown happen at the same relative offset from the bottom
     of the knob as the initial mousedown.  */
  ev.location.y = NX_Y (&r) + last_mouse_offset * NX_HEIGHT (&r);

  /* Fake a NX_LMOUSEUP event in the next call to
     `getNextEvent:waitFor:threshold:'.  This is called from our super's
     trackKnob: method which will then exit it's modal loop after calling
     our `scrollerMoved:' method.  */
  fake_event_p = YES;
  fake_event = *e;
  fake_event.type = NX_LMOUSEUP;

  /* Move the x coordinate of the mouse up event into the frame of
     the scrollbar handle.  */
  fake_event.location.x = r.origin.x + r.size.width / 2;

  return [super mouseDown: &ev];
}

@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

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.