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]; pasteboard = [Pasteboard new]; 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; } /* * 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; { const char *const *p, *const *types; char *data; int len; if (!eventChannel) return self; 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; } - pasteboard { return pasteboard; } - 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.