diff --git a/Make.defs b/Make.defs
index ab79ce71cb5..2f784a7647f 100644
--- a/Make.defs
+++ b/Make.defs
@@ -130,6 +130,18 @@ endif
CFLAGS += ${INCDIR_PREFIX}"$(APPDIR)$(DELIM)include"
CXXFLAGS += ${INCDIR_PREFIX}"$(APPDIR)$(DELIM)include"
+# EXPORTED_INCLUDES lists include directories that must be available to
+# applications built out of tree from the NuttX export package. Each
+# entry is prefixed with ${INCDIR_PREFIX} (i.e. -I
) and is added
+# to CFLAGS/CXXFLAGS so that it takes effect in the in-tree build as
+# well. The content of the listed directories is copied to the include
+# directory of the export package by the export target in apps/Makefile.
+# Components should append their public include directories in their
+# Make.defs.
+
+CFLAGS += $(EXPORTED_INCLUDES)
+CXXFLAGS += $(EXPORTED_INCLUDES)
+
NUTTXLIB ?= $(call CONVERT_PATH,$(TOPDIR)$(DELIM)staging)
# SPLITVARIABLE - Split long variables into specified batches
diff --git a/Makefile b/Makefile
index 5a357053891..74cb37da32b 100644
--- a/Makefile
+++ b/Makefile
@@ -206,6 +206,13 @@ ifneq ($(BUILTIN_REGISTRY),)
done
endif
endif
+ $(Q) for inc in $(EXPORTED_INCLUDES) ; do \
+ inc=$${inc#"$(INCDIR_PREFIX)"}; \
+ if [ -d "$${inc}" ]; then \
+ mkdir -p "${EXPORTDIR}"$(DELIM)include || exit 1; \
+ cp -Rf "$${inc}"$(DELIM). "${EXPORTDIR}"$(DELIM)include || exit 1; \
+ fi \
+ done
endif
.depdirs: $(foreach SDIR, $(CONFIGURED_APPS), $(SDIR)_depend)
diff --git a/examples/nanoxcalc/Kconfig b/examples/nanoxcalc/Kconfig
new file mode 100644
index 00000000000..5b2fa079cd0
--- /dev/null
+++ b/examples/nanoxcalc/Kconfig
@@ -0,0 +1,28 @@
+config EXAMPLES_NANOXCALC
+ tristate "Nano-X calculator example"
+ default n
+ depends on MICROWINDOWS_NANOX && LIBC_FLOATINGPOINT
+ ---help---
+ Enable the Nano-X calculator example application. It is a
+ port of the nxcalc demo from Microwindows which connects to
+ the Nano-X server started by examples/nanoxterm and shows
+ a calculator keypad operated by the mouse or keyboard.
+
+if EXAMPLES_NANOXCALC
+
+config EXAMPLES_NANOXCALC_PROGNAME
+ string "Program name"
+ default "nanoxcalc"
+ ---help---
+ This is the name of the program that will be used when the NSH ELF
+ program is installed.
+
+config EXAMPLES_NANOXCALC_PRIORITY
+ int "Nano-X calculator task priority"
+ default 100
+
+config EXAMPLES_NANOXCALC_STACKSIZE
+ int "Nano-X calculator stack size"
+ default 16384
+
+endif
diff --git a/examples/nanoxcalc/Make.defs b/examples/nanoxcalc/Make.defs
new file mode 100644
index 00000000000..c3081015861
--- /dev/null
+++ b/examples/nanoxcalc/Make.defs
@@ -0,0 +1,25 @@
+############################################################################
+# apps/examples/nanoxcalc/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_NANOXCALC),)
+CONFIGURED_APPS += $(APPDIR)/examples/nanoxcalc
+endif
diff --git a/examples/nanoxcalc/Makefile b/examples/nanoxcalc/Makefile
new file mode 100644
index 00000000000..7a703a39814
--- /dev/null
+++ b/examples/nanoxcalc/Makefile
@@ -0,0 +1,35 @@
+############################################################################
+# apps/examples/nanoxcalc/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_NANOXCALC_PROGNAME)
+PRIORITY = $(CONFIG_EXAMPLES_NANOXCALC_PRIORITY)
+STACKSIZE = $(CONFIG_EXAMPLES_NANOXCALC_STACKSIZE)
+MODULE = $(CONFIG_EXAMPLES_NANOXCALC)
+
+# Port of the upstream nxcalc demo (src/demos/nanox/nxcalc.c)
+MAINSRC = nxcalc.c
+CFLAGS += -Wno-undef
+
+
+include $(APPDIR)/Application.mk
diff --git a/examples/nanoxcalc/nxcalc.c b/examples/nanoxcalc/nxcalc.c
new file mode 100644
index 00000000000..d43a741282d
--- /dev/null
+++ b/examples/nanoxcalc/nxcalc.c
@@ -0,0 +1,356 @@
+/* nxcalc - Simple graphical calculator for Microwindows / Nano-X
+ *
+ * Inspired by nxclock example (Greg Haerr).
+ *
+ * Run inside an environment where the Nano-X server is available.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include "nano-X.h"
+
+#define WIN_W 160
+#define WIN_H 200
+#define DISP_H 35
+
+static GR_WINDOW_ID win;
+static GR_GC_ID gc_text, gc_button, gc_button_press, gc_border;
+
+/* calculator state */
+static char display[128];
+static double acc = 0.0;
+static char pending_op = 0;
+static int entering = 0;
+static int dot_entered = 0;
+
+/* button layout */
+static char *btn_labels[] = {
+ "C", "<-", "+/-", "/",
+ "7", "8", "9", "*",
+ "4", "5", "6", "-",
+ "1", "2", "3", "+",
+ "0", ".", "=", NULL
+};
+
+#define COLS 4
+#define ROWS 5
+
+static GR_RECT btn_rects[ROWS][COLS];
+
+/* function prototypes */
+static void redraw(void);
+static void press_button(const char *label);
+static void compute_equal(void);
+static void apply_pending(double val);
+
+static double display_to_double(void) {
+ if (strlen(display) == 0) return 0.0;
+ return strtod(display, NULL);
+}
+
+static void set_display_from_double(double v) {
+ char buf[128];
+ double iv;
+
+ if (fabs(v) < 1e-12) v = 0.0;
+ if (modf(v, &iv) == 0.0) {
+ snprintf(buf, sizeof(buf), "%.0f", v);
+ } else {
+ snprintf(buf, sizeof(buf), "%.10g", v);
+ }
+
+ strncpy(display, buf, sizeof(display) - 1);
+ display[sizeof(display) - 1] = '\0';
+}
+
+/* ========================= MAIN ========================= */
+
+int main(int argc, char **argv) {
+ GR_EVENT event;
+ int r, c, idx;
+
+ if (GrOpen() < 0) {
+ fprintf(stderr, "nxcalc: cannot open Nano-X\n");
+ return 1;
+ }
+
+ win = GrNewWindowEx(GR_WM_PROPS_APPWINDOW, "nxcalc", GR_ROOT_WINDOW_ID,
+ 10, 10, WIN_W, WIN_H, GrGetSysColor(GR_COLOR_WINDOW));
+
+ GrSelectEvents(win, GR_EVENT_MASK_EXPOSURE | GR_EVENT_MASK_BUTTON_DOWN |
+ GR_EVENT_MASK_BUTTON_UP | GR_EVENT_MASK_CLOSE_REQ);
+
+ gc_text = GrNewGC();
+ gc_button = GrNewGC();
+ gc_button_press = GrNewGC();
+ gc_border = GrNewGC();
+
+ GrSetGCForeground(gc_text, GrGetSysColor(GR_COLOR_WINDOWTEXT));
+ GrSetGCBackground(gc_text, GrGetSysColor(GR_COLOR_WINDOW));
+
+ GrSetGCForeground(gc_button, GrGetSysColor(GR_COLOR_BTNFACE));
+ GrSetGCBackground(gc_button, GrGetSysColor(GR_COLOR_WINDOW));
+
+ GrSetGCForeground(gc_button_press, GrGetSysColor(GR_COLOR_BTNSHADOW));
+ GrSetGCBackground(gc_button_press, GrGetSysColor(GR_COLOR_WINDOW));
+
+ GrSetGCForeground(gc_border, GrGetSysColor(GR_COLOR_BTNTEXT));
+ GrSetGCBackground(gc_border, GrGetSysColor(GR_COLOR_WINDOW));
+
+ /* init state */
+ display[0] = '\0';
+ acc = 0.0;
+ pending_op = 0;
+ entering = 0;
+ dot_entered = 0;
+
+ /* layout buttons */
+ {
+ int grid_x = 12, grid_y = DISP_H + 10;
+ int grid_w = WIN_W - 20;
+ int grid_h = WIN_H - DISP_H - 20;
+ int cell_w = grid_w / COLS;
+ int cell_h = grid_h / ROWS;
+
+ idx = 0;
+ for (r = 0; r < ROWS; r++) {
+ for (c = 0; c < COLS; c++) {
+ int x = grid_x + c * cell_w;
+ int y = grid_y + r * cell_h;
+ btn_rects[r][c].x = x;
+ btn_rects[r][c].y = y;
+ btn_rects[r][c].width = cell_w - 6;
+ btn_rects[r][c].height = cell_h - 6;
+ idx++;
+ }
+ }
+ }
+
+ GrMapWindow(win);
+ set_display_from_double(0.0);
+ redraw();
+
+ while (1) {
+ GrGetNextEvent(&event);
+ switch (event.type) {
+ case GR_EVENT_TYPE_EXPOSURE:
+ redraw();
+ break;
+
+ case GR_EVENT_TYPE_BUTTON_DOWN: {
+ int mx = event.button.x;
+ int my = event.button.y;
+ int found = 0;
+ for (r = 0; r < ROWS && !found; r++) {
+ for (c = 0; c < COLS && !found; c++) {
+ GR_RECT *rc = &btn_rects[r][c];
+ if (mx >= rc->x && mx <= rc->x + rc->width &&
+ my >= rc->y && my <= rc->y + rc->height) {
+ GrFillRect(win, gc_button_press, rc->x, rc->y, rc->width, rc->height);
+ found = 1;
+ }
+ }
+ }
+ break;
+ }
+
+ case GR_EVENT_TYPE_BUTTON_UP: {
+ int mx = event.button.x;
+ int my = event.button.y;
+ int found = 0;
+ for (r = 0; r < ROWS && !found; r++) {
+ for (c = 0; c < COLS && !found; c++) {
+ GR_RECT *rc = &btn_rects[r][c];
+ if (mx >= rc->x && mx <= rc->x + rc->width &&
+ my >= rc->y && my <= rc->y + rc->height) {
+ idx = r * COLS + c;
+ const char *lbl =
+ (idx < (int)(sizeof(btn_labels)/sizeof(btn_labels[0]))
+ ? btn_labels[idx] : NULL);
+ if (lbl) {
+ press_button(lbl);
+ redraw();
+ }
+ found = 1;
+ }
+ }
+ }
+ break;
+ }
+
+ case GR_EVENT_TYPE_CLOSE_REQ:
+ GrClose();
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+/* ========================= UI DRAWING ========================= */
+
+static void redraw(void) {
+ int r, c, idx, tw, th, tb;
+
+ /* display area */
+ GrSetGCForeground(gc_button, GrGetSysColor(GR_COLOR_WINDOW));
+ GrFillRect(win, gc_button, 5, 5, WIN_W - 10, DISP_H - 10);
+ GrSetGCForeground(gc_border, GrGetSysColor(GR_COLOR_BTNSHADOW));
+ GrRect(win, gc_border, 5, 5, WIN_W - 11, DISP_H - 10);
+
+ /* display text (right aligned) */
+ GrGetGCTextSize(gc_text, display, strlen(display), GR_TFTOP, &tw, &th, &tb);
+ {
+ int x = WIN_W - 12 - tw;
+ if (x < 10) x = 10;
+ GrText(win, gc_text, x, 5 + (DISP_H / 2) - 10, display, strlen(display), GR_TFTOP);
+ }
+
+ /* draw buttons */
+ idx = 0;
+ GrSetGCForeground(gc_button, GrGetSysColor(GR_COLOR_BTNFACE));
+ for (r = 0; r < ROWS; r++) {
+ for (c = 0; c < COLS; c++) {
+ GR_RECT *rc = &btn_rects[r][c];
+ char *lbl =
+ (idx < (int)(sizeof(btn_labels)/sizeof(btn_labels[0]))
+ ? btn_labels[idx] : NULL);
+
+ GrSetGCForeground(gc_button, GrGetSysColor(GR_COLOR_WINDOW));
+ GrFillRect(win, gc_button, rc->x, rc->y, rc->width, rc->height);
+ GrSetGCForeground(gc_border, GrGetSysColor(GR_COLOR_BTNSHADOW));
+ GrRect(win, gc_border, rc->x, rc->y, rc->width, rc->height);
+
+ if (lbl) {
+ GrGetGCTextSize(gc_text, lbl, strlen(lbl), GR_TFTOP, &tw, &th, &tb);
+ GrText(win, gc_text, rc->x + rc->width/2 - tw/2, rc->y + rc->height/2 - 6, lbl, strlen(lbl), GR_TFTOP);
+ }
+ idx++;
+ }
+ }
+
+ GrFlush();
+}
+
+/* ========================= LOGIC ========================= */
+
+static void press_button(const char *label) {
+ char tmp[128];
+ double val;
+
+ if (strcmp(label, "C") == 0) {
+ display[0] = '\0';
+ acc = 0.0;
+ pending_op = 0;
+ entering = 0;
+ dot_entered = 0;
+ set_display_from_double(0.0);
+ return;
+ }
+
+ if (strcmp(label, "<-") == 0) {
+ int l = strlen(display);
+ if (l > 0) {
+ if (display[l-1] == '.') dot_entered = 0;
+ display[l-1] = '\0';
+ if (strlen(display) == 0) set_display_from_double(0.0);
+ }
+ entering = (strlen(display) > 0);
+ return;
+ }
+
+ if (strcmp(label, "+/-") == 0) {
+ if (display[0] == '-') {
+ memmove(display, display+1, strlen(display));
+ } else {
+ if (strlen(display) == 0 || strcmp(display, "0") == 0) return;
+ snprintf(tmp, sizeof(tmp), "-%s", display);
+ strncpy(display, tmp, sizeof(display)-1);
+ }
+ return;
+ }
+
+ if (strcmp(label, "+") == 0 || strcmp(label, "-") == 0 ||
+ strcmp(label, "*") == 0 || strcmp(label, "/") == 0) {
+
+ val = display_to_double();
+ if (pending_op == 0)
+ acc = val;
+ else
+ apply_pending(val);
+
+ pending_op = label[0];
+ entering = 0;
+ dot_entered = 0;
+ set_display_from_double(acc);
+ return;
+ }
+
+ if (strcmp(label, "=") == 0) {
+ compute_equal();
+ return;
+ }
+
+ if (strcmp(label, ".") == 0) {
+ if (!dot_entered) {
+ if (!entering) {
+ strcpy(display, "0.");
+ entering = 1;
+ } else {
+ strncat(display, ".", sizeof(display)-strlen(display)-1);
+ }
+ dot_entered = 1;
+ }
+ return;
+ }
+
+ if (strlen(label) == 1 && isdigit((unsigned char)label[0])) {
+ if (!entering) {
+ display[0] = label[0];
+ display[1] = '\0';
+ entering = 1;
+ } else {
+ if (strcmp(display, "0") == 0 && label[0] != '0' && !dot_entered) {
+ display[0] = label[0]; display[1] = '\0';
+ } else if (strlen(display) < sizeof(display) - 2) {
+ strncat(display, label, 1);
+ }
+ }
+ return;
+ }
+}
+
+static void apply_pending(double val) {
+ if (pending_op == '+') acc = acc + val;
+ else if (pending_op == '-') acc = acc - val;
+ else if (pending_op == '*') acc = acc * val;
+ else if (pending_op == '/') {
+ if (val == 0.0) {
+ strncpy(display, "Error", sizeof(display)-1);
+ display[sizeof(display)-1] = '\0';
+ pending_op = 0;
+ entering = 0;
+ dot_entered = 0;
+ redraw();
+ return;
+ } else {
+ acc = acc / val;
+ }
+ }
+}
+
+static void compute_equal(void) {
+ double val = display_to_double();
+ if (pending_op != 0) {
+ apply_pending(val);
+ pending_op = 0;
+ set_display_from_double(acc);
+ entering = 0;
+ dot_entered = 0;
+ } else {
+ set_display_from_double(val);
+ }
+}
diff --git a/examples/nanoxterm/Kconfig b/examples/nanoxterm/Kconfig
new file mode 100644
index 00000000000..3d13d7a9fae
--- /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 && ARCH_HAVE_FORK
+ ---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 00000000000..41807c0b597
--- /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 00000000000..0a10792e9e9
--- /dev/null
+++ b/examples/nanoxterm/Makefile
@@ -0,0 +1,41 @@
+############################################################################
+# 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
+
+ifneq ($(CONFIG_MICROWINDOWS_NANOX_NONETWORK),)
+CFLAGS += -DNONETWORK=1
+endif
+
+
+include $(APPDIR)/Application.mk
diff --git a/examples/nanoxterm/nxterm.c b/examples/nanoxterm/nxterm.c
new file mode 100755
index 00000000000..22a11114db6
--- /dev/null
+++ b/examples/nanoxterm/nxterm.c
@@ -0,0 +1,1745 @@
+/*
+ * nxterm - terminal emulator for Nano-X
+ *
+ * (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
+ */
+
+#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
+#include
+#include
+#include
+#define MWINCLUDECOLORS
+#include "nano-X.h"
+#include "uni_std.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* 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];
+
+#define stdcol 80
+#define stdrow 50
+#define KBDBUF 10240
+
+#define TITLE "nxterm"
+#define stdforeground BLACK
+#define stdbackground LTGRAY
+//#define stdbackground GR_COLOR_GAINSBORO
+//#define stdbackground BLUE
+#define LINEBUF stdcol
+
+/*
+ * globals
+ */
+GR_WINDOW_ID w1; /* id for window */
+GR_GC_ID gc1; /* graphics context */
+GR_FONT_ID regFont;
+/*GR_FONT_ID boldFont;*/
+GR_SCREEN_INFO si; /* screen info */
+GR_FONT_INFO fi; /* Font Info */
+
+#define fonh fi.height
+#define fonw fi.maxwidth
+
+GR_WINDOW_INFO wi;
+GR_GC_INFO gi;
+GR_BOOL havefocus = GR_FALSE;
+pid_t pid;
+short winw, 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; /* set when screen has been scrolled up */
+
+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 */
+#define ANSI 0
+#define VT52 1
+int termtype = ANSI;
+int bgmode, escstate, curx, cury, curon, curvis;
+int savx, savy, wrap, style;
+int col, row, colmask = 0x7f, rowmask = 0x7f;
+int sbufcnt = 0;
+int sbufx, sbufy;
+char lineBuffer[LINEBUF+1];
+char *sbuf = lineBuffer;
+
+int term_init(void);
+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); //ANSI codes
+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); /* cursor position x axis for ansi */
+void pos_yaxis(int c); /* cursor position x axis for ansi */
+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) /* setting background color */
+{
+ GrSetGCBackground(gc1, c);
+ GrGetGCInfo(gc1,&gi);
+ escstate = 0;
+}
+
+void esc4(unsigned char c) /* setting foreground color */
+{
+ GrSetGCForeground(gc1,c);
+ GrGetGCInfo(gc1,&gi);
+ escstate = 0;
+}
+
+void esc3(unsigned char c) /* cursor position x axis */
+{
+ curx = (c - 32) & colmask;
+ if (curx >= col)
+ curx = col - 1;
+ else if (curx < 0)
+ curx = 0;
+ escstate = 0;
+}
+
+
+void esc2(unsigned char c) /* cursor position y axis */
+{
+ cury = (c - 32) & rowmask;
+ if (cury >= row)
+ cury = row - 1;
+ else if (cury < 0)
+ cury = 0;
+ escstate = 3;
+}
+
+
+void esc1(unsigned char c) /* various control codes */
+{
+
+ 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':/* cursor up */
+ hide_cursor();
+ if ((cury -= 1) < 0)
+ cury = 0;
+ break;
+
+ case 'B':/* cursor down */
+ hide_cursor();
+ if ((cury += 1) >= row)
+ cury = row - 1;
+ break;
+
+ case 'C':/* cursor right */
+ hide_cursor();
+ if ((curx += 1) >= col)
+ curx = col - 1;
+ break;
+
+ case 'D':/* cursor left */
+ hide_cursor();
+ if ((curx -= 1) < 0)
+ curx = 0;
+ break;
+
+ case 'E':/* clear screen & home */
+ GrClearWindow(w1, 0);
+ curx = 0;
+ cury = 0;
+ break;
+
+ case 'H':/* cursor home */
+ curx = 0;
+ cury = 0;
+ break;
+
+ case 'I':/* reverse index */
+ scrolledFlag = 1;
+ if ((cury -= 1) < 0) {
+ cury = 0;
+ vscrollup(1);
+ }
+ break;
+
+ case 'J':/* erase to end of page */
+ 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':/* erase to end of line */
+ GrSetGCForeground(gc1,gi.background);
+ GrFillRect(w1, gc1, curx*fonw, cury*fonh, (col-curx)*fonw, fonh);
+ GrSetGCForeground(gc1,gi.foreground);
+ break;
+
+ case 'L':/* insert line */
+ if (cury < row-1)
+ vscrollup(1);
+ curx = 0;
+ break;
+
+ case 'M':/* delete line */
+ if (cury < row-1)
+ vscrollup(1);
+ curx = 0;
+ break;
+
+ case 'Y':/* position cursor */
+ escstate = 2;
+ break;
+
+ case 'b':/* set foreground color */
+ escstate = 4;
+ break;
+
+ case 'c':/* set background color */
+ escstate = 5;
+ break;
+
+ case 'd':/* erase beginning of display */
+ /* w_setmode(win, bgmode); */
+ 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':/* enable cursor */
+ curon = 1;
+ break;
+
+ case 'f':/* disable cursor */
+ curon = 0;
+ break;
+
+ case 'j':/* save cursor position */
+ savx = curx;
+ savy = cury;
+ break;
+
+ case 'k':/* restore cursor position */
+ curx = savx;
+ cury = savy;
+ break;
+
+ case 'l':/* erase entire line */
+ GrSetGCForeground(gc1,gi.background);
+ GrFillRect(w1,gc1, 0, cury*fonh, winw, fonh);
+ GrSetGCForeground(gc1,gi.foreground);
+ curx = 0;
+ break;
+
+ case 'o':/* erase beginning of line */
+ if (curx > 0) {
+ GrSetGCForeground(gc1,gi.background);
+ GrFillRect(w1,gc1,0, cury*fonh, curx*fonw, fonh);
+ GrSetGCForeground(gc1,gi.foreground);
+ }
+ break;
+
+ case 'p':/* enter reverse video mode */
+ if(!ReverseMode) {
+ GrSetGCForeground(gc1,gi.background);
+ GrSetGCBackground(gc1,gi.foreground);
+ ReverseMode=1;
+ }
+ break;
+
+ case 'q':/* exit reverse video mode */
+ if(ReverseMode) {
+ GrSetGCForeground(gc1,gi.foreground);
+ GrSetGCBackground(gc1,gi.background);
+ ReverseMode=0;
+ }
+ break;
+
+ case 'v':/* enable wrap at end of line */
+ wrap = 1;
+ break;
+
+ case 'w':/* disable wrap at end of line */
+ wrap = 0;
+ break;
+
+/* and these are the extentions not in VT52 */
+ case 'G': /* clear all attributes */
+ break;
+
+ case 'g': /* enter bold mode */
+ /*GrSetGCFont(gc1, boldFont); */
+ break;
+
+ case 'h': /* exit bold mode */
+ /* GrSetGCFont(gc1, regFont); */
+ break;
+
+ case 'i': /* enter underline mode */
+ break;
+
+ /* j, k and l are already used */
+ case 'm': /* exit underline mode */
+ break;
+
+/* these ones aren't yet on the termcap entries */
+ case 'n': /* enter italic mode */
+ break;
+
+ /* o, p and q are already used */
+ case 'r': /* exit italic mode */
+ break;
+
+ case 's': /* enter light mode */
+ break;
+
+ case 't': /* exit ligth mode */
+ break;
+
+ default: /* unknown escape sequence */
+ break;
+ }
+}
+
+
+void pos_xaxis(int c) /* cursor position x axis for ansi */
+{
+ curx = c & colmask;
+ if (curx >= col)
+ curx = col - 1;
+ else if (curx < 0)
+ curx = 0;
+}
+
+
+void pos_yaxis(int c) /* cursor position y axis for ansi */
+{
+ cury = c & rowmask;
+/* if (cury >= row)
+ cury = row - 1;
+ else if (cury < 0)
+ cury = 0;
+*/
+ if (cury >= scrollbottom)
+ cury = scrollbottom - 1;
+ else if (cury < scrolltop)
+ cury = scrolltop-1;
+}
+
+
+void rendition(int escvalue) {
+
+ if (escvalue==0) { //reset to default
+ GrSetGCForeground(gc1, stdforeground);
+ GrSetGCBackground(gc1, stdbackground);
+ ReverseMode=0;
+ } else if (escvalue==7) { //inverse
+ if(!ReverseMode) {
+ GrSetGCForeground(gc1,gi.background);
+ GrSetGCBackground(gc1,gi.foreground);
+ ReverseMode=1;
+ }
+ } else if (escvalue==27) { //inverse off
+ 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); //default color
+ 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); //default color
+ break;
+ }
+ }
+}
+
+void esc100(unsigned char c) /* various ANSI control codes */
+{
+ int y, yy;
+ char buf[32];
+//leave escstate=10 till done. This states gets this function called.
+
+static int escvalue1,escvalue2,escvalue3;
+static char valuebuffer[3];
+valuebuffer[2]='\0';
+
+if (c == '?') return; //skip question mark for now, e.g. ESC[?7h for wrap on
+
+if (nobracket==1){
+ //fall through
+}else if (roundbracket == 1) { //just remove ESC(B for set US ASCII
+ //fall through
+}
+else if ( (c > 0x2F) && (c<':') ) // is it a number?
+{
+
+ 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 { //unknown
+ 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
+*/
+ //GrError("\n\nC:%c,val1:%d,val2:%d,val3:%d,scflag:%d,curx:%d,cury:%d,nobracket:%d\n",c,escvalue1,escvalue2,escvalue3,semicolonflag,curx,cury,nobracket);
+
+ if (nobracket==1){
+ if (c=='8'){ //Restore cursor
+ //HOME will reduce by one below, so add here!
+ escvalue1=savey+1;
+ escvalue2=savex+1;
+ c='H'; //position cursor
+ } else if (c=='7'){ //save cursor
+ savex=curx;
+ savey=cury;
+ c='!'; //done, so invalid code
+ } else if (c=='M'){ //reverse index, same as cursor up but scroll display at top
+ if (cury <= scrolltop){
+ escvalue1=1;
+ c='L'; //insert line
+ //cury--;
+ //vscrolldown(1);
+ //c='!'; //invalid code
+ } else {
+ cury--;
+ pos_yaxis(cury);
+ c='!'; //invalid code
+ }
+
+ } else if (c=='D'){ //index, same as cursor down but scroll display at bottom
+ if ((cury+1) > scrollbottom){
+ escvalue1=1;
+ c='M'; //delete line
+ //vscrollup(1);
+ //c='!'; //invalid code
+ } else {
+ cury++;
+ pos_yaxis(cury);
+ c='!'; //invalid code
+ }
+
+ } //c==8
+ } //nobracket
+ nobracket=0; //always reset
+
+ if (roundbracket==1){ //remove ESC(B for set to US ASCII
+ c=0;
+ roundbracket=0;
+ }
+
+ switch(c) {
+ case 'A':/* cursor up */
+ if (escvalue1==0) escvalue1=1;
+ hide_cursor();
+ if ((cury -= escvalue1) < 0)
+ //cury = 0;
+ cury = scrolltop-1; //cury zero based
+ break;
+
+ case 'B':/* cursor down */
+ if (escvalue1==0) escvalue1=1;
+ hide_cursor();
+ if ((cury += escvalue1) >= row)
+ //cury = row - 1;
+ cury = scrollbottom-1; //cury zero based
+ break;
+
+ case 'C':/* cursor right */
+ if (escvalue1==0) escvalue1=1;
+ hide_cursor();
+ if ((curx += escvalue1) >= col)
+ curx = col - 1;
+ break;
+
+ case 'D':/* cursor left */
+ 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) { //erase area below current line
+ GrSetGCForeground(gc1,gi.background);
+ GrFillRect(w1,gc1, 0,(cury+1)*fonh, winw, (scrollbottom-1-cury)*fonh);
+ GrSetGCForeground(gc1,gi.foreground);
+ } //erase from cursor to end of line
+ 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);
+ } //erase from beginning of line to cursor position
+ 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':/* erase to end of line */
+ if (escvalue1==0) { //erase from current cursor to end of line
+ 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 beginning of line to cursor
+ 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':/* insert lines */
+ 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':/* delete lines */
+
+ 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':/* scroll page up number of lines */
+ if (escvalue1==0) escvalue1=1;
+ vscrollup(escvalue1);
+ break;
+
+ case 'T':/* scroll page down number of lines */
+ if (escvalue1==0) escvalue1=1;
+ vscrolldown(escvalue1);
+ break;
+
+ case 'H':/* position cursor */
+ case 'f':/* position cursor */
+ if (escvalue1>0) escvalue1--;
+ if (escvalue2>0) escvalue2--;
+ pos_yaxis(escvalue1);
+ pos_xaxis(escvalue2);
+ break;
+
+ case 'E':/* lines down, col=1 */
+ if (escvalue1==0) escvalue1=1;
+ hide_cursor();
+ if ((cury += escvalue1) >= scrollbottom)
+ cury = scrollbottom - 1;
+ curx = 0;
+ break;
+
+ case 'F':/* lines up, col=1 */
+ if (escvalue1==0) escvalue1=1;
+ hide_cursor();
+ if ((cury -= escvalue1) < 0)
+ cury = 0;
+ curx = 0;
+ break;
+
+ case 'd':/* position cursor to row */
+ if (escvalue1>0) escvalue1--;
+ pos_yaxis(escvalue1);
+ break;
+
+ case '`':/* position cursor to col */
+ case 'G':/* position cursor to col */
+ if (escvalue1>0) escvalue1--;
+ pos_xaxis(escvalue1);
+ break;
+
+ case 'e':/* move cursor down rows */
+ if (escvalue1>0) escvalue1--;
+ pos_yaxis(escvalue1+cury);
+ break;
+
+ case 'a':/* move cursor right columns */
+ if (escvalue1>0) escvalue1--;
+ pos_xaxis(escvalue1+curx);
+ break;
+
+ case 'm':/* Set graphics rendition */
+ //may be more values, do just up to three here
+ //foreground colors run from 30 to 37, background from 40 to 47
+ rendition(escvalue1); //always, even if 0
+ if (escvalue2!=0) rendition(escvalue2);
+ if (escvalue3!=0) rendition(escvalue3);
+
+ escvalue1=0;
+ escvalue2=0;
+ escvalue3=0;
+ GrGetGCInfo(gc1,&gi);
+ break;
+
+
+ case 's':/* save cursor position */
+ savx = curx;
+ savy = cury;
+ break;
+
+ case 'u':/* restore cursor position */
+ curx = savx;
+ cury = savy;
+ break;
+
+ case 'r':/* set scrolling region */
+ if ((escvalue1>-1) && (escvalue2 <= (winh-1))){
+ scrolltop = escvalue1;
+ scrollbottom = escvalue2;
+ }
+ break;
+
+ case 'h':/* enable private modes - e.g. wrap at end of line */
+ if (escvalue1==7) wrap = 1;
+ if (escvalue1==25) show_cursor();
+ break;
+
+ case 'l':/* disable private modes - e.g. wrap at end of line */
+ if (escvalue1==7) wrap = 0;
+ if (escvalue1==25) hide_cursor();
+ break;
+
+ case 'n':/* DSR device status report */
+ sprintf(buf, "\033[%d;%dR", cury + 1, curx + 1);
+ write(termfd, buf, strlen(buf));
+ break;
+ default: /* unknown escape sequence */
+ break;
+ }
+ //GrError("end-C:%c,val1:%d,val2:%d,val3:%d,scflag:%d,curx:%d,cury:%d\n",c,escvalue1,escvalue2,escvalue3,semicolonflag,curx,cury);
+}
+
+
+/*
+ * 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: /* bell */
+ 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: /* backspace */
+ hide_cursor();
+ if ((curx -= 1) < 0)
+ /* lineRedraw();
+ if (--curx < 0) */
+ curx = 0;
+ pos_xaxis(curx);
+ break;
+
+ case 9: /* tab */
+ {
+ int borg,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: /* line feed */
+ sflush();
+ if (++cury >= scrollbottom) {
+ //have to scroll before moving cursor, so 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: /* carriage return */
+ sflush();
+ curx = 0;
+ pos_xaxis(curx);
+ break;
+
+ case 27: /* escape */
+ sflush();
+ semicolonflag=0;
+ escstate = 1;
+ break;
+
+ case 127: /* delete */
+ break;
+
+ default: /* any printable char */
+ 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 = savx = 0;
+ cury = savy = 0;
+ wrap = 1;
+ curon = 1;
+ curvis = 0;
+ escstate = 0;
+}
+
+
+/*
+ * general code...
+ */
+void
+term(void)
+{
+ long in, 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; /* wait until mapped before reading */
+ hide_cursor();
+ if ((in = read(termfd, buf, sizeof(buf))) > 0) {
+ for (l=0; l 0)
+ strcpy((char *)buffer, str);
+ return len;
+}
+
+int do_special_key_ansi(unsigned char *buffer, int key, int modifier)
+{
+ int len;
+ char *str, 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~"; //this is correct
+ 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, 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);
+}
+
+#include
+
+#ifndef NONETWORK
+extern int nanox_server_main(int argc, char *argv[]);
+#endif
+
+int main(int argc, char **argv)
+{
+ GR_CURSOR_ID c1;
+ GR_BITMAP bitmap1fg[7]; /* mouse cursor */
+ GR_BITMAP bitmap1bg[7];
+
+#ifndef NONETWORK
+ /* 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);
+#endif
+
+ 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);
+
+#define _ ((unsigned) 0) /* off bits */
+#define X ((unsigned) 1) /* on bits */
+#define MASK7(a,b,c,d,e,f,g) (((((((((((((a * 2) + b) * 2) + c) * 2) + d) * 2) + e) * 2) + f) * 2) + g) << 9)
+ 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(); /* create pty */
+ 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);
+ signal(SIGTTOU, SIG_IGN); /* just in case we're started in the background */
+
+ init();
+ term();
+ return 0;
+}
+
+/*
+ * pty create/open routines
+ */
+
+/* new-style /dev/ptmx, /dev/pts/0*/
+int term_init(void)
+{
+ int tfd;
+ pid_t child;
+
+ 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); /* required before grantpt()*/
+ grantpt(tfd);
+ unlockpt(tfd);
+
+ if ((child = fork()) == -1) {
+ GrError("No processes\n");
+ return -1;
+ }
+ if (!child) {
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(tfd);
+
+ if ((tfd = open(ptyname, O_RDWR)) < 0) {
+ GrError("Can't open %s\n", ptyname);
+ exit(1);
+ }
+
+ close(STDERR_FILENO);
+ dup2(tfd, STDIN_FILENO);
+ dup2(tfd, STDOUT_FILENO);
+ dup2(tfd, STDERR_FILENO);
+ nsh_consolemain(0, NULL);
+ exit(1);
+ }
+ return tfd;
+}
diff --git a/graphics/microwindows/Kconfig b/graphics/microwindows/Kconfig
index 9d5f0cb8c43..ff0a954e2f1 100644
--- a/graphics/microwindows/Kconfig
+++ b/graphics/microwindows/Kconfig
@@ -128,4 +128,34 @@ config MICROWINDOWS_MWIN
a high-level windowing API similar to the Microsoft Windows API
with window management, controls, and standard dialogs.
+config MICROWINDOWS_NANOX
+ bool "Nano-X (X11 API) client/server support"
+ default n
+ depends on MICROWINDOWS_NANOX_NONETWORK || (NET_LOCAL && NET_LOCAL_STREAM)
+ ---help---
+ Build the Nano-X server core and the Nano-X client library.
+ The Nano-X server runs as a separate task and provides the
+ X11-like API (GrXXX calls) to client applications which
+ connect to it through a local stream socket. Alternatively,
+ with MICROWINDOWS_NANOX_NONETWORK, the client applications
+ are linked directly with the server and run in the same task.
+
+config MICROWINDOWS_NANOX_NONETWORK
+ bool "Link Nano-X applications directly with the server"
+ default n
+ ---help---
+ Build the Nano-X client library and server in the linked-in
+ (NONETWORK) mode: client applications are linked directly
+ with the server, so no network stack or socket is required
+ and the memory footprint is minimal. Only a single client
+ application can be active at a time.
+
+config MICROWINDOWS_NANOX_NANOWM
+ bool "Built-in Nano-X window manager"
+ default n
+ depends on MICROWINDOWS_NANOX
+ ---help---
+ Link the built-in window manager (wm*.c, nxdraw.c) into the
+ Nano-X server, providing window decorations (title bar, 3D
+ frame), cascaded placement, move/resize and focus handling.
endif # GRAPHICS_MICROWINDOWS
diff --git a/graphics/microwindows/Make.defs b/graphics/microwindows/Make.defs
index 935abf66b6e..db124fcfaa9 100644
--- a/graphics/microwindows/Make.defs
+++ b/graphics/microwindows/Make.defs
@@ -25,8 +25,7 @@ CONFIGURED_APPS += $(APPDIR)/graphics/microwindows
# It allows `microwindows/src/include` import.
-CFLAGS += ${INCDIR_PREFIX}$(APPDIR)/graphics/microwindows/microwindows/src/include
-CXXFLAGS += ${INCDIR_PREFIX}$(APPDIR)/graphics/microwindows/microwindows/src/include
+EXPORTED_INCLUDES += ${INCDIR_PREFIX}$(APPDIR)/graphics/microwindows/microwindows/src/include
CFLAGS += -DMWCONFIG_FILE='"mwconfig.nuttx"'
diff --git a/graphics/microwindows/Makefile b/graphics/microwindows/Makefile
index 2ea7e4c1a8f..6b0ae03f057 100644
--- a/graphics/microwindows/Makefile
+++ b/graphics/microwindows/Makefile
@@ -153,4 +153,73 @@ CFLAGS += -DNOGDI
endif
+ifneq ($(CONFIG_MICROWINDOWS_NANOX),)
+
+MW_NANOX_DRAW_SRCS = microwindows/src/nanox/nxdraw.c
+MW_NANOX_DRAW_SRCS += microwindows/src/nanox/nxutil.c
+MW_NANOX_DRAW_SRCS += microwindows/src/nanox/nxtransform.c
+MW_NANOX_DRAW_SRCS += microwindows/src/nanox/nxpaintnc.c
+
+MW_NANOX_WM_SRCS = microwindows/src/nanox/wmaction.c
+MW_NANOX_WM_SRCS += microwindows/src/nanox/wmclients.c
+MW_NANOX_WM_SRCS += microwindows/src/nanox/wmevents.c
+MW_NANOX_WM_SRCS += microwindows/src/nanox/wmutil.c
+
+MW_NANOX_SERVER_SRCS = microwindows/src/nanox/srvmain.c
+MW_NANOX_SERVER_SRCS += microwindows/src/nanox/srvfunc.c
+MW_NANOX_SERVER_SRCS += microwindows/src/nanox/srvutil.c
+MW_NANOX_SERVER_SRCS += microwindows/src/nanox/srvevent.c
+MW_NANOX_SERVER_SRCS += microwindows/src/nanox/srvclip.c
+
+ifeq ($(CONFIG_MICROWINDOWS_NANOX_NONETWORK),y)
+
+# NONETWORK mode: the client application is linked directly with the
+# server and runs in the same task, without a network socket. Only a
+# single client can be active at a time.
+CFLAGS += -DNONETWORK=1
+
+ifneq ($(CONFIG_MICROWINDOWS_NANOX_NANOWM),)
+CFLAGS += -DNANOWM=1
+CSRCS += $(MW_NANOX_WM_SRCS)
+endif
+
+CSRCS += $(MW_NANOX_DRAW_SRCS) $(MW_NANOX_SERVER_SRCS)
+CSRCS += microwindows/src/nanox/srvnonet.c
+
+else
+
+# Network client/server mode: each client connects to the server
+# through a local stream socket. MULTITHREAD_SERVER enables the
+# per-task client data (POSIX per-thread key) so that several client
+# tasks can run at the same time in the flat build.
+
+MW_NANOX_CLIENT_SRCS = $(MW_NANOX_DRAW_SRCS)
+MW_NANOX_CLIENT_SRCS += microwindows/src/nanox/client.c
+MW_NANOX_CLIENT_SRCS += microwindows/src/nanox/clientfb.c
+MW_NANOX_CLIENT_SRCS += microwindows/src/nanox/nxproto.c
+MW_NANOX_CLIENT_SRCS += microwindows/src/drivers/osdep.c
+
+MW_NANOX_SERVER_SRCS += microwindows/src/nanox/srvnet.c
+
+# The server main() is renamed to nanox_server_main() and invoked from
+# the nanoxterm example, which starts the server as a separate task.
+microwindows/src/nanox/srvmain.c_CFLAGS += -Dmain=nanox_server_main
+
+CFLAGS += -DMULTITHREAD_SERVER=1
+ifneq ($(CONFIG_MICROWINDOWS_NANOX_NANOWM),)
+CFLAGS += -DNANOWM=1
+
+# Rename the server-side GrXXX symbols to SVR_GrXXX, since the server
+# and the client code are linked into a single image.
+$(foreach src,$(MW_NANOX_WM_SRCS),$(eval $(src)_CFLAGS += -include microwindows/src/nanox/serv.h))
+
+CSRCS += $(MW_NANOX_WM_SRCS)
+endif
+
+CSRCS += $(MW_NANOX_CLIENT_SRCS) $(MW_NANOX_SERVER_SRCS)
+
+endif
+
+endif
+
include $(APPDIR)/Application.mk