This is EtermView.m in view mode; [Download] [Up]
/* The EtermView implementation.
For legal stuff see the file COPYRIGHT. */
#import <dpsclient/dpsclient.h>
#import <libc.h>
#import <mach/mach.h>
#import <stdio.h>
#import <stdlib.h>
#import <sys/ioctl.h>
#import <sys/param.h>
#import "EtermView.h"
#import "EmacsApp.h"
#import "etermSupport.h"
#import "defaults.h"
#import "display.h"
extern char **environ;
/* These are the window size limits imposed by GNUEmacs. */
#define MINWIDTH 10
#define MINHEIGHT 5
#define MAXWIDTH 300
#define MAXHEIGHT 300
/* Function prototypes for display.pswm */
void *new_display_rock (EtermView *theView);
void input_from_emacs (int fd, void *theDisplay);
/* Function prototypes for keycode.c */
void kc_init (void);
int kc_keyforcode (int code, int flags);
/* write(2), except it will keep retrying upon partial success. */
static int
write_safely (int fd, char *buf, int n)
{
int i, start = 0;
while (start < n)
{
i = write (fd, buf + start, n - start);
if (i == -1)
return i; /* Some error */
start += i;
}
return n;
}
/* Handle a connection to the event server socket */
static void
connection_from_emacs (int fd, void *rock)
{
EtermView *view = (EtermView *) rock;
int newfd = accept_server_connection (fd);
if (newfd == -1)
return;
if ([view eventChannel])
close (newfd);
else
[view setEventChannel: fdopen (newfd, "w")];
}
@implementation EtermView
+ initialize
{
static NXDefaultsVector etermDefaults =
{
{"NXAutoLaunch", "NO" },
{"NXFixedPitchFontSpacing", "0.95" },
{"HideOnAutoLaunch", "NO" },
{"EmacsPath", DEFAULT_EMACS_PATH},
{"LispPath", DEFAULT_LISP_PATH},
{NULL},
};
const char *sTypes[2], *rTypes[2];
sTypes[0] = NXAsciiPboardType;
sTypes[1] = NULL;
rTypes[0] = NXAsciiPboardType;
rTypes[1] = NULL;
NXRegisterDefaults ([NXApp appName], etermDefaults);
[NXApp registerServicesMenuSendTypes: sTypes andReturnTypes: rTypes];
kc_init ();
return self;
} /* +initialize */
-initFrame: (const NXRect *) newFrame
{
self = [super initFrame: newFrame];
[self setFlipped: YES];
#if 0
[self notifyAncestorWhenFrameChanged: YES];
#endif
spacing = atof (NXGetDefaultValue ([NXApp appName],
"NXFixedPitchFontSpacing"));
/* Check for sane values of spacing. */
if (spacing < 0.9)
spacing = 0.9;
else if (spacing > 2.0)
spacing = 2.0;
[self setFont: [self font]];
[[NXApp fontManager] setSelFont: [self font] isMultiple: NO];
return self;
} /* -initFrame: */
-(float) spacing
{
return spacing;
}
-setSpacing: (float) newSpacing
{
char val[20];
spacing = newSpacing;
[self setFont: [self font]];
sprintf (val, "%f", newSpacing);
NXWriteDefault ([NXApp appName], "NXFixedPitchFontSpacing", val);
return self;
}
-font
{
return [Font userFixedPitchFontOfSize: (float) 0 matrix: NX_FLIPPEDMATRIX];
} /* -font */
-setFont: newFont
{
NXCoord ascender, descender, lineHeight;
NXRect frameRect, contentRect;
Font *screenfont;
int leading;
/* NXTextFontInfo is broken for screen fonts, so call it before converting
the font to a screenfont. */
NXTextFontInfo (newFont , &ascender, &descender, &lineHeight);
screenfont = [newFont screenFont];
if (screenfont)
newFont = screenfont;
leading = (spacing - 1.0) * lineHeight;
fontHeight = (int) (lineHeight * spacing + leading);
fontWidth = (float) ([newFont getWidthOf : " "]
- [newFont getWidthOf : " "] + 0.5);
fontDescender = (int) (descender + leading);
[newFont set];
if (screenfont)
set_font ();
else
fix_font (fontWidth);
displayFont = newFont;
/* Snap the window size to a 1 x 1 character grid. */
[window getFrame: &frameRect];
[self windowWillResize: self toSize: &frameRect.size];
[Window getContentRect: &contentRect forFrameRect: &frameRect
style: [window style]];
[window sizeWindow: contentRect.size.width : contentRect.size.height];
[self windowDidResize: self];
[self display];
return self;
} /* -setFont: */
/* Sent by fontManager. */
-changeFont: sender
{
id selectedFont;
/* Get the font selected in the FontPanel. */
selectedFont = [[NXApp fontManager]
convertFont: [[NXApp fontManager] selFont]];
[self setFont: selectedFont];
[Font setUserFixedPitchFont: selectedFont];
return self;
} /* -changeFont: */
-(FILE *) eventChannel;
{
return eventChannel;
} /* -eventChannel */
-setEventChannel: (FILE *) fp;
{
eventChannel = fp;
return self;
} /* -setEventChannel: */
/* Constrain window so that the content view has an integral width in terms
of characters. XXX this method needs some cleaning up to stop the window
from flashing while resizing. */
-windowWillResize: sender toSize: (NXSize *) frameSize;
{
static int oldw = 0, oldh = 0;
int neww, newh;
NXRect frameRect, contentRect;
int newwidth, newheight;
int style = [window style];
NXSetRect (&frameRect, 0.0, 0.0, frameSize->width, frameSize->height);
[Window getContentRect: &contentRect forFrameRect: &frameRect style: style];
newwidth = (int) (contentRect.size.width + fontWidth / 2 - BORDER_WIDTH * 2);
newheight = (int) (contentRect.size.height
+ fontHeight / 2 - BORDER_WIDTH * 2);
newwidth -= newwidth % fontWidth;
newheight -= newheight % fontHeight;
if (newwidth < MINWIDTH * fontWidth)
newwidth = MINWIDTH * fontWidth;
if (newwidth > MAXWIDTH * fontWidth)
newwidth = MAXWIDTH * fontWidth;
if (newheight < MINHEIGHT * fontHeight)
newheight = MINHEIGHT * fontHeight;
if (newheight > MAXHEIGHT * fontHeight)
newheight = MAXHEIGHT * fontHeight;
contentRect.size.width = (NXCoord) newwidth + BORDER_WIDTH * 2;
contentRect.size.height = (NXCoord) newheight + BORDER_WIDTH * 2;
[Window getFrameRect:&frameRect forContentRect:&contentRect style:style];
frameSize->width = frameRect.size.width;
frameSize->height = frameRect.size.height;
newh = newheight / fontHeight;
neww = newwidth / fontWidth;
if (newh != oldh || neww != oldw)
{
[self showTitle: newh : neww];
oldh = newh;
oldw = neww;
}
return self;
} /* -windowWillResize:toSize: */
/* Convert size to number of characters in content view. Inform child emacs
process of our new size. */
-windowDidResize: sender
{
NXRect frameRect, contentRect;
int style = [window style];
struct winsize winsize;
[window getFrame: &frameRect];
[Window getContentRect: &contentRect forFrameRect: &frameRect style: style];
lines = (int) ((contentRect.size.height - BORDER_WIDTH * 2) / fontHeight);
cols = (int) ((contentRect.size.width - BORDER_WIDTH * 2) / fontWidth);
[self showTitle: lines : cols];
/* emacs doesn't do anything with a TIOCCSWINSZ if LINES nor COLS change,
so send a redraw screen in that case. */
winsize.ws_row = lines;
winsize.ws_col = cols;
winsize.ws_xpixel = (int) contentRect.size.width;
winsize.ws_ypixel = (int) contentRect.size.height;
ioctl (masterChannel, TIOCSWINSZ, &winsize);
if (eventChannel)
fprintf (eventChannel, "(redraw-display)\n");
return self;
} /* -windowDidResize: */
/* Start up the child emacs process, perhaps on a file. */
-startEmacs: (char **) files : (int) nfiles
{
int master, slave, ptynumber, portno, i, n;
char **args, **env;
char *lisp, *path;
/* Grab a pty/tty pair */
create_channel (&master, &slave, &ptynumber);
masterChannel = master;
/* Create the server socket */
eventServerSocket = create_server_socket (&portno);
DPSAddFD (eventServerSocket, connection_from_emacs,
(void *) self, NX_RUNMODALTHRESHOLD);
/* Frob the environment */
env = patch_env (environ, portno);
/* Make sure the size is set correctly */
[self windowDidResize: self];
args = calloc (nfiles + 6, sizeof (char *));
args[0] = "emacs";
path = malloc (MAXPATHLEN + 1);
lisp = NXGetDefaultValue ([NXApp appName], "LispPath");
args[1] = "-l";
args[3] = "-f";
args[4] = "start-event-server";
n = 5;
if (strrchr (lisp, '/'))
args[2] = lisp;
else
{
if ([[NXBundle mainBundle] getPath: path forResource: lisp ofType: NULL])
args[2] = path;
else
n = 1;
}
for (i = 0; i < nfiles; i++, n++)
args[n] = files[i];
/* Fork off the Emacs */
fork_shell (NXGetDefaultValue ([NXApp appName], "EmacsPath"),
args, env, slave);
free (path);
free (args);
close (slave);
DPSAddFD (master, input_from_emacs, new_display_rock (self),
NX_RUNMODALTHRESHOLD);
[window display];
return self;
} /* -startEmacs: */
-(BOOL) acceptsFirstResponder
{
return YES;
} /* -acceptsFirstResponder */
/* Keyboard input is given to the child emacs process. XXX this method needs some cleeaning up. */
-keyDown: (NXEvent *) theEvent;
{
NXEvent eventbuf;
char buf[1024];
int i = 0;
int code;
int start = 0;
if (theEvent->flags & NX_NUMERICPADMASK
&& theEvent->data.key.charCode >= 0xac
&& theEvent->data.key.charCode <= 0xaf)
{
/* Arrow keys (left, up, right, down) */
if (theEvent->flags & NX_SHIFTMASK)
buf[i++] = "\302\326\306\026"[theEvent->data.key.charCode - 0xac];
else if (theEvent->flags & NX_CONTROLMASK)
buf[i++] = "\001\274\005\276"[theEvent->data.key.charCode - 0xac];
else if (theEvent->flags & NX_ALTERNATEMASK)
buf[i++] = "\202\220\206\216"[theEvent->data.key.charCode - 0xac];
else
buf[i++] = "\002\020\006\016"[theEvent->data.key.charCode - 0xac];
}
else if (theEvent->flags & NX_NUMERICPADMASK
&& theEvent->data.key.charCode == 0x03)
{
/* Enter key */
buf[i++] = '\r';
}
else if (theEvent->flags & NX_ALTERNATEMASK)
{
/* Handle ALT key as a META key */
code = kc_keyforcode(theEvent->data.key.keyCode, theEvent->flags);
if (code != -1) {
buf[i++] = code | 0x80;
}
}
else buf[i++] = theEvent->data.key.charCode;
/* Grab as many keypressed events as we can from a modal event loop */
while (i < sizeof (buf)
&& (theEvent = [NXApp peekNextEvent: NX_ALLEVENTS into: &eventbuf])
&& theEvent->type == NX_KEYDOWN
&& !(theEvent->flags & NX_COMMANDMASK))
{
theEvent = [NXApp getNextEvent: NX_ALLEVENTS];
if (theEvent->flags & NX_NUMERICPADMASK
&& theEvent->data.key.charCode >= 0xac
&& theEvent->data.key.charCode <= 0xaf)
{
/* Arrow keys (left, up, right, down) */
if (theEvent->flags & NX_SHIFTMASK)
buf[i++] = "\302\326\306\026"[theEvent->data.key.charCode - 0xac];
else if (theEvent->flags & NX_CONTROLMASK)
buf[i++] = "\001\274\005\276"[theEvent->data.key.charCode - 0xac];
else if (theEvent->flags & NX_ALTERNATEMASK)
buf[i++] = "\202\220\206\216"[theEvent->data.key.charCode - 0xac];
else
buf[i++] = "\002\020\006\016"[theEvent->data.key.charCode - 0xac];
}
else if (theEvent->flags & NX_NUMERICPADMASK
&& theEvent->data.key.charCode == 0x03)
{
/* Enter key */
buf[i++] = '\r';
}
else if (theEvent->flags & NX_ALTERNATEMASK)
{
/* Handle ALT key as a META key */
code = kc_keyforcode (theEvent->data.key.keyCode, theEvent->flags);
if (code != -1)
buf[i++] = code | 0x80;
}
else buf[i++] = theEvent->data.key.charCode;
}
write_safely (masterChannel, buf, i);
obscure_cursor ();
return self;
} /* -keyDown: */
/* Mouse input is converted to a character position and given to the child
emacs process, but only if the emacs has opened an event port. */
-mouseEvent: (NXEvent *) theEvent : (int) button;
{
int x, y;
char buf[35];
if (!eventChannel)
return self;
[self convertPoint: &theEvent->location fromView:nil];
x = (int) ((theEvent->location.x - BORDER_WIDTH) / fontWidth);
y = (int) ((theEvent->location.y - BORDER_WIDTH) / fontHeight);
if (x < 0)
x = 0;
if (y < 0)
y = 0;
if (x >= cols)
x = cols - 1;
if (y >= lines)
y = lines - 1;
sprintf (buf, "\030%c(%d %d %d 1)\r", 0,
(button + ((theEvent->flags & NX_SHIFTMASK) ? 8 : 0)
+ ((theEvent->flags & NX_CONTROLMASK) ? 16 : 0)
+ ((theEvent->flags & NX_ALTERNATEMASK) ? 32 : 0)), x, y);
write_safely (masterChannel, buf, 2+strlen (buf+2));
return self;
} /* -mouseEvent:: */
-mouseDown: (NXEvent *) theEvent;
{
[self mouseEvent: theEvent: 1];
return self;
} /* -mouseDown: */
-mouseUp: (NXEvent *) theEvent;
{
[self mouseEvent: theEvent: 1 + 128];
return self;
} /* -mouseUp */
-rightMouseDown: (NXEvent *) theEvent;
{
[self mouseEvent: theEvent: 4];
return self;
} /* rightMouseDown: */
-rightMouseUp: (NXEvent *) theEvent;
{
[self mouseEvent: theEvent: 4 + 128];
return self;
} /* rightMouseUp: */
-(BOOL) emacsOn
{
if (eventChannel)
return YES;
else
return NO;
} /* -emacsOn */
-(BOOL) newFile: (const char *) path
{
if (!eventChannel)
return NO;
fprintf (eventChannel, "(find-file \"%s\")\n", path);
fflush (eventChannel);
return YES;
} /* -newFile: */
/* Quit, cut, copy, paste, and undo messages are sent to the child's event
port. */
-quitEmacs
{
if (!eventChannel)
return nil;
fprintf (eventChannel, "(event-quit)\n");
fflush (eventChannel);
return self;
} /* -quitEmacs */
-pasteboard
{
return currentPasteboard;
} /* -pasteboard */
-appPasteboard
{
if (!mainPasteboard)
mainPasteboard = [Pasteboard new];
return mainPasteboard;
} /* -appPasteboard */
-setPasteboard: pboard
{
currentPasteboard = pboard;
return self;
} /* -setPasteboard: */
-(BOOL) sendEmacsEvent: (char *) theEvent
{
if (!eventChannel)
return NO;
fprintf (eventChannel, "%s\n", theEvent);
fflush (eventChannel);
return YES;
} /* -(BOOL) sendEmacsEvent: */
-undo: sender;
{
[self sendEmacsEvent: "(undo)"];
return self;
} /* -undo: */
-save: sender;
{
[self sendEmacsEvent: "(save-buffer)"];
return self;
} /* -save: */
-saveAll: sender;
{
[self sendEmacsEvent: "(save-some-buffers t)"];
return self;
} /* -saveAll: */
-cut: sender;
{
[self cutTo: [self appPasteboard]];
return self;
} /* -cut: */
-copy: sender;
{
[self copyTo: [self appPasteboard]];
return self;
} /* -copy: */
-paste: sender;
{
[self pasteFrom: [self appPasteboard]];
return self;
} /* -paste: */
-open: sender;
{
int i;
char *path;
const char *directory;
const char *const *fileNames;
if (!eventChannel)
return self;
if (!openPanel)
openPanel = [OpenPanel new];
[openPanel allowMultipleFiles: YES];
[openPanel chooseDirectories: NO];
if ([openPanel runModal])
{
directory = [openPanel directory];
fileNames = [openPanel filenames];
if (fileNames)
{
for (i = 0; fileNames[i]; i++)
{
path = malloc (2 + strlen (directory) + strlen (fileNames[i]));
strcat (strcat (strcpy (path, directory), "/"), fileNames[i]);
fprintf (eventChannel, "(find-file \"%s\")\n", path);
fflush (eventChannel);
free (path);
}
}
}
return self;
} /* -open: */
-saveAs: sender;
{
const char *path;
if (!eventChannel)
return self;
if (!savePanel)
savePanel = [SavePanel new];
if ([savePanel runModal])
{
path = [savePanel filename];
if (path)
{
fprintf (eventChannel, "(write-file \"%s\")\n", path);
fflush (eventChannel);
}
}
return self;
} /* -saveAs: */
-(BOOL) cutTo: pasteboard
{
return [[self setPasteboard: pasteboard] sendEmacsEvent: "(event-cut)"];
} /* -(BOOL) cutTo: */
-(BOOL) copyTo: pasteboard
{
return [[self setPasteboard: pasteboard] sendEmacsEvent: "(event-copy)"];
} /* -(BOOL) copyTo: */
-(BOOL) pasteFrom: pasteboard
{
const NXAtom *types, *p;
char *data;
int len;
if (!eventChannel || !pasteboard)
return NO;
types = [pasteboard types];
for (p = types; *p && strcmp (*p, NXAsciiPboardType); p++);
if (*p && [pasteboard readType: NXAsciiPboardType data: &data length: &len]
&& len)
{
int c;
char *tosend = data;
fputs("(event-paste \"", eventChannel);
/* Write out string, quoting quote, backslash, and newline */
for (tosend = data; len--; tosend++)
{
switch (c = *tosend)
{
case '\n':
fputs ("\\n", eventChannel);
break;
case '\\':
case '\"':
putc('\\', eventChannel);
/* FALL THROUGH */
default:
putc (c, eventChannel);
break;
}
}
fputs ("\")\n", eventChannel);
fflush (eventChannel);
vm_deallocate (task_self (), (vm_address_t) data, len);
}
return YES;
} /* -(BOOL) pasteFrom: */
-getDimensions: (int *) linesPtr : (int *) colsPtr
{
*linesPtr = lines;
*colsPtr = cols;
return self;
} /* -getDimensions:: */
-(Font *) getDisplayFont:
(int *) heightPtr :
(int *) widthPtr :
(int *) descenderPtr
{
*heightPtr = fontHeight;
*widthPtr = fontWidth;
*descenderPtr = fontDescender;
return displayFont;
} /* -getDisplayFont::: */
-showTitle: (int) newLines : (int) newColumns
{
char *newtitle;
if (!titleprefix)
return self;
newtitle = malloc (22 + strlen (titleprefix));
sprintf (newtitle, "%s (%dx%d)", titleprefix, newColumns, newLines);
[(Window *) window setTitle: newtitle];
free (newtitle);
return self;
} /* -showTitle:: */
-setTitle: (char *) title
{
titleprefix = title;
[self showTitle: lines : cols];
return self;
} /* -setTitle: */
-validRequestorForSendType: (NXAtom) typeSent
andReturnType: (NXAtom) typeReturned
{
/* Check to make sure that the types are ones that we can handle. */
if (eventChannel && (typeSent == NXAsciiPboardType || typeSent == NULL)
&& (typeReturned == NXAsciiPboardType || typeReturned == NULL))
return self;
/* Otherwise, return the default. */
return [super validRequestorForSendType: typeSent
andReturnType: typeReturned];
} /* -validRequestorForSendType:andReturnType: */
-(BOOL) writeSelectionToPasteboard: pboard types: (NXAtom *) types
{
const NXAtom *p;
if (eventChannel && pboard)
{
for (p = types; *p && strcmp (*p, NXAsciiPboardType); p++);
if (*p)
{
/* We should have the pasteboard data ready before returning here, how? */
#if 0
[pboard declareTypes: &NXAsciiPboardType
num: 1
owner: NULL];
#endif
return [self copyTo: pboard];
}
}
return NO;
} /* -writeSelectionToPasteboard:types: */
-readSelectionFromPasteboard: pboard
{
[self sendEmacsEvent: "(kill-region (mark) (point))"];
[self pasteFrom: pboard];
return self;
} /* -readSelectionFromPasteboard: */
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.