diff --git a/examples/nanoxterm/Kconfig b/examples/nanoxterm/Kconfig new file mode 100644 index 000000000..d608de2c5 --- /dev/null +++ b/examples/nanoxterm/Kconfig @@ -0,0 +1,29 @@ +config EXAMPLES_NANOXTERM + tristate "Nano-X terminal emulator example" + default n + depends on MICROWINDOWS_NANOX && PSEUDOTERM && LIBC_EXECFUNCS + ---help--- + Enable the Nano-X terminal emulator example application. It is + a port of the nxterm demo from Microwindows. It starts the + Nano-X server, connects to it and creates a + terminal window that runs an NSH shell instance on a pseudo + terminal. + +if EXAMPLES_NANOXTERM + +config EXAMPLES_NANOXTERM_PROGNAME + string "Program name" + default "nanoxterm" + ---help--- + This is the name of the program that will be used when the NSH ELF + program is installed. + +config EXAMPLES_NANOXTERM_PRIORITY + int "Nano-X terminal emulator task priority" + default 100 + +config EXAMPLES_NANOXTERM_STACKSIZE + int "Nano-X terminal emulator stack size" + default 16384 + +endif diff --git a/examples/nanoxterm/Make.defs b/examples/nanoxterm/Make.defs new file mode 100644 index 000000000..41807c0b5 --- /dev/null +++ b/examples/nanoxterm/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/examples/nanoxterm/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_EXAMPLES_NANOXTERM),) +CONFIGURED_APPS += $(APPDIR)/examples/nanoxterm +endif diff --git a/examples/nanoxterm/Makefile b/examples/nanoxterm/Makefile new file mode 100644 index 000000000..19d45941d --- /dev/null +++ b/examples/nanoxterm/Makefile @@ -0,0 +1,37 @@ +############################################################################ +# apps/examples/nanoxterm/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +PROGNAME = $(CONFIG_EXAMPLES_NANOXTERM_PROGNAME) +PRIORITY = $(CONFIG_EXAMPLES_NANOXTERM_PRIORITY) +STACKSIZE = $(CONFIG_EXAMPLES_NANOXTERM_STACKSIZE) +MODULE = $(CONFIG_EXAMPLES_NANOXTERM) + +# Port of the upstream nxterm demo (src/demos/nanox/nxterm.c). On NuttX +# the pty child runs an NSH instance (nsh_consolemain) instead of exec'ing +# /bin/sh, since flat builds have no filesystem binaries. +MAINSRC = nxterm.c +CFLAGS += -Wno-undef + + +include $(APPDIR)/Application.mk diff --git a/examples/nanoxterm/nxterm.c b/examples/nanoxterm/nxterm.c new file mode 100644 index 000000000..636c98ff8 --- /dev/null +++ b/examples/nanoxterm/nxterm.c @@ -0,0 +1,2591 @@ +/**************************************************************************** + * apps/examples/nanoxterm/nxterm.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * nxterm - terminal emulator for Nano-X + * + * Ported from the Microwindows nxterm demo (src/demos/nanox/nxterm.c): + * + * (C) 1994,95,96 by Torsten Scherer (TeSche) + * itschere@techfak.uni-bielefeld.de + * + * - quite some changes for W1R1 + * - yet more changes for W1R2 + * + * TeSche 01/96: + * - supports W_ICON & W_CLOSE + * - supports /etc/utmp logging for SunOS4 and Linux + * - supports catching of console output for SunOS4 + * Phx 02-06/96: + * - supports NetBSD-Amiga + * Eero 11/97: + * - unsetenv(DISPLAY), setenv(LINES, COLUMNS). + * - Add new text modes (you need to use terminfo...). + * Eero 2/98: + * - Implemented fg/bgcolor setting. With monochrome server it changes + * bgmode variable, which tells in which mode to draw to screen + * (M_CLEAR/M_DRAW) and affects F_REVERSE settings. + * - Added a couple of checks. + * 1/23/10 ghaerr + * - added support for UNIX98 ptys (Linux default) + * - added ngterm terminal type and environment variable + * + * TODO: + * - Allocate and set sensible window palette for fg/bg color setting. + * - add scroll-region ('cs') command. Fairly many programs + * can take advantage of that. + * - Add xterm like mouse event to terminfo key event conversion... :) + * + * Georg 16th Nov 2013: + * - Added ANSI emulation with color support and scrolling region support + * tested the emulation with the Nano editor. + * made ANSI the default, select vt52 with -5 command line switch + * - 8th Dec 2013: improved ANSI emulation and Nano support + * - 15th Dec 2013: added reading program from command line for Linux + * use double quotes when calling from a script e.g.: + * bin/nano-X & bin/nxterm "ls -l *.sh >test.log" & sleep 10000 + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MWINCLUDECOLORS +#include "nano-X.h" +#include "uni_std.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +#define GR_COLOR_WHITESMOKE MWRGB(245, 245, 245) +#define GR_COLOR_GAINSBORO MWRGB(220, 220, 220) +#define GR_COLOR_ANTIQUEWHITE MWRGB(250, 235, 215) +#define GR_COLOR_BLANCHEDALMOND MWRGB(255, 235, 205) +#define GR_COLOR_LAVENDER MWRGB(230, 230, 250) +#define GR_COLOR_WHITE MWRGB(255, 255, 255) +#define _XOPEN_SOURCE 600 +#define stdcol 80 +#define stdrow 50 +#define KBDBUF 10240 +#define TITLE "nxterm" +#define stdforeground BLACK +#define stdbackground LTGRAY +#define LINEBUF stdcol +#define fonh fi.height +#define fonw fi.maxwidth +#define ANSI 0 +#define VT52 1 +#define _ ((unsigned)0) +#define X ((unsigned)1) +#define MASK7(a, b, c, d, e, f, \ + g) (((((((((((((a * 2) + b) * 2) + c) * 2) + d) * 2) + e) * \ + 2) + f) * 2) + g) << 9) +/* NuttX has no filesystem shell binary, so the pty child runs an NSH + * instance directly instead of exec'ing /bin/sh. + */ + +extern int nsh_consolemain(int argc, char *argv[]); + +/* NuttX fork() does not preserve the child's stack-local variables (the + * child may only exec/_exit per POSIX), so the pty name must live in + * static storage to be readable by the forked child. + */ + +static char ptyname[50]; + +/* globals + */ + +GR_WINDOW_ID w1; +GR_GC_ID gc1; +GR_FONT_ID regFont; + +/* GR_FONT_ID boldFont; */ + +GR_SCREEN_INFO si; +GR_FONT_INFO fi; + +GR_WINDOW_INFO wi; +GR_GC_INFO gi; +GR_BOOL havefocus = GR_FALSE; +pid_t pid; +short winw; +short winh; +int termfd; +int visualbell; +int fgcolor[12] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11 +}; + +int bgcolor[12] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11 +}; + +int scrolledFlag; + +int scrolltop; +int scrollbottom; +int ReverseMode = 0; +int semicolonflag = 0; +int nobracket = 0; +int roundbracket = 0; +int savex; +int savey; +char *startprogram; + +/* the terminal code, almost like VT52 */ + +int termtype = ANSI; +int bgmode; +int escstate; +int curx; +int cury; +int curon; +int curvis; +int saved_x; +int saved_y; +int wrap; +int style; +int col; +int row; +int colmask = 0x7f; +int rowmask = 0x7f; +int sbufcnt = 0; +int sbufx; +int sbufy; +char lineBuffer[LINEBUF + 1]; +char *sbuf = lineBuffer; + +int term_init(void); +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +void sflush(void); +void lineRedraw(void); +void sadd(char c); +void show_cursor(void); +void draw_cursor(void); +void hide_cursor(void); +void vscrollup(int lines); +void vscrolldown(int lines); +void esc5(unsigned char c); +void esc4(unsigned char c); +void esc3(unsigned char c); +void esc2(unsigned char c); +void esc1(unsigned char c); +void esc0(unsigned char c); +void esc100(unsigned char c); +void printc(unsigned char c); +void init(void); +void term(void); +void usage(void); +int do_special_key(unsigned char *buffer, int key, int modifiers); +int do_special_key_ansi(unsigned char *buffer, int key, int modifiers); +void pos_xaxis(int c); +void pos_yaxis(int c); +void rendition(int escvalue); + +/* **************************************************************************/ + +void sflush(void) +{ + if (sbufcnt) + { + GrText(w1, gc1, sbufx * fonw, sbufy * fonh, sbuf, sbufcnt, GR_TFTOP); + sbufcnt = 0; + } +} + +void lineRedraw(void) +{ + GrSetGCForeground(gc1, gi.background); + GrFillRect(w1, gc1, curx * fonw, cury * fonh, (col - curx) * fonw, fonh); + GrSetGCForeground(gc1, gi.foreground); + + if (sbufcnt) + { + sbuf[sbufcnt] = 0; + GrText(w1, gc1, sbufx * fonw, sbufy * fonh, sbuf, sbufcnt, GR_TFTOP); + } +} + +void sadd(char c) +{ + if (sbufcnt == LINEBUF) + { + sflush(); + } + + if (!sbufcnt) + { + sbufx = curx; + sbufy = cury; + } + + sbuf[sbufcnt++] = c; +} + +void show_cursor(void) +{ + GrSetGCMode(gc1, GR_MODE_XOR); + GrSetGCForeground(gc1, WHITE); + GrFillRect(w1, gc1, curx * fonw, cury * fonh + 1, fonw, fonh - 1); + GrSetGCForeground(gc1, gi.foreground); + GrSetGCMode(gc1, GR_MODE_COPY); +} + +void draw_cursor(void) +{ + if (curon) + { + if (!curvis) + { + curvis = 1; + show_cursor(); + } + } +} + +void hide_cursor(void) +{ + if (curvis) + { + curvis = 0; + show_cursor(); + } +} + +/* VVV */ + +void vscrollup(int lines) +{ + hide_cursor(); + GrCopyArea(w1, gc1, 0, scrolltop * fonh, winw, + (scrollbottom - (scrolltop - 1) - lines - 1) * fonh, w1, 0, + (scrolltop + lines) * fonh, MWROP_COPY); + GrSetGCForeground(gc1, gi.background); + GrFillRect(w1, gc1, 0, (scrollbottom - lines) * fonh, winw, lines * fonh); + GrSetGCForeground(gc1, gi.foreground); +} + +void vscrolldown(int lines) +{ + hide_cursor(); + + /* FIXME add for loop */ + + GrCopyArea(w1, gc1, 0, (scrolltop + lines) * fonh, winw, + (scrollbottom - scrolltop - lines) * fonh, w1, 0, scrolltop * fonh, + MWROP_COPY); + GrSetGCForeground(gc1, gi.background); + GrFillRect(w1, gc1, 0, scrolltop * fonh, winw, lines * fonh); + GrSetGCForeground(gc1, gi.foreground); +} + +void esc5(unsigned char c) +{ + GrSetGCBackground(gc1, c); + GrGetGCInfo(gc1, &gi); + escstate = 0; +} + +void esc4(unsigned char c) +{ + GrSetGCForeground(gc1, c); + GrGetGCInfo(gc1, &gi); + escstate = 0; +} + +void esc3(unsigned char c) +{ + curx = (c - 32) & colmask; + if (curx >= col) + { + curx = col - 1; + } + else if (curx < 0) + { + curx = 0; + } + + escstate = 0; +} + +void esc2(unsigned char c) +{ + cury = (c - 32) & rowmask; + if (cury >= row) + { + cury = row - 1; + } + else if (cury < 0) + { + cury = 0; + } + + escstate = 3; +} + +void esc1(unsigned char c) +{ + escstate = 0; + + /* detect ANSI / VT100 codes */ + + if (c == '[') + { + escstate = 10; + return; + } + + if (c == '(') + { + escstate = 10; + roundbracket = 1; + return; + } + + if (termtype == ANSI) + { + /* no bracket code - just ESC+letter + * so read no further char just do esc100 and terminate + * ESC state to read new sequence or unescaped chars. + */ + + nobracket = 1; + esc100(c); + escstate = 0; + return; + } + + /* now vt52 codes */ + + switch (c) + { + case 'A': + { + hide_cursor(); + if ((cury -= 1) < 0) + { + cury = 0; + } + break; + } + + case 'B': + { + hide_cursor(); + if ((cury += 1) >= row) + { + cury = row - 1; + } + break; + } + + case 'C': + { + hide_cursor(); + if ((curx += 1) >= col) + { + curx = col - 1; + } + break; + } + + case 'D': + { + hide_cursor(); + if ((curx -= 1) < 0) + { + curx = 0; + } + break; + } + + case 'E': + { + GrClearWindow(w1, 0); + curx = 0; + cury = 0; + break; + } + + case 'H': + { + curx = 0; + cury = 0; + break; + } + + case 'I': + { + scrolledFlag = 1; + if ((cury -= 1) < 0) + { + cury = 0; + vscrollup(1); + } + break; + } + + case 'J': + { + if (cury < row - 1) + { + GrSetGCForeground(gc1, gi.background); + GrFillRect(w1, gc1, 0, (cury + 1) * fonh, winw, + (row - 1 - cury) * fonh); + GrSetGCForeground(gc1, gi.foreground); + } + + GrSetGCForeground(gc1, gi.background); + GrFillRect(w1, gc1, curx * fonw, cury * fonh, (col - curx) * fonw, + fonh); + GrSetGCForeground(gc1, gi.foreground); + break; + } + + case 'K': + { + GrSetGCForeground(gc1, gi.background); + GrFillRect(w1, gc1, curx * fonw, cury * fonh, (col - curx) * fonw, + fonh); + GrSetGCForeground(gc1, gi.foreground); + break; + } + + case 'L': + { + if (cury < row - 1) + { + vscrollup(1); + } + + curx = 0; + break; + } + + case 'M': + { + if (cury < row - 1) + { + vscrollup(1); + } + + curx = 0; + break; + } + + case 'Y': + { + escstate = 2; + break; + } + + case 'b': + { + escstate = 4; + break; + } + + case 'c': + { + escstate = 5; + break; + } + + case 'd': + + { + if (cury > 0) + { + GrSetGCForeground(gc1, gi.background); + GrFillRect(w1, gc1, 0, 0, winw, cury * fonh); + GrSetGCForeground(gc1, gi.foreground); + } + + if (curx > 0) + { + GrSetGCForeground(gc1, gi.background); + GrFillRect(w1, gc1, 0, cury * fonh, curx * fonw, fonh); + GrSetGCForeground(gc1, gi.foreground); + } + break; + } + + case 'e': + { + curon = 1; + break; + } + + case 'f': + { + curon = 0; + break; + } + + case 'j': + { + saved_x = curx; + saved_y = cury; + break; + } + + case 'k': + { + curx = saved_x; + cury = saved_y; + break; + } + + case 'l': + { + GrSetGCForeground(gc1, gi.background); + GrFillRect(w1, gc1, 0, cury * fonh, winw, fonh); + GrSetGCForeground(gc1, gi.foreground); + curx = 0; + break; + } + + case 'o': + { + if (curx > 0) + { + GrSetGCForeground(gc1, gi.background); + GrFillRect(w1, gc1, 0, cury * fonh, curx * fonw, fonh); + GrSetGCForeground(gc1, gi.foreground); + } + break; + } + + case 'p': + { + if (!ReverseMode) + { + GrSetGCForeground(gc1, gi.background); + GrSetGCBackground(gc1, gi.foreground); + ReverseMode = 1; + } + break; + } + + case 'q': + { + if (ReverseMode) + { + GrSetGCForeground(gc1, gi.foreground); + GrSetGCBackground(gc1, gi.background); + ReverseMode = 0; + } + break; + } + + case 'v': + { + wrap = 1; + break; + } + + case 'w': + { + wrap = 0; + break; + } + + /* and these are the extensions not in VT52 */ + + case 'G': + { + break; + } + + case 'g': + + /* GrSetGCFont(gc1, boldFont); */ + + { + break; + } + + case 'h': + + { + break; + } + + case 'i': + { + break; + } + + /* j, k and l are already used */ + + case 'm': + { + break; + } + + /* these ones aren't yet on the termcap entries */ + + case 'n': + { + break; + } + + /* o, p and q are already used */ + + case 'r': + { + break; + } + + case 's': + { + break; + } + + case 't': + { + break; + } + + default: + { + break; + } + } +} + +void pos_xaxis(int c) +{ + curx = c & colmask; + if (curx >= col) + { + curx = col - 1; + } + else if (curx < 0) + { + curx = 0; + } +} + +void pos_yaxis(int c) +{ + cury = c & rowmask; + + if (cury >= scrollbottom) + { + cury = scrollbottom - 1; + } + else if (cury < scrolltop) + { + cury = scrolltop - 1; + } +} + +void rendition(int escvalue) +{ + if (escvalue == 0) + { + GrSetGCForeground(gc1, stdforeground); + GrSetGCBackground(gc1, stdbackground); + ReverseMode = 0; + } + else if (escvalue == 7) + { + if (!ReverseMode) + { + GrSetGCForeground(gc1, gi.background); + GrSetGCBackground(gc1, gi.foreground); + ReverseMode = 1; + } + } + else if (escvalue == 27) + { + if (ReverseMode) + { + GrSetGCForeground(gc1, gi.foreground); + GrSetGCBackground(gc1, gi.background); + ReverseMode = 0; + } + } + else if ((escvalue > 29) && (escvalue < 38)) + { + switch (escvalue) + { + case 30: + { + GrSetGCForeground(gc1, BLACK); + break; + } + + case 31: + { + GrSetGCForeground(gc1, RED); + break; + } + + case 32: + { + GrSetGCForeground(gc1, GREEN); + break; + } + + case 33: + { + GrSetGCForeground(gc1, BROWN); + break; + } + + case 34: + { + GrSetGCForeground(gc1, BLUE); + break; + } + + case 35: + { + GrSetGCForeground(gc1, MAGENTA); + break; + } + + case 36: + { + GrSetGCForeground(gc1, CYAN); + break; + } + + case 37: + { + GrSetGCForeground(gc1, WHITE); + break; + } + + case 39: + { + GrSetGCForeground(gc1, stdforeground); + break; + } + } + } + else if ((escvalue > 39) && (escvalue < 49)) + { + switch (escvalue) + { + case 40: + { + GrSetGCBackground(gc1, BLACK); + break; + } + + case 41: + { + GrSetGCBackground(gc1, RED); + break; + } + + case 42: + { + GrSetGCBackground(gc1, GREEN); + break; + } + + case 43: + { + GrSetGCBackground(gc1, BROWN); + break; + } + + case 44: + { + GrSetGCBackground(gc1, BLUE); + break; + } + + case 45: + { + GrSetGCBackground(gc1, MAGENTA); + break; + } + + case 46: + { + GrSetGCBackground(gc1, CYAN); + break; + } + + case 47: + { + GrSetGCBackground(gc1, WHITE); + break; + } + + case 49: + { + GrSetGCBackground(gc1, stdbackground); + break; + } + } + } +} + +void esc100(unsigned char c) +{ + int y; + int yy; + char buf[32]; + + /* leave escstate=10 till done. This states gets this function called. */ + + static int escvalue1; + static int escvalue2; + static int escvalue3; + static char valuebuffer[3]; + + valuebuffer[2] = '\0'; + + if (c == '?') + { + return; + } + + if (nobracket == 1) + { + /* fall through */ + } + else if (roundbracket == 1) + + /* fall through */ + + { + } + else if ((c > 0x2F) && (c < ':')) + { + if (valuebuffer[0] != '\0') + { + valuebuffer[1] = c; + } + else + { + valuebuffer[0] = c; + } + + return; + } + else if (c == ';') + { + if (semicolonflag == 0) + { + escvalue1 = atoi(valuebuffer); + semicolonflag++; + valuebuffer[0] = '\0'; + valuebuffer[1] = '\0'; + return; + } + else if (semicolonflag == 1) + { + escvalue2 = atoi(valuebuffer); + semicolonflag++; + valuebuffer[0] = '\0'; + valuebuffer[1] = '\0'; + return; + } + } + else if (((c > '@') && (c < '[')) || ((c > 0x60) && (c < '{'))) /* is it a + * letter? */ + { + if (semicolonflag == 0) + { + escvalue1 = atoi(valuebuffer); + escvalue2 = 0; + escvalue3 = 0; + } + else if (semicolonflag == 1) + { + escvalue2 = atoi(valuebuffer); + escvalue3 = 0; + } + else if (semicolonflag == 2) + { + escvalue3 = atoi(valuebuffer); + } + + escstate = 0; + valuebuffer[0] = '\0'; + valuebuffer[1] = '\0'; + } + else + { + return; + } + + /* fall through now if letter received */ + + /* now interpret the ESC sequence */ + +/* the cursor positions: cury,curx are zero based, so 0,0 is home position + * also if command asks to position to 5 this has to be 4 + * y is the row/line position, x is the column position + * fonh = fi.height = height of character or line in pixel + * fonw = fi.maxwidth = width of character in pixel + * winw = width of line in pixel + * winh = height of screen in pixel + * col = number of columns for current screen width + * row = number of lines for current screen height + * scrolltop, scrollbottom = upper and lower scroll region limit in + * lines/rows + */ + + if (nobracket == 1) + { + if (c == '8') + + /* HOME will reduce by one below, so add here! */ + + { + escvalue1 = savey + 1; + escvalue2 = savex + 1; + c = 'H'; + } + else if (c == '7') + { + savex = curx; + savey = cury; + c = '!'; + } + else if (c == 'M') /* reverse index, same as cursor up but scroll + * display at top */ + { + if (cury <= scrolltop) + { + escvalue1 = 1; + c = 'L'; + } + else + { + cury--; + pos_yaxis(cury); + c = '!'; + } + } + else if (c == 'D') /* index, same as cursor down but scroll display at + * bottom */ + { + if ((cury + 1) > scrollbottom) + { + escvalue1 = 1; + c = 'M'; + } + else + { + cury++; + pos_yaxis(cury); + c = '!'; + } + } + } + + nobracket = 0; + + if (roundbracket == 1) + { + c = 0; + roundbracket = 0; + } + + switch (c) + { + case 'A': + { + if (escvalue1 == 0) + { + escvalue1 = 1; + } + + hide_cursor(); + if ((cury -= escvalue1) < 0) + { + /* cury = 0; */ + + cury = scrolltop - 1; + } + break; + } + + case 'B': + { + if (escvalue1 == 0) + { + escvalue1 = 1; + } + + hide_cursor(); + if ((cury += escvalue1) >= row) + { + /* cury = row - 1; */ + + cury = scrollbottom - 1; + } + break; + } + + case 'C': + { + if (escvalue1 == 0) + { + escvalue1 = 1; + } + + hide_cursor(); + if ((curx += escvalue1) >= col) + { + curx = col - 1; + } + break; + } + + case 'D': + { + if (escvalue1 == 0) + { + escvalue1 = 1; + } + + hide_cursor(); + if ((curx -= escvalue1) < 0) + { + curx = 0; + } + break; + } + + case 'J': + { + if (escvalue1 == 0) /* erase from current cursor to end + * of page/scrollbottom */ + { + if (cury < scrollbottom - 1) + { + GrSetGCForeground(gc1, gi.background); + GrFillRect(w1, gc1, 0, (cury + 1) * fonh, winw, + (scrollbottom - 1 - cury) * fonh); + GrSetGCForeground(gc1, gi.foreground); + } + + GrSetGCForeground(gc1, gi.background); + GrFillRect(w1, gc1, curx * fonw, cury * fonh, + (col - curx) * fonw, fonh); + GrSetGCForeground(gc1, gi.foreground); + break; + } + else if (escvalue1 == 1) /* erase from home/scrolltop to + * cursor */ + { + if (cury < scrollbottom - 1) /* erase area from top to line + * above current line */ + { + GrSetGCForeground(gc1, gi.background); + GrFillRect(w1, gc1, 0, scrolltop, winw, (cury + 1) * fonh); + GrSetGCForeground(gc1, gi.foreground); + } + + GrSetGCForeground(gc1, gi.background); + GrFillRect(w1, gc1, 0, cury * fonh, curx * fonw, fonh); + GrSetGCForeground(gc1, gi.foreground); + break; + } + else if (escvalue1 == 2) /* erase entire page - leave cursor + * untouched */ + + /* GrClearWindow(w1, 0); + * erase just the scrolling area + */ + + { + GrSetGCForeground(gc1, gi.background); + GrFillRect(w1, gc1, 0, scrolltop, winw, (scrollbottom) * fonh); + GrSetGCForeground(gc1, gi.foreground); + break; + } + } + + case 'K': + { + if (escvalue1 == 0) + { + GrSetGCForeground(gc1, gi.background); + GrFillRect(w1, gc1, curx * fonw, cury * fonh, + (col - curx) * fonw, fonh); + GrSetGCForeground(gc1, gi.foreground); + break; + } + else if (escvalue1 == 1) + { + GrSetGCForeground(gc1, gi.background); + GrFillRect(w1, gc1, 0, cury * fonh, curx * fonw, fonh); + GrSetGCForeground(gc1, gi.foreground); + break; + } + else if (escvalue1 == 2) /* erase entire line - leave cursor + * untouched */ + { + GrSetGCForeground(gc1, gi.background); + GrFillRect(w1, gc1, 0, cury * fonh, winw, fonh); + GrSetGCForeground(gc1, gi.foreground); + break; + } + } + + case 'P':/* erase number of characters after and including the cursor + * and move remaining to this position */ + { + if (escvalue1 == 0) + { + escvalue1 = 1; + } + + GrSetGCForeground(gc1, gi.background); + + /* copy remaining chars on line to cursor position */ + + GrCopyArea(w1, gc1, curx * fonw, cury * fonh, + (col - curx - escvalue1) * fonw, fonh, w1, + (curx + escvalue1) * fonw, cury * fonh, MWROP_COPY); + + /* clear space at end of line */ + + GrFillRect(w1, gc1, (col - escvalue1) * fonw, cury * fonh, + (escvalue1) * fonw, fonh); + GrSetGCForeground(gc1, gi.foreground); + break; + } + + case 'L': + { + if (escvalue1 == 0) + { + escvalue1 = 1; + } + + y = cury; + yy = stdrow - 1; + + /* XXX */ + + hide_cursor(); + + /* copy from cursor the number of lines down */ + + while (--yy >= y) + { + GrCopyArea(w1, gc1, 0, (yy + escvalue1) * fonh, winw - 0, fonh, + w1, 0, yy * fonh, MWROP_COPY); + } + + /* clear number of lines starting at cursor position */ + + GrSetGCForeground(gc1, gi.background); + GrFillRect(w1, gc1, 0, cury * fonh, winw, escvalue1 * fonh); + GrSetGCForeground(gc1, gi.foreground); + break; + } + + case 'M': + + { + if (escvalue1 == 0) + { + escvalue1 = 1; + } + + GrCopyArea(w1, gc1, 0, cury * fonh, winw, + (scrollbottom - cury - escvalue1) * fonh, w1, 0, + (cury + escvalue1) * fonh, MWROP_COPY); + + /* clear number of lines starting from scrollbottom up */ + + GrSetGCForeground(gc1, gi.background); + GrFillRect(w1, gc1, 0, (scrollbottom - escvalue1) * fonh, winw, + escvalue1 * fonh); + GrSetGCForeground(gc1, gi.foreground); + break; + } + + case 'S': + { + if (escvalue1 == 0) + { + escvalue1 = 1; + } + + vscrollup(escvalue1); + break; + } + + case 'T': + { + if (escvalue1 == 0) + { + escvalue1 = 1; + } + + vscrolldown(escvalue1); + break; + } + + case 'H': + case 'f': + { + if (escvalue1 > 0) + { + escvalue1--; + } + + if (escvalue2 > 0) + { + escvalue2--; + } + + pos_yaxis(escvalue1); + pos_xaxis(escvalue2); + break; + } + + case 'E': + { + if (escvalue1 == 0) + { + escvalue1 = 1; + } + + hide_cursor(); + if ((cury += escvalue1) >= scrollbottom) + { + cury = scrollbottom - 1; + } + + curx = 0; + break; + } + + case 'F': + { + if (escvalue1 == 0) + { + escvalue1 = 1; + } + + hide_cursor(); + if ((cury -= escvalue1) < 0) + { + cury = 0; + } + + curx = 0; + break; + } + + case 'd': + { + if (escvalue1 > 0) + { + escvalue1--; + } + + pos_yaxis(escvalue1); + break; + } + + case '`': + case 'G': + { + if (escvalue1 > 0) + { + escvalue1--; + } + + pos_xaxis(escvalue1); + break; + } + + case 'e': + { + if (escvalue1 > 0) + { + escvalue1--; + } + + pos_yaxis(escvalue1 + cury); + break; + } + + case 'a': + { + if (escvalue1 > 0) + { + escvalue1--; + } + + pos_xaxis(escvalue1 + curx); + break; + } + + case 'm': + + /* may be more values, do just up to three here + * foreground colors run from 30 to 37, background from 40 to 47 + */ + + { + rendition(escvalue1); + if (escvalue2 != 0) + { + rendition(escvalue2); + } + + if (escvalue3 != 0) + { + rendition(escvalue3); + } + + escvalue1 = 0; + escvalue2 = 0; + escvalue3 = 0; + GrGetGCInfo(gc1, &gi); + break; + } + + case 's': + { + saved_x = curx; + saved_y = cury; + break; + } + + case 'u': + { + curx = saved_x; + cury = saved_y; + break; + } + + case 'r': + { + if ((escvalue1 > -1) && (escvalue2 <= (winh - 1))) + { + scrolltop = escvalue1; + scrollbottom = escvalue2; + } + break; + } + + case 'h': + { + if (escvalue1 == 7) + { + wrap = 1; + } + + if (escvalue1 == 25) + { + show_cursor(); + } + break; + } + + case 'l': + { + if (escvalue1 == 7) + { + wrap = 0; + } + + if (escvalue1 == 25) + { + hide_cursor(); + } + break; + } + + case 'n': + { + sprintf(buf, "\033[%d;%dR", cury + 1, curx + 1); + write(termfd, buf, strlen(buf)); + break; + } + + default: + { + break; + } + } +} + +/* un-escaped character print routine + */ + +void esc0(unsigned char c) +{ + switch (c) + { + case 0: + { + /* printing \000 on a terminal means "do nothing". + * But since we use \000 as string terminator none + * of the characters that follow were printed. + * + * perl -e 'printf("a%ca", 0);' + * + * said 'a' in a wterm, but should say 'aa'. This + * bug screwed up most ncurses programs. + */ + + break; + } + + case 7: + { + if (visualbell) + { + /* w_setmode(win, M_INVERS); + * w_pbox(win, 0, 0, winw, winh); + * w_test(win, 0, 0); + * w_pbox(win, 0, 0, winw, winh); + */ + } + else + { + GrBell(); + } + break; + } + + case 8: + { + hide_cursor(); + if ((curx -= 1) < 0) + { + curx = 0; + } + + pos_xaxis(curx); + break; + } + + case 9: + { + int borg; + int i; + + borg = (((curx >> 3) + 1) << 3); + if (borg >= col) + { + borg = col - 1; + } + + borg = borg - curx; + for (i = 0; i < borg; ++i) + { + sadd(' '); + } + + if ((curx = ((curx >> 3) + 1) << 3) >= col) + { + curx = col - 1; + } + + pos_xaxis(curx); + } + break; + + case 10: + { + sflush(); + if (++cury >= scrollbottom) + { + /* scroll before moving cursor, then reduce and add again */ + + cury--; + vscrollup(1); + cury++; + + /* set cursor into lowest line (cury zero based so -1) */ + + cury = scrollbottom - 1; + + /* cury = row-1; */ + } + + pos_yaxis(cury); + break; + } + + case 13: + { + sflush(); + curx = 0; + pos_xaxis(curx); + break; + } + + case 27: + { + sflush(); + semicolonflag = 0; + escstate = 1; + break; + } + + case 127: + { + break; + } + + default: + { + sadd(c); + if (++curx >= col) + { + sflush(); + if (!wrap) + { + curx = col - 1; + } + else + { + curx = 0; + if (++cury >= scrollbottom) + { + vscrollup(1); + } + } + } + break; + } + } +} + +void printc(unsigned char c) +{ + switch (escstate) + { + case 0: + { + esc0(c); + break; + } + + case 1: + { + sflush(); + esc1(c); + break; + } + + case 2: + { + sflush(); + esc2(c); + break; + } + + case 3: + { + sflush(); + esc3(c); + break; + } + + case 4: + { + sflush(); + esc4(c); + break; + } + + case 5: + { + sflush(); + esc5(c); + break; + } + + case 10: + { + sflush(); + esc100(c); + break; + } + + default: + { + escstate = 0; + break; + } + } +} + +void init(void) +{ + curx = saved_x = 0; + cury = saved_y = 0; + wrap = 1; + curon = 1; + curvis = 0; + escstate = 0; +} + +/* general code... + */ + +void term(void) +{ + long in; + long l; + GR_EVENT_KEYSTROKE *kp; + int bufflen; + int gotexpose = 0; + GR_EVENT wevent; + unsigned char buf[KBDBUF]; + + if (startprogram) + { + usleep(1000); + write(termfd, startprogram, strlen(startprogram)); + write(termfd, "\r", 1); + } + + while (42) + { + if (havefocus) + { + draw_cursor(); + } + + GrGetNextEvent(&wevent); + + switch (wevent.type) + { + case GR_EVENT_TYPE_CLOSE_REQ: + { + GrClose(); + exit(0); + break; + } + + case GR_EVENT_TYPE_KEY_DOWN: + { + /* deal with special keys */ + + kp = (GR_EVENT_KEYSTROKE *)&wevent; + if (kp->ch & MWKEY_NONASCII_MASK) + { + if (termtype == ANSI) + { + bufflen = do_special_key_ansi(buf, kp->ch, + kp->modifiers); + } + else + { + bufflen = do_special_key(buf, kp->ch, kp->modifiers); + } + } + else + { + *buf = kp->ch & 0xff; + bufflen = 1; + } + + if (bufflen > 0) + { + write(termfd, buf, bufflen); + } + break; + } + + case GR_EVENT_TYPE_FOCUS_IN: + { + havefocus = GR_TRUE; + break; + } + + case GR_EVENT_TYPE_FOCUS_OUT: + { + havefocus = GR_FALSE; + hide_cursor(); + break; + } + + case GR_EVENT_TYPE_UPDATE: + { + /* if we get temporarily unmapped (moved), + * set cursor state off. + */ + + if (wevent.update.utype == GR_UPDATE_UNMAPTEMP) + { + hide_cursor(); + } + break; + } + + case GR_EVENT_TYPE_EXPOSURE: + { + if (!gotexpose) + { + GrRegisterInput(termfd); + gotexpose = GR_TRUE; + } + break; + } + + case GR_EVENT_TYPE_FDINPUT: + { + if (!gotexpose) + { + break; + } + + hide_cursor(); + if ((in = read(termfd, buf, sizeof(buf))) > 0) + { + for (l = 0; l < in; l++) + { + printc(buf[l]); + } + + sflush(); + } + break; + } + + case GR_EVENT_TYPE_NONE: + case GR_EVENT_TYPE_TIMEOUT: + { + break; + } + + default: + { + hide_cursor(); + break; + } + } + } +} + +int do_special_key(unsigned char *buffer, int key, int modifier) +{ + /* handle vt52 keys here */ + + int len; + char *str; + char locbuff[32]; + + switch (key) + { + case MWKEY_LEFT: + { + str = "\033D"; + len = 2; + break; + } + + case MWKEY_RIGHT: + { + str = "\033C"; + len = 2; + break; + } + + case MWKEY_UP: + { + if (scrolledFlag) + { + str = ""; + len = 0; + scrolledFlag = 0; + } + else + { + str = "\033A"; + len = 2; + } + break; + } + + case MWKEY_DOWN: + { + str = "\033B"; + len = 2; + break; + } + + case MWKEY_KP0: + { + str = "\033\077\160"; + len = 3; + break; + } + + case MWKEY_KP1: + { + str = "\033\077\161"; + len = 3; + break; + } + + case MWKEY_KP2: + { + str = "\033\077\162"; + len = 3; + break; + } + + case MWKEY_KP3: + { + str = "\033\077\163"; + len = 3; + break; + } + + case MWKEY_KP4: + { + str = "\033\077\164"; + len = 3; + break; + } + + case MWKEY_KP5: + { + str = "\033\077\165"; + len = 3; + break; + } + + case MWKEY_KP6: + { + str = "\033\077\166"; + len = 3; + break; + } + + case MWKEY_KP7: + { + str = "\033\077\167"; + len = 3; + break; + } + + case MWKEY_KP8: + { + str = "\033\077\170"; + len = 3; + break; + } + + case MWKEY_KP9: + { + str = "\033\077\161"; + len = 3; + break; + } + + case MWKEY_KP_PERIOD: + { + str = "\033\077\156"; + len = 3; + break; + } + + case MWKEY_KP_ENTER: + { + str = "\033\077\115"; + len = 3; + break; + } + + case MWKEY_DELETE: + { + str = "\033C\177"; + len = 3; + break; + } + + case MWKEY_F1 ... MWKEY_F12: + { + if (modifier & MWKMOD_LMETA) + { + locbuff[0] = 033; + locbuff[1] = 'c'; + locbuff[2] = (char)bgcolor[key - MWKEY_F1]; + locbuff[3] = '\0'; + str = locbuff; + len = 3; + } + else if (modifier & MWKMOD_RMETA) + { + locbuff[0] = 033; + locbuff[1] = 'b'; + locbuff[2] = (char)fgcolor[key - MWKEY_F1]; + locbuff[3] = '\0'; + str = locbuff; + len = 3; + } + else + { + switch (key) + { + case MWKEY_F1: + { + str = "\033Y"; + len = 2; + break; + } + + case MWKEY_F2: + { + str = "\033P"; + len = 2; + break; + } + + case MWKEY_F3: + { + str = "\033Q"; + len = 2; + break; + } + + case MWKEY_F4: + { + str = "\033R"; + len = 2; + break; + } + + case MWKEY_F5: + { + str = "\033S"; + len = 2; + break; + } + + case MWKEY_F6: + { + str = "\033T"; + len = 2; + break; + } + + case MWKEY_F7: + { + str = "\033U"; + len = 2; + break; + } + + case MWKEY_F8: + { + str = "\033V"; + len = 2; + break; + } + + case MWKEY_F9: + { + str = "\033W"; + len = 2; + break; + } + + case MWKEY_F10: + { + str = "\033X"; + len = 2; + break; + } + } + } + } + + default: + { + len = 0; + } + } + + buffer[0] = '\0'; + if (len > 0) + { + strcpy((char *)buffer, str); + } + + return len; +} + +int do_special_key_ansi(unsigned char *buffer, int key, int modifier) +{ + int len; + char *str; + char locbuff[32]; + + switch (key) + { + case MWKEY_LEFT: + { + str = "\033[D"; + len = 3; + break; + } + + case MWKEY_RIGHT: + { + str = "\033[C"; + len = 3; + break; + } + + case MWKEY_UP: + { + if (scrolledFlag) + { + str = ""; + len = 0; + scrolledFlag = 0; + } + else + { + str = "\033[A"; + len = 3; + } + break; + } + + case MWKEY_DOWN: + { + str = "\033[B"; + len = 3; + break; + } + + case MWKEY_HOME: + { + str = "\033[H"; + len = 3; + break; + } + + case MWKEY_INSERT: + { + str = "\033[2~"; + len = 4; + break; + } + + case MWKEY_KP0: + { + str = "\033Op"; + len = 3; + break; + } + + case MWKEY_END: + { + str = "\033[F"; + len = 3; + break; + } + + case MWKEY_KP1: + { + str = "\033Oq"; + len = 3; + break; + } + + case MWKEY_KP2: + { + str = "\033Or"; + len = 3; + break; + } + + case MWKEY_PAGEDOWN: + { + str = "\033[6~"; + len = 4; + break; + } + + case MWKEY_KP3: + { + str = "\033Os"; + len = 3; + break; + } + + case MWKEY_KP4: + { + str = "\033Ot"; + len = 3; + break; + } + + case MWKEY_KP5: + { + str = "\033Ou"; + len = 3; + break; + } + + case MWKEY_KP6: + { + str = "\033Ov"; + len = 3; + break; + } + + case MWKEY_KP7: + { + str = "\033Ow"; + len = 3; + break; + } + + case MWKEY_KP8: + { + str = "\033Ox"; + len = 3; + break; + } + + case MWKEY_PAGEUP: + { + str = "\033[5~"; + len = 4; + break; + } + + case MWKEY_KP9: + { + str = "\033Oy"; + len = 3; + break; + } + +/* case MWKEY_KP_PERIOD: + * str="\033On"; + * len=3; + * break; + */ + + case MWKEY_KP_ENTER: + { + str = "\033OM"; + len = 3; + break; + } + + case MWKEY_KP_PERIOD: + case MWKEY_DELETE: + { + str = "\033[3~"; + len = 4; + break; + } + + case MWKEY_F1 ... MWKEY_F12: + { + if (modifier & MWKMOD_LMETA) + { + /* we set background color */ + + locbuff[0] = 033; + locbuff[1] = 'c'; + locbuff[2] = (char)bgcolor[key - MWKEY_F1]; + locbuff[3] = '\0'; + str = locbuff; + len = 3; + } + else if (modifier & MWKMOD_RMETA) + { + /* we set foreground color */ + + locbuff[0] = 033; + locbuff[1] = 'b'; + locbuff[2] = (char)fgcolor[key - MWKEY_F1]; + locbuff[3] = '\0'; + str = locbuff; + len = 3; + } + else + { + switch (key) + { + case MWKEY_F1: + { + str = "\033OP"; + len = 3; + break; + } + + case MWKEY_F2: + { + str = "\033OQ"; + len = 3; + break; + } + + case MWKEY_F3: + { + str = "\033OR"; + len = 3; + break; + } + + case MWKEY_F4: + { + str = "\033OS"; + len = 3; + break; + } + + case MWKEY_F5: + { + str = "\033[15~"; + len = 5; + break; + } + + case MWKEY_F6: + { + str = "\033[17~"; + len = 5; + break; + } + + case MWKEY_F7: + { + str = "\033[18~"; + len = 5; + break; + } + + case MWKEY_F8: + { + str = "\033[19~"; + len = 5; + break; + } + + case MWKEY_F9: + { + str = "\033[20~"; + len = 5; + break; + } + + case MWKEY_F10: + { + str = "\033[21~"; + len = 5; + break; + } + + case MWKEY_F11: + { + str = "\033[22~"; + len = 5; + break; + } + + case MWKEY_F12: + { + str = "\033[23~"; + len = 5; + break; + } + } + } + } + + /* fall thru */ + + default: + { + len = 0; + } + } + + buffer[0] = '\0'; + if (len > 0) + { + strcpy((char *)buffer, str); + } + + return len; +} + +void usage(void) +{ + GrError("usage: nxterm [-v5] [command]]\n"); + exit(1); +} + +static void *mysignal(int signum, void *handler) +{ + struct sigaction sa; + struct sigaction so; + + sa.sa_handler = handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sigaction(signum, &sa, &so); + + return so.sa_handler; +} + +static void sigpipe(int sig) +{ + GrClose(); + kill(-pid, SIGHUP); + _exit(sig); +} + +static void sigchild(int sig) +{ + GrClose(); + _exit(sig); +} + +static void sigquit(int sig) +{ + signal(sig, SIG_IGN); + GrClose(); + kill(-pid, SIGHUP); +} + +extern int nanox_server_main(int argc, char *argv[]); + +int main(int argc, char **argv) +{ + GR_CURSOR_ID c1; + GR_BITMAP bitmap1fg[7]; + GR_BITMAP bitmap1bg[7]; + + /* Start the Nano-X server as a separate task; GrOpen() below retries + * the connection while the server comes up. + */ + + task_create("nanoxserver", 100, 65536, nanox_server_main, NULL); + + argv++; + while (*argv && **argv == '-') + { + switch (*(*argv + 1)) + { + case '5': + { + termtype = VT52; + argv++; + break; + } + + case 'v': + { + visualbell = 1; + argv++; + break; + } + + default: + { + usage(); + } + } + } + + /* now *argv either points to a program to start or is zero + */ + + if (*argv) + { + startprogram = *argv++; + } + + if (GrOpen() < 0) + { + GrError("cannot open graphics\n"); + exit(1); + } + + GrGetScreenInfo(&si); + + col = stdcol; + row = stdrow; + scrolltop = 0; + scrollbottom = row; + + regFont = GrCreateFontEx(GR_FONT_SYSTEM_FIXED, 0, 0, NULL); + /* regFont = GrCreateFontEx(GR_FONT_OEM_FIXED, 0, 0, NULL); + *boldFont = GrCreateFontEx(GR_FONT_SYSTEM_FIXED, 0, 0, NULL); + */ + + GrGetFontInfo(regFont, &fi); + winw = col * fi.maxwidth; + winh = row * fi.height; + w1 = GrNewWindowEx(GR_WM_PROPS_APPWINDOW, TITLE, GR_ROOT_WINDOW_ID, -1, -1, + winw, winh, stdbackground); + + GrSelectEvents(w1, + GR_EVENT_MASK_BUTTON_DOWN | GR_EVENT_MASK_EXPOSURE | + GR_EVENT_MASK_KEY_DOWN | GR_EVENT_MASK_FOCUS_IN | + GR_EVENT_MASK_FOCUS_OUT | GR_EVENT_MASK_UPDATE | + GR_EVENT_MASK_CLOSE_REQ); + GrMapWindow(w1); + + gc1 = GrNewGC(); + GrSetGCFont(gc1, regFont); + + bitmap1fg[0] = MASK7(_, _, X, _, X, _, _); + bitmap1fg[1] = MASK7(_, _, _, X, _, _, _); + bitmap1fg[2] = MASK7(_, _, _, X, _, _, _); + bitmap1fg[3] = MASK7(_, _, _, X, _, _, _); + bitmap1fg[4] = MASK7(_, _, _, X, _, _, _); + bitmap1fg[5] = MASK7(_, _, _, X, _, _, _); + bitmap1fg[6] = MASK7(_, _, X, _, X, _, _); + + bitmap1bg[0] = MASK7(_, X, X, X, X, X, _); + bitmap1bg[1] = MASK7(_, _, X, X, X, _, _); + bitmap1bg[2] = MASK7(_, _, X, X, X, _, _); + bitmap1bg[3] = MASK7(_, _, X, X, X, _, _); + bitmap1bg[4] = MASK7(_, _, X, X, X, _, _); + bitmap1bg[5] = MASK7(_, _, X, X, X, _, _); + bitmap1bg[6] = MASK7(_, X, X, X, X, X, _); + + c1 = GrNewCursor(7, 7, 3, 3, stdforeground, stdbackground, bitmap1fg, + bitmap1bg); + GrSetWindowCursor(w1, c1); + GrSetGCForeground(gc1, stdforeground); + GrSetGCBackground(gc1, stdbackground); + GrGetWindowInfo(w1, &wi); + GrGetGCInfo(gc1, &gi); + + /* create pty, SIGCHLD handler set afterwards in case of grantpt() called */ + + termfd = term_init(); + if (termfd < 0) + { + GrClose(); + exit(1); + } + + mysignal(SIGTERM, sigquit); + mysignal(SIGHUP, sigquit); + mysignal(SIGQUIT, sigquit); + mysignal(SIGPIPE, sigpipe); + mysignal(SIGCHLD, sigchild); + mysignal(SIGINT, SIG_IGN); + + /* just in case we're started in the background */ + + signal(SIGTTOU, SIG_IGN); + + init(); + term(); + return 0; +} + +int term_init(void) +{ + int tfd; + int ptfd; + pid_t child; + int status; + posix_spawnattr_t attr; + posix_spawn_file_actions_t file_actions; + char *const argv[] = + { + CONFIG_SYSTEM_NSH_PROGNAME, NULL + }; + + tfd = posix_openpt(O_RDWR | O_NOCTTY | O_NONBLOCK); + if (tfd < 0) + { + GrError("Can't create pty /dev/ptmx\n"); + return -1; + } + + strcpy(ptyname, ptsname(tfd)); + + signal(SIGCHLD, SIG_DFL); + grantpt(tfd); + unlockpt(tfd); + + if ((ptfd = open(ptyname, O_RDWR)) < 0) + { + GrError("Can't open %s\n", ptyname); + return -1; + } + + posix_spawnattr_init(&attr); + posix_spawnattr_setstacksize(&attr, CONFIG_SYSTEM_NSH_STACKSIZE); + + posix_spawn_file_actions_init(&file_actions); + posix_spawn_file_actions_addclose(&file_actions, STDIN_FILENO); + posix_spawn_file_actions_addclose(&file_actions, STDOUT_FILENO); + posix_spawn_file_actions_addclose(&file_actions, STDERR_FILENO); + posix_spawn_file_actions_adddup2(&file_actions, ptfd, STDIN_FILENO); + posix_spawn_file_actions_adddup2(&file_actions, ptfd, STDOUT_FILENO); + posix_spawn_file_actions_adddup2(&file_actions, ptfd, STDERR_FILENO); + posix_spawn_file_actions_addclose(&file_actions, ptfd); + + status = posix_spawn(&child, CONFIG_SYSTEM_NSH_PROGNAME, &file_actions, + &attr, argv, environ); + + posix_spawn_file_actions_destroy(&file_actions); + close(ptfd); + + if (status != 0) + { + GrError("Error: posix_spawn returned %d\n", status); + return -1; + } + + return tfd; +}