This is EtermView.m in view mode; [Download] [Up]
/*
* Copyright 1990, John G. Myers
*
* This program 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 1, or (at your option)
* any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#import <stdio.h>
#import <stdlib.h>
#import <libc.h>
#import <sys/ioctl.h>
#import <mach.h>
#import <appkit/defaults.h>
#import <appkit/Window.h>
#import <appkit/Text.h>
#import <appkit/Application.h>
#import <appkit/Pasteboard.h>
#import <dpsclient/dpsclient.h>
#import "EtermView.h"
#import "etermSupport.h"
extern char **environ;
#define MINWIDTH 10 /* these are */
#define MINHEIGHT 5 /* the limits */
#define MAXWIDTH 300 /* imposed by */
#define MAXHEIGHT 300 /* gnu-emacs. */
extern void *new_display_rock(EtermView *theView);
extern void input_from_emacs(int fd, void *theDisplay);
extern void kc_init(void);
extern void obscure_cursor(void);
extern void fix_font(int width);
extern void set_font(void);
extern 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" },
{"NXFixedPitchFont", "Ohlfs" },
{"NXFixedPitchFontSize", "10" },
{"Columns", "80" },
{"Rows", "24" },
{"WinLocX", "205"},
{"WinLocY", "85"},
{"HideOnAutoLaunch", "NO" },
{NULL}
};
NXRegisterDefaults("Emacs", etermDefaults);
kc_init();
return self;
}
+ new
{
const char *fontname;
Font *screenfont;
NXCoord ascender, descender, lineHeight;
int leading;
int fontsize;
float ffontWidth;
self = [super new];
[self setFlipped:YES];
[self setAutosizing:NX_WIDTHSIZABLE|NX_HEIGHTSIZABLE];
[self notifyAncestorWhenFrameChanged:YES];
fontname = NXGetDefaultValue("Emacs", "NXFixedPitchFont");
fontsize = atoi(NXGetDefaultValue("Emacs", "NXFixedPitchFontSize"));
/* TODO - Check non-zero, etc. */
displayFont = [Font newFont: fontname size:(float)fontsize];
NXTextFontInfo( displayFont , &ascender, &descender, &lineHeight);
/*
* NXTextFontInfo appears to be broken for screen fonts, so wait
* until now to get it.
*/
screenfont = [displayFont screenFont];
if (screenfont)
displayFont = screenfont;
leading = -0.0 * lineHeight;
fontHeight = (int) (lineHeight + leading);
ffontWidth = ([displayFont getWidthOf : " "] -
[displayFont getWidthOf : " "]);
fontWidth = ffontWidth + 0.5;
[displayFont set];
if (screenfont) {
set_font();
}
else {
fix_font(fontWidth);
}
fontDescender = (int) (descender + leading);
return self;
}
- (FILE *)eventChannel;
{
return eventChannel;
}
- setEventChannel: (FILE *)fp;
{
eventChannel = fp;
return self;
}
/*
* Constrain window so that the content view has an integral
* width in terms of characters.
*/
- windowWillResize: sender toSize: (NXSize *)frameSize;
{
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;
return self;
}
/*
* 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);
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);
return self;
}
/*
* Resize our parent window so that we have a given number of
* characters in our content view.
*/
- sizeWindowTo: (int)newCols : (int)newLines
{
NXRect contentRect, frameRect;
cols = newCols;
lines = newLines;
[window sizeWindow: (NXCoord) (int) (newCols * fontWidth + BORDER_WIDTH*2) :
(NXCoord) (int) (newLines * fontHeight + BORDER_WIDTH*2)];
return self;
}
/*
* Start up the child emacs process, perhaps on a file
*/
- startEmacs: (char *)withFile
{
int master, slave, ptynumber, portno;
char **env;
static char *args[] = {
"emacs",
NULL,
NULL
};
/* 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[1] = withFile;
/* Fork off the Emacs */
fork_shell("/usr/bin/emacs", args, env, slave);
(void) close(slave);
DPSAddFD(master, input_from_emacs, new_display_rock(self), NX_RUNMODALTHRESHOLD);
[window display];
[window makeKeyAndOrderFront:self];
}
- (BOOL)acceptsFirstResponder
{
return YES;
}
/*
* Keyboard input is given to the child emacs process.
*/
- keyDown: (NXEvent *)theEvent;
{
NXEvent eventbuf;
char buf[1024];
int i = 0;
int code;
int start=0, n;
if (theEvent->flags & NX_NUMERICPADMASK &&
theEvent->data.key.charCode >= 0xac &&
theEvent->data.key.charCode <= 0xaf) {
/* Arrow keys (left, up, right, down) */
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) */
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;
}
/*
* 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;
}
- mouseDown: (NXEvent *)theEvent;
{
[self mouseEvent: theEvent: 1];
return self;
}
- mouseUp: (NXEvent *)theEvent;
{
[self mouseEvent: theEvent: 1+128];
return self;
}
- rightMouseDown: (NXEvent *)theEvent;
{
[self mouseEvent: theEvent: 4];
return self;
}
- rightMouseUp: (NXEvent *)theEvent;
{
[self mouseEvent: theEvent: 4+128];
return self;
}
- (BOOL)emacsOn
/* Added by J. Gregory on 9-Oct-91. */
{
if (eventChannel) { /* I don't trust anyone */
return YES;
} else {
return NO;
}
}
- openFile:(const char *)path
/* Added by J. Gregory on 9-Oct-91. */
{
if (!eventChannel) return self;
fprintf(eventChannel, "(find-file \"%s\")\n", path);
fflush(eventChannel);
return self;
}
/*
* 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;
}
- cut: sender;
{
if (!eventChannel) return self;
fprintf(eventChannel, "(event-cut)\n");
fflush(eventChannel);
return self;
}
- copy: sender;
{
if (!eventChannel) return self;
fprintf(eventChannel, "(event-copy)\n");
fflush(eventChannel);
return self;
}
- paste: sender;
{
static id pasteboard;
const char *const *p, *const *types;
char *data;
int len;
if (!eventChannel) return self;
if (!pasteboard) pasteboard = [Pasteboard new];
types = [pasteboard types];
for (p=types; *p && strcmp(*p, NXAsciiPboard); p++);
if (*p &&
[pasteboard readType:NXAsciiPboard data:&data length:&len] &&
len) {
int c;
char *tosend=data, *quote, *backslash;
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 self;
}
- undo: sender;
{
if (!eventChannel) return self;
fprintf(eventChannel, "(event-undo)\n");
fflush(eventChannel);
return self;
}
- getDimensions: (int *)linesPtr : (int *) colsPtr
{
*linesPtr = lines;
*colsPtr = cols;
return self;
}
- (Font *)getDisplayFont:
(int *) heightPtr :
(int *) widthPtr :
(int *) descenderPtr
{
*heightPtr = fontHeight;
*widthPtr = fontWidth;
*descenderPtr = fontDescender;
return displayFont;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.