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.