diff --git a/HOWTO/INSTALL-WIN32.md b/HOWTO/INSTALL-WIN32.md
index 4ad0159bcd25..bd8387aaae65 100644
--- a/HOWTO/INSTALL-WIN32.md
+++ b/HOWTO/INSTALL-WIN32.md
@@ -68,7 +68,7 @@ This is the short story though, for the experienced and impatient:
) and unpack with `tar`
to the windows disk for example to: /mnt/c/src/
- * Install mingw-gcc, and make: `sudo apt install g++-mingw-w64 gcc-mingw-w64 make`
+ * Install mingw-gcc, and make: `sudo apt update && sudo apt install g++-mingw-w64 gcc-mingw-w64 make`
* `$ cd UNPACK_DIR`
@@ -150,7 +150,7 @@ the different tools:
Install into `C:/OpenSSL-Win64` (or `C:/OpenSSL-Win32`)
* wxWidgets (optional)
- You need this to build wx and use gui's in debugger and observer.
+ You need this to build wx to use gui's in debugger and observer.
We recommend v3.1.4 or later.
Unpack into `c:/opt/local64/pgm/wxWidgets-3.1.4`
diff --git a/erts/Makefile b/erts/Makefile
index d6d9dee40d08..a0f0dcfdb3ab 100644
--- a/erts/Makefile
+++ b/erts/Makefile
@@ -86,10 +86,8 @@ local_setup:
cp $(ERL_TOP)/bin/$(TARGET)/erlc.exe $(ERL_TOP)/bin/erlc.exe; \
cp $(ERL_TOP)/bin/$(TARGET)/erl.exe $(ERL_TOP)/bin/erl.exe; \
cp $(ERL_TOP)/bin/$(TARGET)/erl_call.exe $(ERL_TOP)/bin/erl_call.exe; \
- cp $(ERL_TOP)/bin/$(TARGET)/werl.exe $(ERL_TOP)/bin/werl.exe; \
cp $(ERL_TOP)/bin/$(TARGET)/escript.exe $(ERL_TOP)/bin/escript.exe; \
- chmod 755 $(ERL_TOP)/bin/erl.exe $(ERL_TOP)/bin/erlc.exe \
- $(ERL_TOP)/bin/werl.exe; \
+ chmod 755 $(ERL_TOP)/bin/erl.exe $(ERL_TOP)/bin/erlc.exe; \
make_local_ini.sh $(ERL_TOP); \
cp $(ERL_TOP)/bin/erl.ini $(ERL_TOP)/bin/$(TARGET)/erl.ini; \
else \
diff --git a/erts/doc/src/erlsrv_cmd.xml b/erts/doc/src/erlsrv_cmd.xml
index e8f066b21b5d..fe952f690e66 100644
--- a/erts/doc/src/erlsrv_cmd.xml
+++ b/erts/doc/src/erlsrv_cmd.xml
@@ -112,8 +112,7 @@
-
The location of the Erlang emulator.
The default is the located in the same
- directory as erlsrv.exe. Do not specify
- as this emulator, it will not work.
+ directory as erlsrv.exe.
If the system uses release handling, this is to be set to a
program similar to .
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in
index 42f7a6bb1c03..3791d2caa24c 100644
--- a/erts/emulator/Makefile.in
+++ b/erts/emulator/Makefile.in
@@ -1129,6 +1129,7 @@ RUN_OBJS += \
LTTNG_OBJS = $(OBJDIR)/erlang_lttng.o
NIF_OBJS = \
+ $(OBJDIR)/prim_tty_nif.o \
$(OBJDIR)/erl_tracer_nif.o \
$(OBJDIR)/prim_buffer_nif.o \
$(OBJDIR)/prim_file_nif.o \
@@ -1139,10 +1140,8 @@ ifeq ($(TARGET),win32)
DRV_OBJS = \
$(OBJDIR)/registry_drv.o \
$(OBJDIR)/inet_drv.o \
- $(OBJDIR)/ram_file_drv.o \
- $(OBJDIR)/ttsl_drv.o
+ $(OBJDIR)/ram_file_drv.o
OS_OBJS = \
- $(OBJDIR)/win_con.o \
$(OBJDIR)/dll_sys.o \
$(OBJDIR)/driver_tab.o \
$(OBJDIR)/sys_float.o \
@@ -1166,8 +1165,7 @@ OS_OBJS = \
DRV_OBJS = \
$(OBJDIR)/inet_drv.o \
- $(OBJDIR)/ram_file_drv.o \
- $(OBJDIR)/ttsl_drv.o
+ $(OBJDIR)/ram_file_drv.o
endif
ifneq ($(STATIC_NIFS),no)
diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c
index 3b6de45587a7..dff270c60f61 100644
--- a/erts/emulator/beam/break.c
+++ b/erts/emulator/beam/break.c
@@ -581,7 +581,7 @@ do_break(void)
/* check if we're in console mode and, if so,
halt immediately if break is called */
mode = erts_read_env("ERL_CONSOLE_MODE");
- if (mode && sys_strcmp(mode, "window") != 0)
+ if (mode && sys_strcmp(mode, "detached") == 0)
erts_exit(0, "");
erts_free_read_env(mode);
#endif /* __WIN32__ */
diff --git a/erts/emulator/drivers/unix/ttsl_drv.c b/erts/emulator/drivers/unix/ttsl_drv.c
deleted file mode 100644
index 3fb5bdb8fb4d..000000000000
--- a/erts/emulator/drivers/unix/ttsl_drv.c
+++ /dev/null
@@ -1,1607 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1996-2022. All Rights Reserved.
- *
- * Licensed 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.
- *
- * %CopyrightEnd%
- */
-/*
- * Tty driver that reads one character at the time and provides a
- * smart line for output.
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include "erl_driver.h"
-
-static int ttysl_init(void);
-static ErlDrvData ttysl_start(ErlDrvPort, char*);
-
-#ifdef HAVE_TERMCAP /* else make an empty driver that cannot be opened */
-
-#ifndef WANT_NONBLOCKING
-#define WANT_NONBLOCKING
-#endif
-
-#include "sys.h"
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#ifdef HAVE_WCWIDTH
-#include
-#endif
-#ifdef HAVE_FCNTL_H
-#include
-#endif
-#ifdef HAVE_SYS_IOCTL_H
-#include
-#endif
-#if !defined(HAVE_SETLOCALE) || !defined(HAVE_NL_LANGINFO) || !defined(HAVE_LANGINFO_H)
-#define PRIMITIVE_UTF8_CHECK 1
-#else
-#include
-#endif
-
-#if defined IOV_MAX
-#define MAXIOV IOV_MAX
-#elif defined UIO_MAXIOV
-#define MAXIOV UIO_MAXIOV
-#else
-#define MAXIOV 16
-#endif
-
-#define TRUE 1
-#define FALSE 0
-
-/* Termcap functions. */
-int tgetent(char* bp, char *name);
-int tgetnum(char* cap);
-int tgetflag(char* cap);
-char *tgetstr(char* cap, char** buf);
-char *tgoto(char* cm, int col, int line);
-int tputs(char* cp, int affcnt, int (*outc)(int c));
-
-/* Terminal capabilities in which we are interested. */
-static char *capbuf;
-static char *up, *down, *left, *right;
-static int cols, xn;
-static volatile int cols_needs_update = FALSE;
-
-/* The various opcodes. */
-#define OP_PUTC 0
-#define OP_MOVE 1
-#define OP_INSC 2
-#define OP_DELC 3
-#define OP_BEEP 4
-#define OP_PUTC_SYNC 5
-/* Control op */
-#define CTRL_OP_GET_WINSIZE 100
-#define CTRL_OP_GET_UNICODE_STATE 101
-#define CTRL_OP_SET_UNICODE_STATE 102
-
-/* We use 1024 as the buf size as that was the default buf size of FILE streams
- on all platforms that I checked. */
-#define TTY_BUFFSIZE 1024
-
-static int lbuf_size = BUFSIZ;
-static Uint32 *lbuf; /* The current line buffer */
-static int llen; /* The current line length */
-static int lpos; /* The current "cursor position" in the line buffer */
- /* NOTE: not the same as column position a char may not take a"
- * column to display or it might take many columns
- */
-/*
- * Tags used in line buffer to show that these bytes represent special characters,
- * Max unicode is 0x0010ffff, so we have lots of place for meta tags...
- */
-#define CONTROL_TAG 0x10000000U /* Control character, value in first position */
-#define ESCAPED_TAG 0x01000000U /* Escaped character, value in first position */
-#define TAG_MASK 0xFF000000U
-
-#define MAXSIZE (1 << 16)
-
-#define COL(_l) ((_l) % cols)
-#define LINE(_l) ((_l) / cols)
-
-#define NL '\n'
-
-/* Main interface functions. */
-static void ttysl_stop(ErlDrvData);
-static void ttysl_from_erlang(ErlDrvData, char*, ErlDrvSizeT);
-static void ttysl_to_tty(ErlDrvData, ErlDrvEvent);
-static void ttysl_flush_tty(ErlDrvData);
-static void ttysl_from_tty(ErlDrvData, ErlDrvEvent);
-static void ttysl_stop_select(ErlDrvEvent, void*);
-static Sint16 get_sint16(char*);
-
-static ErlDrvPort ttysl_port;
-static int ttysl_fd;
-static int ttysl_terminate = 0;
-static int ttysl_send_ok = 0;
-static ErlDrvBinary *putcbuf;
-static int putcpos;
-static int putclen;
-
-/* Functions that work on the line buffer. */
-static int start_lbuf(void);
-static int stop_lbuf(void);
-static int put_chars(byte*,int);
-static int move_rel(int);
-static int ins_chars(byte *,int);
-static int del_chars(int);
-static int step_over_chars(int);
-static int insert_buf(byte*,int);
-static int write_buf(Uint32 *,int,int);
-static int outc(int c);
-static int move_cursor(int,int);
-static int cp_pos_to_col(int cp_pos);
-
-
-/* Termcap functions. */
-static int start_termcap(void);
-static int stop_termcap(void);
-static int move_left(int);
-static int move_right(int);
-static int move_up(int);
-static int move_down(int);
-static void update_cols(void);
-
-/* Terminal setting functions. */
-static int tty_init(int,int,int,int);
-static int tty_set(int);
-static int tty_reset(int);
-static ErlDrvSSizeT ttysl_control(ErlDrvData, unsigned int,
- char *, ErlDrvSizeT, char **, ErlDrvSizeT);
-#ifdef ERTS_NOT_USED
-static RETSIGTYPE suspend(int);
-#endif
-static RETSIGTYPE cont(int);
-static RETSIGTYPE winch(int);
-
-/*#define LOG_DEBUG*/
-
-#ifdef LOG_DEBUG
-FILE *debuglog = NULL;
-
-#define DEBUGLOG(X) \
-do { \
- if (debuglog != NULL) { \
- my_debug_printf X; \
- } \
-} while (0)
-
-static void my_debug_printf(char *fmt, ...)
-{
- char buffer[1024];
- va_list args;
-
- va_start(args, fmt);
- erts_vsnprintf(buffer,1024,fmt,args);
- va_end(args);
- erts_fprintf(debuglog,"%s\n",buffer);
- /*erts_printf("Debuglog = %s\n",buffer);*/
-}
-
-#else
-
-#define DEBUGLOG(X)
-
-#endif
-
-static int utf8_mode = 0;
-static byte utf8buf[4]; /* for incomplete input */
-static int utf8buf_size; /* size of incomplete input */
-
-# define IF_IMPL(x) x
-#else
-# define IF_IMPL(x) NULL
-#endif /* HAVE_TERMCAP */
-
-/* Define the driver table entry. */
-struct erl_drv_entry ttsl_driver_entry = {
- ttysl_init,
- ttysl_start,
- IF_IMPL(ttysl_stop),
- IF_IMPL(ttysl_from_erlang),
- IF_IMPL(ttysl_from_tty),
- IF_IMPL(ttysl_to_tty),
- "tty_sl", /* driver_name */
- NULL, /* finish */
- NULL, /* handle */
- IF_IMPL(ttysl_control),
- NULL, /* timeout */
- NULL, /* outputv */
- NULL, /* ready_async */
- IF_IMPL(ttysl_flush_tty),
- NULL, /* call */
- NULL, /* event */
- ERL_DRV_EXTENDED_MARKER,
- ERL_DRV_EXTENDED_MAJOR_VERSION,
- ERL_DRV_EXTENDED_MINOR_VERSION,
- 0, /* ERL_DRV_FLAGs */
- NULL, /* handle2 */
- NULL, /* process_exit */
- IF_IMPL(ttysl_stop_select)
-};
-
-
-static int ttysl_init(void)
-{
-#ifdef HAVE_TERMCAP
- ttysl_port = (ErlDrvPort)-1;
- ttysl_fd = -1;
- lbuf = NULL; /* For line buffer handling */
- capbuf = NULL; /* For termcap handling */
-#endif
-#ifdef LOG_DEBUG
- {
- char *dl;
- if ((dl = getenv("TTYSL_DEBUG_LOG")) != NULL && *dl) {
- debuglog = fopen(dl,"w+");
- if (debuglog != NULL)
- setbuf(debuglog,NULL);
- }
- DEBUGLOG(("ttysl_init: Debuglog = %s(0x%ld)\n",dl,(long) debuglog));
- }
-#endif
- return 0;
-}
-
-static ErlDrvData ttysl_start(ErlDrvPort port, char* buf)
-{
-#ifndef HAVE_TERMCAP
- return ERL_DRV_ERROR_GENERAL;
-#else
- char *s, *t, *l;
- int canon, echo, sig; /* Terminal characteristics */
- int flag;
- extern int using_oldshell; /* set this to let the rest of erts know */
-
- DEBUGLOG(("ttysl_start: driver input \"%s\", ttysl_port = %d (-1 expected)", buf, ttysl_port));
- utf8buf_size = 0;
- if (ttysl_port != (ErlDrvPort)-1) {
- DEBUGLOG(("ttysl_start: failure with ttysl_port = %d, not initialized properly?\n", ttysl_port));
- return ERL_DRV_ERROR_GENERAL;
- }
-
- DEBUGLOG(("ttysl_start: isatty(0) = %d (1 expected), isatty(1) = %d (1 expected)", isatty(0), isatty(1)));
- if (!isatty(0) || !isatty(1)) {
- DEBUGLOG(("ttysl_start: failure in isatty, isatty(0) = %d, isatty(1) = %d", isatty(0), isatty(1)));
- return ERL_DRV_ERROR_GENERAL;
- }
-
- /* Set the terminal modes to default leave as is. */
- canon = echo = sig = 0;
-
- /* Parse the input parameters. */
- for (s = strchr(buf, ' '); s; s = t) {
- s++;
- /* Find end of this argument (start of next) and insert NUL. */
- if ((t = strchr(s, ' '))) {
- *t = '\0';
- }
- if ((flag = ((*s == '+') ? 1 : ((*s == '-') ? -1 : 0)))) {
- if (s[1] == 'c') canon = flag;
- if (s[1] == 'e') echo = flag;
- if (s[1] == 's') sig = flag;
- }
- else if ((ttysl_fd = open(s, O_RDWR, 0)) < 0) {
- DEBUGLOG(("ttysl_start: failed to open ttysl_fd, open(%s, O_RDWR, 0)) = %d\n", s, ttysl_fd));
- return ERL_DRV_ERROR_GENERAL;
- }
- }
-
- if (ttysl_fd < 0)
- ttysl_fd = 0;
-
- if (tty_init(ttysl_fd, canon, echo, sig) < 0 ||
- tty_set(ttysl_fd) < 0) {
- DEBUGLOG(("ttysl_start: failed init tty or set tty\n"));
- ttysl_port = (ErlDrvPort)-1;
- tty_reset(ttysl_fd);
- return ERL_DRV_ERROR_GENERAL;
- }
-
- /* Set up smart line and termcap stuff. */
- if (!start_lbuf() || !start_termcap()) {
- DEBUGLOG(("ttysl_start: failed to start_lbuf or start_termcap\n"));
- stop_lbuf(); /* Must free this */
- tty_reset(ttysl_fd);
- return ERL_DRV_ERROR_GENERAL;
- }
-
- SET_NONBLOCKING(ttysl_fd);
-
-#ifdef PRIMITIVE_UTF8_CHECK
- setlocale(LC_CTYPE, ""); /* Set international environment,
- ignore result */
- if (((l = getenv("LC_ALL")) && *l) ||
- ((l = getenv("LC_CTYPE")) && *l) ||
- ((l = getenv("LANG")) && *l)) {
- if (strstr(l, "UTF-8"))
- utf8_mode = 1;
- }
-
-#else
- l = setlocale(LC_CTYPE, ""); /* Set international environment */
- if (l != NULL) {
- utf8_mode = (strcmp(nl_langinfo(CODESET), "UTF-8") == 0);
- DEBUGLOG(("ttysl_start: setlocale: %s",l));
- }
-#endif
- DEBUGLOG(("ttysl_start: utf8_mode is %s",(utf8_mode) ? "on" : "off"));
- sys_signal(SIGCONT, cont);
- sys_signal(SIGWINCH, winch);
-
- driver_select(port, (ErlDrvEvent)(UWord)ttysl_fd, ERL_DRV_READ|ERL_DRV_USE, 1);
- ttysl_port = port;
-
- /* we need to know this when we enter the break handler */
- using_oldshell = 0;
-
- DEBUGLOG(("ttysl_start: successful start\n"));
- return (ErlDrvData)ttysl_port; /* Nothing important to return */
-#endif /* HAVE_TERMCAP */
-}
-
-#ifdef HAVE_TERMCAP
-
-#define DEF_HEIGHT 24
-#define DEF_WIDTH 80
-static void ttysl_get_window_size(Uint32 *width, Uint32 *height)
-{
-#ifdef TIOCGWINSZ
- struct winsize ws;
- if (ioctl(ttysl_fd,TIOCGWINSZ,&ws) == 0) {
- *width = (Uint32) ws.ws_col;
- *height = (Uint32) ws.ws_row;
- if (*width <= 0)
- *width = DEF_WIDTH;
- if (*height <= 0)
- *height = DEF_HEIGHT;
- return;
- }
-#endif
- *width = DEF_WIDTH;
- *height = DEF_HEIGHT;
-}
-
-static ErlDrvSSizeT ttysl_control(ErlDrvData drv_data,
- unsigned int command,
- char *buf, ErlDrvSizeT len,
- char **rbuf, ErlDrvSizeT rlen)
-{
- char resbuff[2*sizeof(Uint32)];
- ErlDrvSizeT res_size;
-
- command -= ERTS_TTYSL_DRV_CONTROL_MAGIC_NUMBER;
- switch (command) {
- case CTRL_OP_GET_WINSIZE:
- {
- Uint32 w,h;
- ttysl_get_window_size(&w,&h);
- memcpy(resbuff,&w,sizeof(Uint32));
- memcpy(resbuff+sizeof(Uint32),&h,sizeof(Uint32));
- res_size = 2*sizeof(Uint32);
- }
- break;
- case CTRL_OP_GET_UNICODE_STATE:
- *resbuff = (utf8_mode) ? 1 : 0;
- res_size = 1;
- break;
- case CTRL_OP_SET_UNICODE_STATE:
- if (len > 0) {
- int m = (int) *buf;
- *resbuff = (utf8_mode) ? 1 : 0;
- res_size = 1;
- utf8_mode = (m) ? 1 : 0;
- } else {
- return 0;
- }
- break;
- default:
- return -1;
- }
- if (rlen < res_size) {
- *rbuf = driver_alloc(res_size);
- }
- memcpy(*rbuf,resbuff,res_size);
- return res_size;
-}
-
-
-static void ttysl_stop(ErlDrvData ttysl_data)
-{
- DEBUGLOG(("ttysl_stop: ttysl_port = %d\n",ttysl_port));
- if (ttysl_port != (ErlDrvPort)-1) {
- stop_lbuf();
- stop_termcap();
- tty_reset(ttysl_fd);
- driver_select(ttysl_port, (ErlDrvEvent)(UWord)ttysl_fd,
- ERL_DRV_WRITE|ERL_DRV_READ|ERL_DRV_USE, 0);
- sys_signal(SIGCONT, SIG_DFL);
- sys_signal(SIGWINCH, SIG_DFL);
- }
- ttysl_port = (ErlDrvPort)-1;
- ttysl_fd = -1;
- ttysl_terminate = 0;
- /* return TRUE; */
-}
-
-static int put_utf8(int ch, byte *target, int sz, int *pos)
-{
- Uint x = (Uint) ch;
- if (x < 0x80) {
- if (*pos >= sz) {
- return -1;
- }
- target[(*pos)++] = (byte) x;
- }
- else if (x < 0x800) {
- if (((*pos) + 1) >= sz) {
- return -1;
- }
- target[(*pos)++] = (((byte) (x >> 6)) |
- ((byte) 0xC0));
- target[(*pos)++] = (((byte) (x & 0x3F)) |
- ((byte) 0x80));
- } else if (x < 0x10000) {
- if ((x >= 0xD800 && x <= 0xDFFF) ||
- (x == 0xFFFE) ||
- (x == 0xFFFF)) { /* Invalid unicode range */
- return -1;
- }
- if (((*pos) + 2) >= sz) {
- return -1;
- }
-
- target[(*pos)++] = (((byte) (x >> 12)) |
- ((byte) 0xE0));
- target[(*pos)++] = ((((byte) (x >> 6)) & 0x3F) |
- ((byte) 0x80));
- target[(*pos)++] = (((byte) (x & 0x3F)) |
- ((byte) 0x80));
- } else if (x < 0x110000) { /* Standard imposed max */
- if (((*pos) + 3) >= sz) {
- return -1;
- }
- target[(*pos)++] = (((byte) (x >> 18)) |
- ((byte) 0xF0));
- target[(*pos)++] = ((((byte) (x >> 12)) & 0x3F) |
- ((byte) 0x80));
- target[(*pos)++] = ((((byte) (x >> 6)) & 0x3F) |
- ((byte) 0x80));
- target[(*pos)++] = (((byte) (x & 0x3F)) |
- ((byte) 0x80));
- } else {
- return -1;
- }
- return 0;
-}
-
-
-static int pick_utf8(byte *s, int sz, int *pos)
-{
- int size = sz - (*pos);
- byte *source;
- Uint unipoint;
-
- if (size > 0) {
- source = s + (*pos);
- if (((*source) & ((byte) 0x80)) == 0) {
- unipoint = (int) *source;
- ++(*pos);
- return (int) unipoint;
- } else if (((*source) & ((byte) 0xE0)) == 0xC0) {
- if (size < 2) {
- return -2;
- }
- if (((source[1] & ((byte) 0xC0)) != 0x80) ||
- ((*source) < 0xC2) /* overlong */) {
- return -1;
- }
- (*pos) += 2;
- unipoint =
- (((Uint) ((*source) & ((byte) 0x1F))) << 6) |
- ((Uint) (source[1] & ((byte) 0x3F)));
- return (int) unipoint;
- } else if (((*source) & ((byte) 0xF0)) == 0xE0) {
- if (size < 3) {
- return -2;
- }
- if (((source[1] & ((byte) 0xC0)) != 0x80) ||
- ((source[2] & ((byte) 0xC0)) != 0x80) ||
- (((*source) == 0xE0) && (source[1] < 0xA0)) /* overlong */ ) {
- return -1;
- }
- if ((((*source) & ((byte) 0xF)) == 0xD) &&
- ((source[1] & 0x20) != 0)) {
- return -1;
- }
- if (((*source) == 0xEF) && (source[1] == 0xBF) &&
- ((source[2] == 0xBE) || (source[2] == 0xBF))) {
- return -1;
- }
- (*pos) += 3;
- unipoint =
- (((Uint) ((*source) & ((byte) 0xF))) << 12) |
- (((Uint) (source[1] & ((byte) 0x3F))) << 6) |
- ((Uint) (source[2] & ((byte) 0x3F)));
- return (int) unipoint;
- } else if (((*source) & ((byte) 0xF8)) == 0xF0) {
- if (size < 4) {
- return -2 ;
- }
- if (((source[1] & ((byte) 0xC0)) != 0x80) ||
- ((source[2] & ((byte) 0xC0)) != 0x80) ||
- ((source[3] & ((byte) 0xC0)) != 0x80) ||
- (((*source) == 0xF0) && (source[1] < 0x90)) /* overlong */) {
- return -1;
- }
- if ((((*source) & ((byte)0x7)) > 0x4U) ||
- ((((*source) & ((byte)0x7)) == 0x4U) &&
- ((source[1] & ((byte)0x3F)) > 0xFU))) {
- return -1;
- }
- (*pos) += 4;
- unipoint =
- (((Uint) ((*source) & ((byte) 0x7))) << 18) |
- (((Uint) (source[1] & ((byte) 0x3F))) << 12) |
- (((Uint) (source[2] & ((byte) 0x3F))) << 6) |
- ((Uint) (source[3] & ((byte) 0x3F)));
- return (int) unipoint;
- } else {
- return -1;
- }
- } else {
- return -1;
- }
-}
-
-static int octal_or_hex_positions(Uint c)
-{
- int x = 0;
- Uint ch = c;
- if (!ch) {
- return 1;
- }
- while(ch) {
- ++x;
- ch >>= 3;
- }
- if (x <= 3) {
- return 3;
- }
- /* \x{H ...} format when larger than \777 */
- x = 0;
- ch = c;
- while(ch) {
- ++x;
- ch >>= 4;
- }
- return x+3;
-}
-
-static void octal_or_hex_format(Uint ch, byte *buf, int *pos)
-{
- static byte hex_chars[] = { '0','1','2','3','4','5','6','7','8','9',
- 'A','B','C','D','E','F'};
- int num = octal_or_hex_positions(ch);
- if (num != 3) {
- ASSERT(num > 3);
- buf[(*pos)++] = 'x';
- buf[(*pos)++] = '{';
- num -= 3;
- while(num--) {
- buf[(*pos)++] = hex_chars[((ch >> (4*num)) & 0xFU)];
- }
- buf[(*pos)++] = '}';
- } else {
- while(num--) {
- buf[(*pos)++] = ((byte) ((ch >> (3*num)) & 0x7U) + '0');
- }
- }
-}
-
-/*
- * Check that there is enough room in all buffers to copy all pad chars
- * and stiff we need If not, realloc lbuf.
- */
-static int check_buf_size(byte *s, int n)
-{
- int pos = 0;
- int ch;
- int size = 10;
-
- DEBUGLOG(("check_buf_size: n = %d",n));
- while(pos < n) {
- /* Indata is always UTF-8 */
- if ((ch = pick_utf8(s,n,&pos)) < 0) {
- /* XXX temporary allow invalid chars */
- ch = (int) s[pos];
- DEBUGLOG(("check_buf_size: Invalid UTF8:%d",ch));
- ++pos;
- }
- if (utf8_mode) { /* That is, terminal is UTF8 compliant */
- if (ch >= 128 || isprint(ch)) {
-#ifdef HAVE_WCWIDTH
- int width;
-#endif
- DEBUGLOG(("check_buf_size: Printable(UTF-8:%d):%d",pos,ch));
- size++;
-#ifdef HAVE_WCWIDTH
- if ((width = wcwidth(ch)) > 1) {
- size += width - 1;
- }
-#endif
- } else if (ch == '\t') {
- size += 8;
- } else {
- DEBUGLOG(("check_buf_size: Magic(UTF-8:%d):%d",pos,ch));
- size += 2;
- }
- } else {
- if (ch <= 255 && isprint(ch)) {
- DEBUGLOG(("check_buf_size: Printable:%d",ch));
- size++;
- } else if (ch == '\t')
- size += 8;
- else if (ch >= 128) {
- DEBUGLOG(("check_buf_size: Non printable:%d",ch));
- size += (octal_or_hex_positions(ch) + 1);
- }
- else {
- DEBUGLOG(("check_buf_size: Magic:%d",ch));
- size += 2;
- }
- }
- }
-
- if (size + lpos >= lbuf_size) {
-
- lbuf_size = size + lpos + BUFSIZ;
- if ((lbuf = driver_realloc(lbuf, lbuf_size * sizeof(Uint32))) == NULL) {
- DEBUGLOG(("check_buf_size: alloc failure of %d bytes", lbuf_size * sizeof(Uint32)));
- driver_failure(ttysl_port, -1);
- return(0);
- }
- }
- DEBUGLOG(("check_buf_size: success\n"));
- return(1);
-}
-
-
-static void ttysl_from_erlang(ErlDrvData ttysl_data, char* buf, ErlDrvSizeT count)
-{
- ErlDrvSizeT sz;
-
- sz = driver_sizeq(ttysl_port);
-
- putclen = count > TTY_BUFFSIZE ? TTY_BUFFSIZE : count;
- putcbuf = driver_alloc_binary(putclen);
- putcpos = 0;
-
- if (lpos > MAXSIZE)
- put_chars((byte*)"\n", 1);
-
- DEBUGLOG(("ttysl_from_erlang: OP = %d", buf[0]));
-
- switch (buf[0]) {
- case OP_PUTC_SYNC:
- /* Using sync means that we have to send an ok to the
- controlling process for each command call. We delay
- sending ok if the driver queue exceeds a certain size.
- We do not set ourselves as a busy port, as this
- could be very bad for user_drv, if it gets blocked on
- the port_command. */
- /* fall through */
- case OP_PUTC:
- DEBUGLOG(("ttysl_from_erlang: OP: Putc(%lu)",(unsigned long) count-1));
- if (check_buf_size((byte*)buf+1, count-1) == 0)
- return;
- put_chars((byte*)buf+1, count-1);
- break;
- case OP_MOVE:
- move_rel(get_sint16(buf+1));
- break;
- case OP_INSC:
- if (check_buf_size((byte*)buf+1, count-1) == 0)
- return;
- ins_chars((byte*)buf+1, count-1);
- break;
- case OP_DELC:
- del_chars(get_sint16(buf+1));
- break;
- case OP_BEEP:
- outc('\007');
- break;
- default:
- /* Unknown op, just ignore. */
- break;
- }
-
- driver_enq_bin(ttysl_port,putcbuf,0,putcpos);
- driver_free_binary(putcbuf);
-
- if (sz == 0) {
- for (;;) {
- int written, qlen;
- SysIOVec *iov;
-
- iov = driver_peekq(ttysl_port,&qlen);
- if (iov)
- written = writev(ttysl_fd, iov, qlen > MAXIOV ? MAXIOV : qlen);
- else
- written = 0;
- if (written < 0) {
- if (errno == ERRNO_BLOCK || errno == EINTR) {
- driver_select(ttysl_port,(ErlDrvEvent)(long)ttysl_fd,
- ERL_DRV_USE|ERL_DRV_WRITE,1);
- break;
- } else {
- DEBUGLOG(("ttysl_from_erlang: driver failure in writev(%d,..) = %d (errno = %d)\n", ttysl_fd, written, errno));
- driver_failure_posix(ttysl_port, errno);
- return;
- }
- } else {
- if (driver_deq(ttysl_port, written) == 0)
- break;
- }
- }
- }
-
- if (buf[0] == OP_PUTC_SYNC) {
- if (driver_sizeq(ttysl_port) > TTY_BUFFSIZE && !ttysl_terminate) {
- /* We delay sending the ack until the buffer has been consumed */
- ttysl_send_ok = 1;
- } else {
- ErlDrvTermData spec[] = {
- ERL_DRV_PORT, driver_mk_port(ttysl_port),
- ERL_DRV_ATOM, driver_mk_atom("ok"),
- ERL_DRV_TUPLE, 2
- };
- ASSERT(ttysl_send_ok == 0);
- erl_drv_output_term(driver_mk_port(ttysl_port), spec,
- sizeof(spec) / sizeof(spec[0]));
- }
- }
-
- return; /* TRUE; */
-}
-
-static void ttysl_to_tty(ErlDrvData ttysl_data, ErlDrvEvent fd) {
- for (;;) {
- int written, qlen;
- SysIOVec *iov;
- ErlDrvSizeT sz;
-
- iov = driver_peekq(ttysl_port,&qlen);
-
- DEBUGLOG(("ttysl_to_tty: qlen = %d", qlen));
-
- if (iov)
- written = writev(ttysl_fd, iov, qlen > MAXIOV ? MAXIOV : qlen);
- else
- written = 0;
- if (written < 0) {
- if (errno == EINTR) {
- continue;
- } else if (errno != ERRNO_BLOCK){
- DEBUGLOG(("ttysl_to_tty: driver failure in writev(%d,..) = %d (errno = %d)\n", ttysl_fd, written, errno));
- driver_failure_posix(ttysl_port, errno);
- }
- break;
- } else {
- sz = driver_deq(ttysl_port, written);
- if (sz < TTY_BUFFSIZE && ttysl_send_ok) {
- ErlDrvTermData spec[] = {
- ERL_DRV_PORT, driver_mk_port(ttysl_port),
- ERL_DRV_ATOM, driver_mk_atom("ok"),
- ERL_DRV_TUPLE, 2
- };
- ttysl_send_ok = 0;
- erl_drv_output_term(driver_mk_port(ttysl_port), spec,
- sizeof(spec) / sizeof(spec[0]));
- }
- if (sz == 0) {
- driver_select(ttysl_port,(ErlDrvEvent)(long)ttysl_fd,
- ERL_DRV_WRITE,0);
- if (ttysl_terminate) {
- /* flush has been called, which means we should terminate
- when queue is empty. This will not send any exit
- message */
- DEBUGLOG(("ttysl_to_tty: ttysl_terminate normal\n"));
- driver_failure_atom(ttysl_port, "normal");
- }
- break;
- }
- }
- }
-
- return;
-}
-
-static void ttysl_flush_tty(ErlDrvData ttysl_data) {
- DEBUGLOG(("ttysl_flush_tty: .."));
- ttysl_terminate = 1;
- return;
-}
-
-static void ttysl_from_tty(ErlDrvData ttysl_data, ErlDrvEvent fd)
-{
- byte b[1024];
- ssize_t i;
- int ch = 0, pos = 0;
- int left = 1024;
- byte *p = b;
- byte t[1024];
- int tpos;
-
- if (utf8buf_size > 0) {
- memcpy(b,utf8buf,utf8buf_size);
- left -= utf8buf_size;
- p += utf8buf_size;
- utf8buf_size = 0;
- }
-
- DEBUGLOG(("ttysl_from_tty: remainder = %d", left));
-
- if ((i = read((int)(SWord)fd, (char *) p, left)) >= 0) {
- if (p != b) {
- i += (p - b);
- }
- if (utf8_mode) { /* Hopefully an UTF8 terminal */
- while(pos < i && (ch = pick_utf8(b,i,&pos)) >= 0)
- ;
- if (ch == -2 && i - pos <= 4) {
- /* bytes left to care for */
- utf8buf_size = i -pos;
- memcpy(utf8buf,b+pos,utf8buf_size);
- } else if (ch == -1) {
- DEBUGLOG(("ttysl_from_tty: Giving up on UTF8 mode, invalid character"));
- utf8_mode = 0;
- goto latin_terminal;
- }
- driver_output(ttysl_port, (char *) b, pos);
- } else {
- latin_terminal:
- tpos = 0;
- while (pos < i) {
- while (tpos < 1020 && pos < i) { /* Max 4 bytes for UTF8 */
- put_utf8((int) b[pos++], t, 1024, &tpos);
- }
- driver_output(ttysl_port, (char *) t, tpos);
- tpos = 0;
- }
- }
- } else if (errno != EAGAIN && errno != EWOULDBLOCK) {
- DEBUGLOG(("ttysl_from_tty: driver failure in read(%d,..) = %d (errno = %d)\n", (int)(SWord)fd, i, errno));
- driver_failure(ttysl_port, -1);
- }
-}
-
-static void ttysl_stop_select(ErlDrvEvent e, void* _)
-{
- int fd = (int)(long)e;
- if (fd != 0) {
- close(fd);
- }
-}
-
-/* Procedures for putting and getting integers to/from strings. */
-static Sint16 get_sint16(char *s)
-{
- return ((*s << 8) | ((byte*)s)[1]);
-}
-
-static int start_lbuf(void)
-{
- if (!lbuf && !(lbuf = ( Uint32*) driver_alloc(lbuf_size * sizeof(Uint32))))
- return FALSE;
- llen = 0;
- lpos = 0;
- return TRUE;
-}
-
-static int stop_lbuf(void)
-{
- if (lbuf) {
- driver_free(lbuf);
- lbuf = NULL;
- }
- return TRUE;
-}
-
-/* Put l bytes (in UTF8) from s into the buffer and output them. */
-static int put_chars(byte *s, int l)
-{
- int n;
-
- n = insert_buf(s, l);
- if (lpos > llen)
- llen = lpos;
- if (n > 0)
- write_buf(lbuf + lpos - n, n, 0);
- return TRUE;
-}
-
-/*
- * Move the current position forwards or backwards within the current
- * line. We know about padding.
- */
-static int move_rel(int n)
-{
- int npos; /* The new position */
-
- /* Step forwards or backwards over the buffer. */
- npos = step_over_chars(n);
-
- /* Calculate move, updates pointers and move the cursor. */
- move_cursor(lpos, npos);
- lpos = npos;
- return TRUE;
-}
-
-/* Insert characters into the buffer at the current position. */
-static int ins_chars(byte *s, int l)
-{
- int n, tl;
- Uint32 *tbuf = NULL; /* Suppress warning about use-before-set */
-
- /* Move tail of buffer to make space. */
- if ((tl = llen - lpos) > 0) {
- if ((tbuf = driver_alloc(tl * sizeof(Uint32))) == NULL)
- return FALSE;
- memcpy(tbuf, lbuf + lpos, tl * sizeof(Uint32));
- }
- n = insert_buf(s, l);
- if (tl > 0) {
- memcpy(lbuf + lpos, tbuf, tl * sizeof(Uint32));
- driver_free(tbuf);
- }
- llen += n;
- write_buf(lbuf + (lpos - n), llen - (lpos - n), 0);
- move_cursor(llen, lpos);
- return TRUE;
-}
-
-/*
- * Delete characters in the buffer. Can delete characters before (n < 0)
- * and after (n > 0) the current position. Cursor left at beginning of
- * deleted block.
- */
-static int del_chars(int n)
-{
- int i, l, r;
- int pos;
- int gcs; /* deleted grapheme characters */
-
- update_cols();
-
- /* Step forward or backwards over n logical characters. */
- pos = step_over_chars(n);
- DEBUGLOG(("del_chars: %d from %d %d %d\n", n, lpos, pos, llen));
- if (pos > lpos) {
- l = pos - lpos; /* Buffer characters to delete */
- r = llen - lpos - l; /* Characters after deleted */
- gcs = cp_pos_to_col(pos) - cp_pos_to_col(lpos);
- /* Fix up buffer and buffer pointers. */
- if (r > 0)
- memmove(lbuf + lpos, lbuf + pos, r * sizeof(Uint32));
- llen -= l;
- /* Write out characters after, blank the tail and jump back to lpos. */
- write_buf(lbuf + lpos, r, 0);
- for (i = gcs ; i > 0; --i)
- outc(' ');
- if (xn && COL(cp_pos_to_col(llen)+gcs) == 0)
- {
- outc(' ');
- move_left(1);
- }
- move_cursor(llen + gcs, lpos);
- }
- else if (pos < lpos) {
- l = lpos - pos; /* Buffer characters */
- r = llen - lpos; /* Characters after deleted */
- gcs = -move_cursor(lpos, lpos-l); /* Move back */
- /* Fix up buffer and buffer pointers. */
- if (r > 0)
- memmove(lbuf + pos, lbuf + lpos, r * sizeof(Uint32));
- lpos -= l;
- llen -= l;
- /* Write out characters after, blank the tail and jump back to lpos. */
- write_buf(lbuf + lpos, r, 0);
- for (i = gcs ; i > 0; --i)
- outc(' ');
- if (xn && COL(cp_pos_to_col(llen)+gcs) == 0)
- {
- outc(' ');
- move_left(1);
- }
- move_cursor(llen + gcs, lpos);
- }
- return TRUE;
-}
-
-/* Step over n logical characters, check for overflow. */
-static int step_over_chars(int n)
-{
- Uint32 *c, *beg, *end;
-
- beg = lbuf;
- end = lbuf + llen;
- c = lbuf + lpos;
- for ( ; n > 0 && c < end; --n) {
- c++;
- while (c < end && (*c & TAG_MASK) && ((*c & ~TAG_MASK) == 0))
- c++;
- }
- for ( ; n < 0 && c > beg; n++) {
- --c;
- while (c > beg && (*c & TAG_MASK) && ((*c & ~TAG_MASK) == 0))
- --c;
- }
- return c - lbuf;
-}
-
-/*
- * Insert n characters into the buffer at lpos.
- * Know about pad characters and treat \n specially.
- */
-
-static int insert_buf(byte *s, int n)
-{
- int pos = 0;
- int buffpos = lpos;
- int ch;
-
- while (pos < n) {
- if ((ch = pick_utf8(s,n,&pos)) < 0) {
- /* XXX temporary allow invalid chars */
- ch = (int) s[pos];
- DEBUGLOG(("insert_buf: Invalid UTF8:%d",ch));
- ++pos;
- }
- if ((utf8_mode && (ch >= 128 || isprint(ch))) || (ch <= 255 && isprint(ch))) {
- DEBUGLOG(("insert_buf: Printable(UTF-8):%d",ch));
- lbuf[lpos++] = (Uint32) ch;
- } else if (ch >= 128) { /* not utf8 mode */
- int nc = octal_or_hex_positions(ch);
- lbuf[lpos++] = ((Uint32) ch) | ESCAPED_TAG;
- while (nc--) {
- lbuf[lpos++] = ESCAPED_TAG;
- }
- } else if (ch == '\t') {
- do {
- lbuf[lpos++] = (CONTROL_TAG | ((Uint32) ch));
- ch = 0;
- } while (lpos % 8);
- } else if (ch == '\e') {
- DEBUGLOG(("insert_buf: ANSI Escape: \\e"));
- lbuf[lpos++] = (CONTROL_TAG | ((Uint32) ch));
- } else if (ch == '\n' || ch == '\r') {
- write_buf(lbuf + buffpos, lpos - buffpos, 1);
- outc('\r');
- if (ch == '\n')
- outc('\n');
- if (llen > lpos) {
- memmove(lbuf, lbuf + lpos, llen - lpos);
- }
- llen -= lpos;
- lpos = buffpos = 0;
- } else {
- DEBUGLOG(("insert_buf: Magic(UTF-8):%d",ch));
- lbuf[lpos++] = ch | CONTROL_TAG;
- lbuf[lpos++] = CONTROL_TAG;
- }
- }
- return lpos - buffpos; /* characters "written" into
- current buffer (may be less due to newline) */
-}
-
-
-
-/*
- * Write n characters in line buffer starting at s. Be smart about
- * non-printables. Know about pad characters and that \n can never
- * occur normally.
- */
-
-static int write_buf(Uint32 *s, int n, int next_char_is_crnl)
-{
- byte ubuf[4];
- int ubytes = 0, i;
- byte lastput = ' ';
-
- update_cols();
-
- DEBUGLOG(("write_buf(%d, %d)",n,next_char_is_crnl));
-
- while (n > 0) {
- if (!(*s & TAG_MASK) ) {
- if (utf8_mode) {
- ubytes = 0;
- if (put_utf8((int) *s, ubuf, 4, &ubytes) == 0) {
- for (i = 0; i < ubytes; ++i) {
- outc(ubuf[i]);
- }
- lastput = 0; /* Means the last written character was multibyte UTF8 */
- }
- } else {
- outc((byte) *s);
- lastput = (byte) *s;
- }
- --n;
- ++s;
- } else if (*s == (CONTROL_TAG | ((Uint32) '\t'))) {
- outc(lastput = ' ');
- --n; s++;
- while (n > 0 && *s == CONTROL_TAG) {
- outc(lastput = ' ');
- --n; s++;
- }
- } else if (*s == (CONTROL_TAG | ((Uint32) '\e'))) {
- outc(lastput = '\e');
- --n;
- ++s;
- } else if (*s & CONTROL_TAG) {
- byte c = (byte)*s;
- outc('^');
- outc(lastput = ((byte) ((c == 0177 ? '?' : c | 0x40))));
- n -= 2;
- s += 2;
- } else if (*s & ESCAPED_TAG) {
- Uint32 ch = *s & ~(TAG_MASK);
- byte *octbuff;
- byte octtmp[256];
- int octbytes;
- DEBUGLOG(("write_buf: Escaped: %d", ch));
- octbytes = octal_or_hex_positions(ch);
- if (octbytes > 256) {
- octbuff = driver_alloc(octbytes);
- } else {
- octbuff = octtmp;
- }
- octbytes = 0;
- octal_or_hex_format(ch, octbuff, &octbytes);
- DEBUGLOG(("write_buf: octbytes: %d", octbytes));
- outc('\\');
- for (i = 0; i < octbytes; ++i) {
- outc(lastput = octbuff[i]);
- DEBUGLOG(("write_buf: outc: %d", (int) lastput));
- }
- n -= octbytes+1;
- s += octbytes+1;
- if (octbuff != octtmp) {
- driver_free(octbuff);
- }
- } else {
- DEBUGLOG(("write_buf: Very unexpected character %d",(int) *s));
- ++n;
- --s;
- }
- }
- /* Check landed in first column of new line and have 'xn' bug.
- * https://www.gnu.org/software/termutils/manual/termcap-1.3/html_node/termcap_27.html
- *
- * The 'xn' bugs (from what I understand) is that the terminal cursor does
- * not wrap to the next line when the current line is full. For example:
- *
- * If the terminal column size is 20 and we output 20 'a' the cursor will be
- * on row 1, column 21. While we actually want it at row 2 column 0. So to
- * achieve this the code below emits " \b", which will move the cursor to the
- * correct place.
- *
- * We should not apply this 'xn' workaround if we know that the next character
- * to be emitted is a cr|nl as that will wrap by itself.
- */
- n = s - lbuf;
- if (!next_char_is_crnl && xn && n != 0 && COL(cp_pos_to_col(n)) == 0) {
- if (n >= llen) {
- outc(' ');
- } else if (lastput == 0) { /* A multibyte UTF8 character */
- for (i = 0; i < ubytes; ++i) {
- outc(ubuf[i]);
- }
- } else {
- outc(lastput);
- }
- move_left(1);
- }
- return TRUE;
-}
-
-
-/* The basic procedure for outputting one character. */
-static int outc(int c)
-{
- putcbuf->orig_bytes[putcpos++] = c;
- if (putcpos == putclen) {
- driver_enq_bin(ttysl_port,putcbuf,0,putclen);
- driver_free_binary(putcbuf);
- putcpos = 0;
- putclen = TTY_BUFFSIZE;
- putcbuf = driver_alloc_binary(BUFSIZ);
- }
- return 1;
-}
-
-static int move_cursor(int from_pos, int to_pos)
-{
- int from_col, to_col;
- int dc, dl;
- update_cols();
-
- from_col = cp_pos_to_col(from_pos);
- to_col = cp_pos_to_col(to_pos);
-
- dc = COL(to_col) - COL(from_col);
- dl = LINE(to_col) - LINE(from_col);
- DEBUGLOG(("move_cursor: from %d %d to %d %d => %d %d\n",
- from_pos, from_col, to_pos, to_col, dl, dc));
- if (dl > 0)
- move_down(dl);
- else if (dl < 0)
- move_up(-dl);
- if (dc > 0)
- move_right(dc);
- else if (dc < 0)
- move_left(-dc);
- return to_col-from_col;
-}
-
-/*
- * Returns the length of an ANSI escape code in a buffer, this function only consider
- * color escape sequences like `\e[33m` or `\e[21;33m`. If a sequence has no valid
- * terminator, the length is equal the number of characters between `\e` and the first
- * invalid character, inclusive.
- */
-
-static int ansi_escape_width(Uint32 *s, int max_length)
-{
- int i;
-
- if (*s != (CONTROL_TAG | ((Uint32) '\e'))) {
- return 0;
- } else if (max_length <= 1) {
- return 1;
- } else if (s[1] != '[') {
- return 2;
- }
-
- for (i = 2; i < max_length && (s[i] == ';' || (s[i] >= '0' && s[i] <= '9')); i++);
-
- return i + 1;
-}
-
-static int cp_pos_to_col(int cp_pos)
-{
- /*
- * If we don't have any character width information. Assume that
- * code points are one column wide
- */
- int w = 1;
- int col = 0;
- int i = 0;
- int j;
-
- if (cp_pos > llen) {
- col += cp_pos - llen;
- cp_pos = llen;
- }
-
- while (i < cp_pos) {
- j = ansi_escape_width(lbuf + i, llen - i);
-
- if (j > 0) {
- i += j;
- } else {
-#ifdef HAVE_WCWIDTH
- w = wcwidth(lbuf[i]);
-#endif
- if (w > 0) {
- col += w;
- }
- i++;
- }
- }
-
- return col;
-}
-
-static int start_termcap(void)
-{
- int eres;
- size_t envsz = 1024;
- char *env = NULL;
- char *c;
- int tres;
-
- DEBUGLOG(("start_termcap: .."));
-
- capbuf = driver_alloc(1024);
- if (!capbuf)
- goto termcap_false;
- eres = erl_drv_getenv("TERM", capbuf, &envsz);
- if (eres == 0)
- env = capbuf;
- else if (eres < 0) {
- DEBUGLOG(("start_termcap: failure in erl_drv_getenv(\"TERM\", ..) = %d\n", eres));
- goto termcap_false;
- } else /* if (eres > 1) */ {
- char *envbuf = driver_alloc(envsz);
- if (!envbuf)
- goto termcap_false;
- while (1) {
- char *newenvbuf;
- eres = erl_drv_getenv("TERM", envbuf, &envsz);
- if (eres == 0)
- break;
- newenvbuf = driver_realloc(envbuf, envsz);
- if (eres < 0 || !newenvbuf) {
- DEBUGLOG(("start_termcap: failure in erl_drv_getenv(\"TERM\", ..) = %d or realloc buf == %p\n", eres, newenvbuf));
- env = newenvbuf ? newenvbuf : envbuf;
- goto termcap_false;
- }
- envbuf = newenvbuf;
- }
- env = envbuf;
- }
- if ((tres = tgetent((char*)lbuf, env)) <= 0) {
- DEBUGLOG(("start_termcap: failure in tgetent(..) = %d\n", tres));
- goto termcap_false;
- }
- if (env != capbuf) {
- env = NULL;
- driver_free(env);
- }
- c = capbuf;
- cols = tgetnum("co");
- if (cols <= 0)
- cols = DEF_WIDTH;
- xn = tgetflag("xn");
- up = tgetstr("up", &c);
- if (!(down = tgetstr("do", &c)))
- down = "\n";
- if (!(left = tgetflag("bs") ? "\b" : tgetstr("bc", &c)))
- left = "\b"; /* Can't happen - but does on Solaris 2 */
- right = tgetstr("nd", &c);
- if (up && down && left && right) {
- DEBUGLOG(("start_termcap: successful start\n"));
- return TRUE;
- }
- DEBUGLOG(("start_termcap: failed start\n"));
- termcap_false:
- if (env && env != capbuf)
- driver_free(env);
- if (capbuf)
- driver_free(capbuf);
- capbuf = NULL;
- return FALSE;
-}
-
-static int stop_termcap(void)
-{
- if (capbuf) driver_free(capbuf);
- capbuf = NULL;
- return TRUE;
-}
-
-static int move_left(int n)
-{
- while (n-- > 0)
- tputs(left, 1, outc);
- return TRUE;
-}
-
-static int move_right(int n)
-{
- while (n-- > 0)
- tputs(right, 1, outc);
- return TRUE;
-}
-
-static int move_up(int n)
-{
- while (n-- > 0)
- tputs(up, 1, outc);
- return TRUE;
-}
-
-static int move_down(int n)
-{
- while (n-- > 0)
- tputs(down, 1, outc);
- return TRUE;
-}
-
-
-/*
- * Updates cols if terminal has resized (SIGWINCH). Should be called
- * at the start of any function that uses the COL or LINE macros. If
- * the terminal is resized after calling this function but before use
- * of the macros, then we may write to the wrong screen location.
- *
- * We cannot call this from the SIGWINCH handler because it uses
- * ioctl() which is not a safe function as listed in the signal(7)
- * man page.
- */
-static void update_cols(void)
-{
- Uint32 width, height;
-
- if (cols_needs_update) {
- cols_needs_update = FALSE;
- ttysl_get_window_size(&width, &height);
- cols = width;
- }
-}
-
-
-/*
- * Put a terminal device into non-canonical mode with ECHO off.
- * Before doing so we first save the terminal's current mode,
- * assuming the caller will call the tty_reset() function
- * (also in this file) when it's done with raw mode.
- */
-
-static struct termios tty_smode, tty_rmode;
-
-static int tty_init(int fd, int canon, int echo, int sig) {
- int tres;
- DEBUGLOG(("tty_init: fd = %d, canon = %d, echo = %d, sig = %d", fd, canon, echo, sig));
- if ((tres = tcgetattr(fd, &tty_rmode)) < 0) {
- DEBUGLOG(("tty_init: failure in tcgetattr(%d,..) = %d\n", fd, tres));
- return -1;
- }
- tty_smode = tty_rmode;
-
- /* Default characteristics for all usage including termcap output. */
- tty_smode.c_iflag &= ~ISTRIP;
-
- /* Turn canonical (line mode) on off. */
- if (canon > 0) {
- tty_smode.c_iflag |= ICRNL;
- tty_smode.c_lflag |= ICANON;
- tty_smode.c_oflag |= OPOST;
- tty_smode.c_cc[VEOF] = tty_rmode.c_cc[VEOF];
-#ifdef VDSUSP
- tty_smode.c_cc[VDSUSP] = tty_rmode.c_cc[VDSUSP];
-#endif
- }
- if (canon < 0) {
- tty_smode.c_iflag &= ~ICRNL;
- tty_smode.c_lflag &= ~ICANON;
- tty_smode.c_oflag &= ~OPOST;
- /* Must get these really right or funny effects can occur. */
- tty_smode.c_cc[VMIN] = 1;
- tty_smode.c_cc[VTIME] = 0;
-#ifdef VDSUSP
- tty_smode.c_cc[VDSUSP] = 0;
-#endif
- }
-
- /* Turn echo on or off. */
- if (echo > 0)
- tty_smode.c_lflag |= ECHO;
- if (echo < 0)
- tty_smode.c_lflag &= ~ECHO;
-
- /* Set extra characteristics for "RAW" mode, no signals. */
- if (sig > 0) {
- /* Ignore IMAXBEL as not POSIX. */
-#ifndef QNX
- tty_smode.c_iflag |= (BRKINT|IGNPAR|ICRNL|IXON|IXANY);
-#else
- tty_smode.c_iflag |= (BRKINT|IGNPAR|ICRNL|IXON);
-#endif
- tty_smode.c_lflag |= (ISIG|IEXTEN);
- }
- if (sig < 0) {
- /* Ignore IMAXBEL as not POSIX. */
-#ifndef QNX
- tty_smode.c_iflag &= ~(BRKINT|IGNPAR|ICRNL|IXON|IXANY);
-#else
- tty_smode.c_iflag &= ~(BRKINT|IGNPAR|ICRNL|IXON);
-#endif
- tty_smode.c_lflag &= ~(ISIG|IEXTEN);
- }
- DEBUGLOG(("tty_init: successful init\n"));
- return 0;
-}
-
-/*
- * Set/restore a terminal's mode to whatever it was on the most
- * recent call to the tty_init() function above.
- */
-
-static int tty_set(int fd)
-{
- int tres;
- DEBUGF(("tty_set: Setting tty...\n"));
-
- if ((tres = tcsetattr(fd, TCSANOW, &tty_smode)) < 0) {
- DEBUGLOG(("tty_set: failure in tcgetattr(%d,..) = %d\n", fd, tres));
- return(-1);
- }
- return(0);
-}
-
-static int tty_reset(int fd) /* of terminal device */
-{
- int tres;
- DEBUGF(("tty_reset: Resetting tty...\n"));
-
- if ((tres = tcsetattr(fd, TCSANOW, &tty_rmode)) < 0) {
- DEBUGLOG(("tty_reset: failure in tcsetattr(%d,..) = %d\n", fd, tres));
- return(-1);
- }
- return(0);
-}
-
-/*
- * Signal handler to cope with signals so that we can reset the tty
- * to the original settings
- */
-
-#ifdef ERTS_NOT_USED
-/* XXX: A mistake that it isn't used, or should it be removed? */
-
-static RETSIGTYPE suspend(int sig)
-{
- if (tty_reset(ttysl_fd) < 0) {
- DEBUGLOG(("signal: failure in suspend(%d), can't reset tty %d\n", sig, ttysl_fd));
- fprintf(stderr,"Can't reset tty \n");
- exit(1);
- }
-
- sys_signal(sig, SIG_DFL); /* Set signal handler to default */
- sys_sigrelease(sig); /* Allow 'sig' to come through */
- kill(getpid(), sig); /* Send ourselves the signal */
- sys_sigblock(sig); /* Reset to old mask */
- sys_signal(sig, suspend); /* Reset signal handler */
-
- if (tty_set(ttysl_fd) < 0) {
- DEBUGLOG(("signal: failure in suspend(%d), can't set tty %d\n", sig, ttysl_fd));
- fprintf(stderr,"Can't set tty raw \n");
- exit(1);
- }
-}
-
-#endif
-
-static RETSIGTYPE cont(int sig)
-{
- if (tty_set(ttysl_fd) < 0) {
- DEBUGLOG(("signal: failure in cont(%d), can't set tty raw %d\n", sig, ttysl_fd));
- fprintf(stderr,"Can't set tty raw\n");
- exit(1);
- }
-}
-
-static RETSIGTYPE winch(int sig)
-{
- cols_needs_update = TRUE;
-}
-#endif /* HAVE_TERMCAP */
diff --git a/erts/emulator/drivers/win32/ttsl_drv.c b/erts/emulator/drivers/win32/ttsl_drv.c
deleted file mode 100644
index 8917e48919f6..000000000000
--- a/erts/emulator/drivers/win32/ttsl_drv.c
+++ /dev/null
@@ -1,786 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1996-2021. All Rights Reserved.
- *
- * Licensed 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.
- *
- * %CopyrightEnd%
- */
-/*
- * Tty driver that reads one character at the time and provides a
- * smart line for output.
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-#include "sys.h"
-#include
-#include
-#include
-#include
-#include
-
-#include "erl_driver.h"
-#include "win_con.h"
-
-#define TRUE 1
-#define FALSE 0
-
-static int cols; /* Number of columns available. */
-static int rows; /* Number of rows available. */
-
-/* The various opcodes. */
-#define OP_PUTC 0
-#define OP_MOVE 1
-#define OP_INSC 2
-#define OP_DELC 3
-#define OP_BEEP 4
-#define OP_PUTC_SYNC 5
-
-/* Control op */
-#define CTRL_OP_GET_WINSIZE 100
-#define CTRL_OP_GET_UNICODE_STATE 101
-#define CTRL_OP_SET_UNICODE_STATE 102
-
-static int lbuf_size = BUFSIZ;
-Uint32 *lbuf; /* The current line buffer */
-int llen; /* The current line length */
-int lpos; /* The current "cursor position" in the line buffer */
-
-/*
- * Tags used in line buffer to show that these bytes represent special characters,
- * Max unicode is 0x0010ffff, so we have lots of place for meta tags...
- */
-#define CONTROL_TAG 0x10000000U /* Control character, value in first position */
-#define ESCAPED_TAG 0x01000000U /* Escaped character, value in first position */
-#define TAG_MASK 0xFF000000U
-
-#define MAXSIZE (1 << 16)
-
-#define ISPRINT(c) (isprint(c) || (128+32 <= (c) && (c) < 256))
-
-#define DEBUGLOG(X) /* nothing */
-
-/*
- * XXX These are used by win_con.c (for command history).
- * Should be cleaned up.
- */
-
-
-#define NL '\n'
-
-/* Main interface functions. */
-static int ttysl_init(void);
-static ErlDrvData ttysl_start(ErlDrvPort, char*);
-static void ttysl_stop(ErlDrvData);
-static ErlDrvSSizeT ttysl_control(ErlDrvData, unsigned int,
- char *, ErlDrvSizeT, char **, ErlDrvSizeT);
-static void ttysl_from_erlang(ErlDrvData, char*, ErlDrvSizeT);
-static void ttysl_from_tty(ErlDrvData, ErlDrvEvent);
-static Sint16 get_sint16(char *s);
-
-static ErlDrvPort ttysl_port;
-
-extern ErlDrvEvent console_input_event;
-extern HANDLE console_thread;
-
-static HANDLE ttysl_in = INVALID_HANDLE_VALUE; /* Handle for console input. */
-static HANDLE ttysl_out = INVALID_HANDLE_VALUE; /* Handle for console output */
-
-/* Functions that work on the line buffer. */
-static int start_lbuf();
-static int stop_lbuf();
-static int put_chars();
-static int move_rel();
-static int ins_chars();
-static int del_chars();
-static int step_over_chars(int n);
-static int insert_buf();
-static int write_buf();
-static void move_cursor(int, int);
-
-/* Define the driver table entry. */
-struct erl_drv_entry ttsl_driver_entry = {
- ttysl_init,
- ttysl_start,
- ttysl_stop,
- ttysl_from_erlang,
- ttysl_from_tty,
- NULL,
- "tty_sl",
- NULL,
- NULL,
- ttysl_control,
- NULL, /* timeout */
- NULL, /* outputv */
- NULL, /* ready_async */
- NULL, /* flush */
- NULL, /* call */
- NULL, /* event */
- ERL_DRV_EXTENDED_MARKER,
- ERL_DRV_EXTENDED_MAJOR_VERSION,
- ERL_DRV_EXTENDED_MINOR_VERSION,
- 0,
- NULL,
- NULL,
- NULL,
-};
-
-static int utf8_mode = 0;
-
-static int ttysl_init()
-{
- lbuf = NULL; /* For line buffer handling */
- ttysl_port = (ErlDrvPort)-1;
- return 0;
-}
-
-static ErlDrvData ttysl_start(ErlDrvPort port, char* buf)
-{
- if ((SWord)ttysl_port != -1 || console_thread == NULL) {
- return ERL_DRV_ERROR_GENERAL;
- }
- start_lbuf();
- utf8_mode = 1;
- driver_select(port, console_input_event, ERL_DRV_READ, 1);
- ttysl_port = port;
- return (ErlDrvData)ttysl_port;/* Nothing important to return */
-}
-
-#define DEF_HEIGHT 24
-#define DEF_WIDTH 80
-
-static void ttysl_get_window_size(Uint32 *width, Uint32 *height)
-{
- *width = ConGetColumns();
- *height = ConGetRows();
-}
-
-
-static ErlDrvSSizeT ttysl_control(ErlDrvData drv_data,
- unsigned int command,
- char *buf, ErlDrvSizeT len,
- char **rbuf, ErlDrvSizeT rlen)
-{
- char resbuff[2*sizeof(Uint32)];
- ErlDrvSizeT res_size;
-
- command -= ERTS_TTYSL_DRV_CONTROL_MAGIC_NUMBER;
- switch (command) {
- case CTRL_OP_GET_WINSIZE:
- {
- Uint32 w,h;
- ttysl_get_window_size(&w,&h);
- memcpy(resbuff,&w,sizeof(Uint32));
- memcpy(resbuff+sizeof(Uint32),&h,sizeof(Uint32));
- res_size = 2*sizeof(Uint32);
- }
- break;
- case CTRL_OP_GET_UNICODE_STATE:
- *resbuff = (utf8_mode) ? 1 : 0;
- res_size = 1;
- break;
- case CTRL_OP_SET_UNICODE_STATE:
- if (len != 0) {
- int m = (int) *buf;
- *resbuff = (utf8_mode) ? 1 : 0;
- res_size = 1;
- utf8_mode = (m) ? 1 : 0;
- } else {
- return 0;
- }
- break;
- default:
- return -1;
- }
- if (rlen < res_size) {
- *rbuf = driver_alloc(res_size);
- }
- memcpy(*rbuf,resbuff,res_size);
- return res_size;
-}
-
-
-static void ttysl_stop(ErlDrvData ttysl_data)
-{
- if ((SWord)ttysl_port != -1) {
- driver_select(ttysl_port, console_input_event, ERL_DRV_READ, 0);
- }
-
- ttysl_in = ttysl_out = INVALID_HANDLE_VALUE;
- stop_lbuf();
- ttysl_port = (ErlDrvPort)-1;
-}
-
-static int put_utf8(int ch, byte *target, int sz, int *pos)
-{
- Uint x = (Uint) ch;
- if (x < 0x80) {
- if (*pos >= sz) {
- return -1;
- }
- target[(*pos)++] = (byte) x;
- }
- else if (x < 0x800) {
- if (((*pos) + 1) >= sz) {
- return -1;
- }
- target[(*pos)++] = (((byte) (x >> 6)) |
- ((byte) 0xC0));
- target[(*pos)++] = (((byte) (x & 0x3F)) |
- ((byte) 0x80));
- } else if (x < 0x10000) {
- if ((x >= 0xD800 && x <= 0xDFFF) ||
- (x == 0xFFFE) ||
- (x == 0xFFFF)) { /* Invalid unicode range */
- return -1;
- }
- if (((*pos) + 2) >= sz) {
- return -1;
- }
-
- target[(*pos)++] = (((byte) (x >> 12)) |
- ((byte) 0xE0));
- target[(*pos)++] = ((((byte) (x >> 6)) & 0x3F) |
- ((byte) 0x80));
- target[(*pos)++] = (((byte) (x & 0x3F)) |
- ((byte) 0x80));
- } else if (x < 0x110000) { /* Standard imposed max */
- if (((*pos) + 3) >= sz) {
- return -1;
- }
- target[(*pos)++] = (((byte) (x >> 18)) |
- ((byte) 0xF0));
- target[(*pos)++] = ((((byte) (x >> 12)) & 0x3F) |
- ((byte) 0x80));
- target[(*pos)++] = ((((byte) (x >> 6)) & 0x3F) |
- ((byte) 0x80));
- target[(*pos)++] = (((byte) (x & 0x3F)) |
- ((byte) 0x80));
- } else {
- return -1;
- }
- return 0;
-}
-
-
-static int pick_utf8(byte *s, int sz, int *pos)
-{
- int size = sz - (*pos);
- byte *source;
- Uint unipoint;
-
- if (size > 0) {
- source = s + (*pos);
- if (((*source) & ((byte) 0x80)) == 0) {
- unipoint = (int) *source;
- ++(*pos);
- return (int) unipoint;
- } else if (((*source) & ((byte) 0xE0)) == 0xC0) {
- if (size < 2) {
- return -2;
- }
- if (((source[1] & ((byte) 0xC0)) != 0x80) ||
- ((*source) < 0xC2) /* overlong */) {
- return -1;
- }
- (*pos) += 2;
- unipoint =
- (((Uint) ((*source) & ((byte) 0x1F))) << 6) |
- ((Uint) (source[1] & ((byte) 0x3F)));
- return (int) unipoint;
- } else if (((*source) & ((byte) 0xF0)) == 0xE0) {
- if (size < 3) {
- return -2;
- }
- if (((source[1] & ((byte) 0xC0)) != 0x80) ||
- ((source[2] & ((byte) 0xC0)) != 0x80) ||
- (((*source) == 0xE0) && (source[1] < 0xA0)) /* overlong */ ) {
- return -1;
- }
- if ((((*source) & ((byte) 0xF)) == 0xD) &&
- ((source[1] & 0x20) != 0)) {
- return -1;
- }
- if (((*source) == 0xEF) && (source[1] == 0xBF) &&
- ((source[2] == 0xBE) || (source[2] == 0xBF))) {
- return -1;
- }
- (*pos) += 3;
- unipoint =
- (((Uint) ((*source) & ((byte) 0xF))) << 12) |
- (((Uint) (source[1] & ((byte) 0x3F))) << 6) |
- ((Uint) (source[2] & ((byte) 0x3F)));
- return (int) unipoint;
- } else if (((*source) & ((byte) 0xF8)) == 0xF0) {
- if (size < 4) {
- return -2 ;
- }
- if (((source[1] & ((byte) 0xC0)) != 0x80) ||
- ((source[2] & ((byte) 0xC0)) != 0x80) ||
- ((source[3] & ((byte) 0xC0)) != 0x80) ||
- (((*source) == 0xF0) && (source[1] < 0x90)) /* overlong */) {
- return -1;
- }
- if ((((*source) & ((byte)0x7)) > 0x4U) ||
- ((((*source) & ((byte)0x7)) == 0x4U) &&
- ((source[1] & ((byte)0x3F)) > 0xFU))) {
- return -1;
- }
- (*pos) += 4;
- unipoint =
- (((Uint) ((*source) & ((byte) 0x7))) << 18) |
- (((Uint) (source[1] & ((byte) 0x3F))) << 12) |
- (((Uint) (source[2] & ((byte) 0x3F))) << 6) |
- ((Uint) (source[3] & ((byte) 0x3F)));
- return (int) unipoint;
- } else {
- return -1;
- }
- } else {
- return -1;
- }
-}
-
-static int octal_or_hex_positions(Uint c)
-{
- int x = 0;
- Uint ch = c;
- if (!ch) {
- return 1;
- }
- while(ch) {
- ++x;
- ch >>= 3;
- }
- if (x <= 3) {
- return 3;
- }
- /* \x{H ...} format when larger than \777 */
- x = 0;
- ch = c;
- while(ch) {
- ++x;
- ch >>= 4;
- }
- return x+3;
-}
-
-static void octal_or_hex_format(Uint ch, byte *buf, int *pos)
-{
- static byte hex_chars[] = { '0','1','2','3','4','5','6','7','8','9',
- 'A','B','C','D','E','F'};
- int num = octal_or_hex_positions(ch);
- if (num != 3) {
- buf[(*pos)++] = 'x';
- buf[(*pos)++] = '{';
- num -= 3;
- while(num--) {
- buf[(*pos)++] = hex_chars[((ch >> (4*num)) & 0xFU)];
- }
- buf[(*pos)++] = '}';
- } else {
- while(num--) {
- buf[(*pos)++] = ((byte) ((ch >> (3*num)) & 0x7U) + '0');
- }
- }
-}
-
-/*
- * Check that there is enough room in all buffers to copy all pad chars
- * and stiff we need If not, realloc lbuf.
- */
-static int check_buf_size(byte *s, int n)
-{
- int pos = 0;
- int ch;
- int size = 10;
-
- while(pos < n) {
- /* Indata is always UTF-8 */
- if ((ch = pick_utf8(s,n,&pos)) < 0) {
- /* XXX temporary allow invalid chars */
- ch = (int) s[pos];
- DEBUGLOG(("Invalid UTF8:%d",ch));
- ++pos;
- }
- if (utf8_mode) { /* That is, terminal is UTF8 compliant */
- if (ch >= 128 || isprint(ch)) {
- DEBUGLOG(("Printable(UTF-8:%d):%d",pos,ch));
- size++; /* Buffer contains wide characters... */
- } else if (ch == '\t') {
- size += 8;
- } else {
- DEBUGLOG(("Magic(UTF-8:%d):%d",pos,ch));
- size += 2;
- }
- } else {
- if (ch <= 255 && isprint(ch)) {
- DEBUGLOG(("Printable:%d",ch));
- size++;
- } else if (ch == '\t')
- size += 8;
- else if (ch >= 128) {
- DEBUGLOG(("Non printable:%d",ch));
- size += (octal_or_hex_positions(ch) + 1);
- }
- else {
- DEBUGLOG(("Magic:%d",ch));
- size += 2;
- }
- }
- }
-
- if (size + lpos >= lbuf_size) {
-
- lbuf_size = size + lpos + BUFSIZ;
- if ((lbuf = driver_realloc(lbuf, lbuf_size * sizeof(Uint32))) == NULL) {
- driver_failure(ttysl_port, -1);
- return(0);
- }
- }
- return(1);
-}
-
-
-static void ttysl_from_erlang(ErlDrvData ttysl_data, char* buf, ErlDrvSizeT count)
-{
- if (lpos > MAXSIZE)
- put_chars((byte*)"\n", 1);
-
- switch (buf[0]) {
- case OP_PUTC:
- case OP_PUTC_SYNC:
- DEBUGLOG(("OP: Putc(%I64u)",(unsigned long long)count-1));
- if (check_buf_size((byte*)buf+1, count-1) == 0)
- return;
- put_chars((byte*)buf+1, count-1);
- break;
- case OP_MOVE:
- move_rel(get_sint16(buf+1));
- break;
- case OP_INSC:
- if (check_buf_size((byte*)buf+1, count-1) == 0)
- return;
- ins_chars((byte*)buf+1, count-1);
- break;
- case OP_DELC:
- del_chars(get_sint16(buf+1));
- break;
- case OP_BEEP:
- ConBeep();
- break;
- default:
- /* Unknown op, just ignore. */
- break;
- }
-
- if (buf[0] == OP_PUTC_SYNC) {
- /* On windows we do a blocking write to the tty so we just
- send the ack immediately. If at some point in the future
- someone has a problem with tty output being blocking
- this has to be changed. */
- ErlDrvTermData spec[] = {
- ERL_DRV_PORT, driver_mk_port(ttysl_port),
- ERL_DRV_ATOM, driver_mk_atom("ok"),
- ERL_DRV_TUPLE, 2
- };
- erl_drv_output_term(driver_mk_port(ttysl_port), spec,
- sizeof(spec) / sizeof(spec[0]));
- }
- return;
-}
-
-extern int read_inbuf(char *data, int n);
-
-static void ttysl_from_tty(ErlDrvData ttysl_data, ErlDrvEvent fd)
-{
- Uint32 inbuf[64];
- byte t[1024];
- int i,pos,tpos;
-
- i = ConReadInput(inbuf,1);
-
- pos = 0;
- tpos = 0;
-
- while (pos < i) {
- while (tpos < 1020 && pos < i) { /* Max 4 bytes for UTF8 */
- put_utf8((int) inbuf[pos++], t, 1024, &tpos);
- }
- driver_output(ttysl_port, (char *) t, tpos);
- tpos = 0;
- }
-}
-
-/*
- * Gets signed 16 bit integer from binary buffer.
- */
-static Sint16
-get_sint16(char *s)
-{
- return ((*s << 8) | ((byte*)s)[1]);
-}
-
-
-static int start_lbuf(void)
-{
- if (!lbuf && !(lbuf = ( Uint32*) driver_alloc(lbuf_size * sizeof(Uint32))))
- return FALSE;
- llen = 0;
- lpos = 0;
- return TRUE;
-}
-
-static int stop_lbuf(void)
-{
- if (lbuf) {
- driver_free(lbuf);
- lbuf = NULL;
- }
- llen = 0; /* To avoid access error in win_con:AddToCmdHistory during exit*/
- return TRUE;
-}
-
-/* Put l bytes (in UTF8) from s into the buffer and output them. */
-static int put_chars(byte *s, int l)
-{
- int n;
-
- n = insert_buf(s, l);
- if (n > 0)
- write_buf(lbuf + lpos - n, n);
- if (lpos > llen)
- llen = lpos;
- return TRUE;
-}
-
-/*
- * Move the current position forwards or backwards within the current
- * line. We know about padding.
- */
-static int move_rel(int n)
-{
- int npos; /* The new position */
-
- /* Step forwards or backwards over the buffer. */
- npos = step_over_chars(n);
-
- /* Calculate move, updates pointers and move the cursor. */
- move_cursor(lpos, npos);
- lpos = npos;
- return TRUE;
-}
-
-/* Insert characters into the buffer at the current position. */
-static int ins_chars(byte *s, int l)
-{
- int n, tl;
- Uint32 *tbuf = NULL; /* Suppress warning about use-before-set */
-
- /* Move tail of buffer to make space. */
- if ((tl = llen - lpos) > 0) {
- if ((tbuf = driver_alloc(tl * sizeof(Uint32))) == NULL)
- return FALSE;
- memcpy(tbuf, lbuf + lpos, tl * sizeof(Uint32));
- }
- n = insert_buf(s, l);
- if (tl > 0) {
- memcpy(lbuf + lpos, tbuf, tl * sizeof(Uint32));
- driver_free(tbuf);
- }
- llen += n;
- write_buf(lbuf + (lpos - n), llen - (lpos - n));
- move_cursor(llen, lpos);
- return TRUE;
-}
-
-/*
- * Delete characters in the buffer. Can delete characters before (n < 0)
- * and after (n > 0) the current position. Cursor left at beginning of
- * deleted block.
- */
-static int del_chars(int n)
-{
- int i, l, r;
- int pos;
-
- /*update_cols();*/
-
- /* Step forward or backwards over n logical characters. */
- pos = step_over_chars(n);
-
- if (pos > lpos) {
- l = pos - lpos; /* Buffer characters to delete */
- r = llen - lpos - l; /* Characters after deleted */
- /* Fix up buffer and buffer pointers. */
- if (r > 0)
- memcpy(lbuf + lpos, lbuf + pos, r * sizeof(Uint32));
- llen -= l;
- /* Write out characters after, blank the tail and jump back to lpos. */
- write_buf(lbuf + lpos, r);
- for (i = l ; i > 0; --i)
- ConPutChar(' ');
- move_cursor(llen + l, lpos);
- }
- else if (pos < lpos) {
- l = lpos - pos; /* Buffer characters */
- r = llen - lpos; /* Characters after deleted */
- move_cursor(lpos, lpos-l); /* Move back */
- /* Fix up buffer and buffer pointers. */
- if (r > 0)
- memcpy(lbuf + pos, lbuf + lpos, r * sizeof(Uint32));
- lpos -= l;
- llen -= l;
- /* Write out characters after, blank the tail and jump back to lpos. */
- write_buf(lbuf + lpos, r);
- for (i = l ; i > 0; --i)
- ConPutChar(' ');
- move_cursor(llen + l, lpos);
- }
- return TRUE;
-}
-
-
-/* Step over n logical characters, check for overflow. */
-static int step_over_chars(int n)
-{
- Uint32 *c, *beg, *end;
-
- beg = lbuf;
- end = lbuf + llen;
- c = lbuf + lpos;
- for ( ; n > 0 && c < end; --n) {
- c++;
- while (c < end && (*c & TAG_MASK) && ((*c & ~TAG_MASK) == 0))
- c++;
- }
- for ( ; n < 0 && c > beg; n++) {
- --c;
- while (c > beg && (*c & TAG_MASK) && ((*c & ~TAG_MASK) == 0))
- --c;
- }
- return c - lbuf;
-}
-
-static int insert_buf(byte *s, int n)
-{
- int pos = 0;
- int buffpos = lpos;
- int ch;
-
- while (pos < n) {
- if ((ch = pick_utf8(s,n,&pos)) < 0) {
- /* XXX temporary allow invalid chars */
- ch = (int) s[pos];
- DEBUGLOG(("insert_buf: Invalid UTF8:%d",ch));
- ++pos;
- }
- if ((utf8_mode && (ch >= 128 || isprint(ch))) || (ch <= 255 && isprint(ch))) {
- DEBUGLOG(("insert_buf: Printable(UTF-8):%d",ch));
- lbuf[lpos++] = (Uint32) ch;
- } else if (ch >= 128) { /* not utf8 mode */
- int nc = octal_or_hex_positions(ch);
- lbuf[lpos++] = ((Uint32) ch) | ESCAPED_TAG;
- while (nc--) {
- lbuf[lpos++] = ESCAPED_TAG;
- }
- } else if (ch == '\t') {
- do {
- lbuf[lpos++] = (CONTROL_TAG | ((Uint32) ch));
- ch = 0;
- } while (lpos % 8);
- } else if (ch == '\n' || ch == '\r') {
- write_buf(lbuf + buffpos, lpos - buffpos);
- ConPutChar('\r');
- if (ch == '\n')
- ConPutChar('\n');
- if (llen > lpos) {
- memcpy(lbuf, lbuf + lpos, llen - lpos);
- }
- llen -= lpos;
- lpos = buffpos = 0;
- } else {
- DEBUGLOG(("insert_buf: Magic(UTF-8):%d",ch));
- lbuf[lpos++] = ch | CONTROL_TAG;
- lbuf[lpos++] = CONTROL_TAG;
- }
- }
- return lpos - buffpos; /* characters "written" into
- current buffer (may be less due to newline) */
-}
-static int write_buf(Uint32 *s, int n)
-{
- int i;
-
- /*update_cols();*/
-
- while (n > 0) {
- if (!(*s & TAG_MASK) ) {
- ConPutChar(*s);
- --n;
- ++s;
- }
- else if (*s == (CONTROL_TAG | ((Uint32) '\t'))) {
- ConPutChar(' ');
- --n; s++;
- while (n > 0 && *s == CONTROL_TAG) {
- ConPutChar(' ');
- --n; s++;
- }
- } else if (*s & CONTROL_TAG) {
- ConPutChar('^');
- ConPutChar((*s == 0177) ? '?' : *s | 0x40);
- n -= 2;
- s += 2;
- } else if (*s & ESCAPED_TAG) {
- Uint32 ch = *s & ~(TAG_MASK);
- byte *octbuff;
- byte octtmp[256];
- int octbytes;
- DEBUGLOG(("Escaped: %d", ch));
- octbytes = octal_or_hex_positions(ch);
- if (octbytes > 256) {
- octbuff = driver_alloc(octbytes);
- } else {
- octbuff = octtmp;
- }
- octbytes = 0;
- octal_or_hex_format(ch, octbuff, &octbytes);
- DEBUGLOG(("octbytes: %d", octbytes));
- ConPutChar('\\');
- for (i = 0; i < octbytes; ++i) {
- ConPutChar(octbuff[i]);
- }
- n -= octbytes+1;
- s += octbytes+1;
- if (octbuff != octtmp) {
- driver_free(octbuff);
- }
- } else {
- DEBUGLOG(("Very unexpected character %d",(int) *s));
- ++n;
- --s;
- }
- }
- return TRUE;
-}
-
-
-static void
-move_cursor(int from, int to)
-{
- ConSetCursor(from,to);
-}
diff --git a/erts/emulator/drivers/win32/win_con.c b/erts/emulator/drivers/win32/win_con.c
deleted file mode 100644
index 2e3c12dc58d1..000000000000
--- a/erts/emulator/drivers/win32/win_con.c
+++ /dev/null
@@ -1,2355 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1997-2021. All Rights Reserved.
- *
- * Licensed 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.
- *
- * %CopyrightEnd%
- */
-
-#define UNICODE 1
-#define _UNICODE 1
-#include
-#include
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-#include "sys.h"
-#include
-#include "resource.h"
-#include "erl_version.h"
-#include
-#include
-#include "erl_driver.h"
-#include "win_con.h"
-
-#define ALLOC(X) malloc(X)
-#define REALLOC(X,Y) realloc(X,Y)
-#define FREE(X) free(X)
-
-#if SIZEOF_VOID_P == 8
-#define WIN64 1
-#ifndef GCL_HBRBACKGROUND
-#define GCL_HBRBACKGROUND GCLP_HBRBACKGROUND
-#endif
-#define DIALOG_PROC_RET INT_PTR
-#define CF_HOOK_RET INT_PTR
-#define CC_HOOK_RET INT_PTR
-#define OFN_HOOK_RET INT_PTR
-#else
-#define DIALOG_PROC_RET BOOL
-#define CF_HOOK_RET UINT
-#define CC_HOOK_RET UINT
-#define OFN_HOOK_RET UINT
-#endif
-
-
-#ifndef STATE_SYSTEM_INVISIBLE
-/* Mingw problem with oleacc.h and WIN32_LEAN_AND_MEAN */
-#define STATE_SYSTEM_INVISIBLE 0x00008000
-#endif
-
-#define WM_CONTEXT (0x0401)
-#define WM_CONBEEP (0x0402)
-#define WM_SAVE_PREFS (0x0403)
-
-#define USER_KEY TEXT("Software\\Ericsson\\Erlang\\") TEXT(ERLANG_VERSION)
-
-#define FRAME_HEIGHT ((2*GetSystemMetrics(SM_CYEDGE))+(2*GetSystemMetrics(SM_CYFRAME))+GetSystemMetrics(SM_CYCAPTION))
-#define FRAME_WIDTH (2*GetSystemMetrics(SM_CXFRAME)+(2*GetSystemMetrics(SM_CXFRAME))+GetSystemMetrics(SM_CXVSCROLL))
-
-#define LINE_LENGTH canvasColumns
-#define COL(_l) ((_l) % LINE_LENGTH)
-#define LINE(_l) ((_l) / LINE_LENGTH)
-
-#ifdef UNICODE
-/*
- * We use a character in the invalid unicode range
- */
-#define SET_CURSOR (0xD8FF)
-#else
-/*
- * XXX There is no escape to send a character 0x80. Fortunately,
- * the ttsl driver currently replaces 0x80 with an octal sequence.
- */
-#define SET_CURSOR (0x80)
-#endif
-
-#define SCAN_CODE_BREAK 0x46 /* scan code for Ctrl-Break */
-
-
-typedef struct ScreenLine_s {
- struct ScreenLine_s* next;
- struct ScreenLine_s* prev;
- int width;
-#ifdef HARDDEBUG
- int allocated;
-#endif
- int newline; /* Ends with hard newline: 1, wrapped at end: 0 */
- TCHAR *text;
-} ScreenLine_t;
-
-extern Uint32 *lbuf; /* The current line buffer */
-extern int llen; /* The current line length */
-extern int lpos;
-
-HANDLE console_input_event;
-HANDLE console_thread = NULL;
-
-#define DEF_CANVAS_COLUMNS 80
-#define DEF_CANVAS_ROWS 26
-
-#define BUFSIZE 4096
-#define MAXBUFSIZE 32768
-typedef struct {
- TCHAR *data;
- int size;
- int wrPos;
- int rdPos;
-} buffer_t;
-
-static buffer_t inbuf;
-static buffer_t outbuf;
-
-static CHOOSEFONT cf;
-
-static TCHAR szFrameClass[] = TEXT("FrameClass");
-static TCHAR szClientClass[] = TEXT("ClientClass");
-static HWND hFrameWnd;
-static HWND hClientWnd;
-static HWND hTBWnd;
-static HWND hComboWnd;
-static HANDLE console_input;
-static HANDLE console_output;
-static int cxChar,cyChar, cxCharMax;
-static int cxClient,cyClient;
-static int cyToolBar;
-static int iVscrollPos,iHscrollPos;
-static int iVscrollMax,iHscrollMax;
-static int nBufLines;
-static int cur_x;
-static int cur_y;
-static int canvasColumns = DEF_CANVAS_COLUMNS;
-static int canvasRows = DEF_CANVAS_ROWS;
-static ScreenLine_t *buffer_top,*buffer_bottom;
-static ScreenLine_t* cur_line;
-static POINT editBeg,editEnd;
-static BOOL fSelecting = FALSE;
-static BOOL fTextSelected = FALSE;
-static HKEY key;
-static BOOL has_key = FALSE;
-static LOGFONT logfont;
-static DWORD fgColor;
-static DWORD bkgColor;
-static FILE *logfile = NULL;
-static RECT winPos;
-static BOOL toolbarVisible;
-static BOOL destroyed = FALSE;
-
-static int lines_to_save = 10000; /* Maximum number of screen lines to save. */
-
-#define TITLE_BUF_SZ 256
-
-struct title_buf {
- TCHAR *name;
- TCHAR buf[TITLE_BUF_SZ];
-};
-
-static TCHAR *erlang_window_title = TEXT("Erlang");
-
-static unsigned __stdcall ConThreadInit(LPVOID param);
-static LRESULT CALLBACK ClientWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);
-static LRESULT CALLBACK FrameWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);
-static DIALOG_PROC_RET CALLBACK AboutDlgProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam);
-static ScreenLine_t *ConNewLine(void);
-static void DeleteTopLine(void);
-static void ensure_line_below(void);
-static ScreenLine_t *GetLineFromY(int y);
-static void LoadUserPreferences(void);
-static void SaveUserPreferences(void);
-static void set_scroll_info(HWND hwnd);
-static void ConCarriageFeed(int);
-static void ConScrollScreen(void);
-static BOOL ConChooseFont(HWND hwnd);
-static void ConFontInitialize(HWND hwnd);
-static void ConSetFont(HWND hwnd);
-static void ConChooseColor(HWND hwnd);
-static void DrawSelection(HWND hwnd, POINT pt1, POINT pt2);
-static void InvertSelectionArea(HWND hwnd);
-static void OnEditCopy(HWND hwnd);
-static void OnEditPaste(HWND hwnd);
-static void OnEditSelAll(HWND hwnd);
-static void GetFileName(HWND hwnd, TCHAR *pFile);
-static void OpenLogFile(HWND hwnd);
-static void CloseLogFile(HWND hwnd);
-static void LogFileWrite(TCHAR *buf, int n);
-static int write_inbuf(TCHAR *data, int n);
-static void init_buffers(void);
-static void AddToCmdHistory(void);
-static int write_outbuf(TCHAR *data, int num_chars);
-static void ConDrawText(HWND hwnd);
-static BOOL (WINAPI *ctrl_handler)(DWORD);
-static HWND InitToolBar(HWND hwndParent);
-static void window_title(struct title_buf *);
-static void free_window_title(struct title_buf *);
-static void Client_OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags);
-
-#ifdef HARDDEBUG
-/* For really hard GUI startup debugging, place DEBUGBOX() macros in code
- and get modal message boxes with the line number. */
-static void debug_box(int line) {
- TCHAR buff[1024];
- swprintf(buff,1024,TEXT("DBG:%d"),line);
- MessageBox(NULL,buff,TEXT("DBG"),MB_OK|MB_APPLMODAL);
-}
-
-#define DEBUGBOX() debug_box(__LINE__)
-#endif
-
-#define CON_VPRINTF_BUF_INC_SIZE 1024
-
-static erts_dsprintf_buf_t *
-grow_con_vprintf_buf(erts_dsprintf_buf_t *dsbufp, size_t need)
-{
- char *buf;
- size_t size;
-
- ASSERT(dsbufp);
-
- if (!dsbufp->str) {
- size = (((need + CON_VPRINTF_BUF_INC_SIZE - 1)
- / CON_VPRINTF_BUF_INC_SIZE)
- * CON_VPRINTF_BUF_INC_SIZE);
- buf = (char *) ALLOC(size * sizeof(char));
- }
- else {
- size_t free_size = dsbufp->size - dsbufp->str_len;
-
- if (need <= free_size)
- return dsbufp;
-
- size = need - free_size + CON_VPRINTF_BUF_INC_SIZE;
- size = (((size + CON_VPRINTF_BUF_INC_SIZE - 1)
- / CON_VPRINTF_BUF_INC_SIZE)
- * CON_VPRINTF_BUF_INC_SIZE);
- size += dsbufp->size;
- buf = (char *) REALLOC((void *) dsbufp->str,
- size * sizeof(char));
- }
- if (!buf)
- return NULL;
- if (buf != dsbufp->str)
- dsbufp->str = buf;
- dsbufp->size = size;
- return dsbufp;
-}
-
-static int con_vprintf(char *format, va_list arg_list)
-{
- int res,i;
- erts_dsprintf_buf_t dsbuf = ERTS_DSPRINTF_BUF_INITER(grow_con_vprintf_buf);
- res = erts_vdsprintf(&dsbuf, format, arg_list);
- if (res >= 0) {
- TCHAR *tmp = ALLOC(dsbuf.str_len*sizeof(TCHAR));
- for (i=0;iwidth < xpos) {
- return (canvasColumns-hscroll)*cxChar;
- }
- /* Not needed (?): SelectObject(hdc,CreateFontIndirect(&logfont)); */
- if (GetTextExtentPoint32(hdc,pLine->text,xpos,&size)) {
-#ifdef HARDDEBUG
- fprintf(stderr,"size.cx:%d\n",(int)size.cx);
- fflush(stderr);
-#endif
- if (hscrollPix >= size.cx) {
- return 0;
- }
- return ((int) size.cx) - hscrollPix;
- } else {
- return (xpos-hscroll)*cxChar;
- }
-}
-
-static int GetXFromCurrentY(HDC hdc, int hscroll, int xpos) {
- return GetXFromLine(hdc, hscroll, xpos, GetLineFromY(cur_y));
-}
-
-void ConSetCursor(int from, int to)
-{ TCHAR cmd[9];
- int *p;
- //DebugBreak();
- cmd[0] = SET_CURSOR;
- /*
- * XXX Expect trouble on CPUs which don't allow misaligned read and writes.
- */
- p = (int *)&cmd[1];
- *p++ = from;
- *p = to;
- write_outbuf(cmd, 1 + (2*sizeof(int)/sizeof(TCHAR)));
-}
-
-void ConPrintf(char *format, ...)
-{
- va_list va;
-
- va_start(va, format);
- (void) con_vprintf(format, va);
- va_end(va);
-}
-
-void ConBeep(void)
-{
- SendMessage(hClientWnd, WM_CONBEEP, 0L, 0L);
-}
-
-int ConReadInput(Uint32 *data, int num_chars)
-{
- TCHAR *buf;
- int nread;
- WaitForSingleObject(console_input,INFINITE);
- nread = num_chars = min(num_chars,inbuf.wrPos-inbuf.rdPos);
- buf = &inbuf.data[inbuf.rdPos];
- inbuf.rdPos += nread;
- while (nread--)
- *data++ = *buf++;
- if (inbuf.rdPos >= inbuf.wrPos) {
- inbuf.rdPos = 0;
- inbuf.wrPos = 0;
- ResetEvent(console_input_event);
- }
- ReleaseSemaphore(console_input,1,NULL);
- return num_chars;
-}
-
-int ConGetKey(void)
-{
- Uint32 c;
- WaitForSingleObject(console_input,INFINITE);
- ResetEvent(console_input_event);
- inbuf.rdPos = inbuf.wrPos = 0;
- ReleaseSemaphore(console_input,1,NULL);
- WaitForSingleObject(console_input_event,INFINITE);
- ConReadInput(&c, 1);
- return (int) c;
-}
-
-int ConGetColumns(void)
-{
- return (int) canvasColumns; /* 32bit atomic on windows */
-}
-
-int ConGetRows(void) {
- return (int) canvasRows;
-}
-
-
-static HINSTANCE hInstance;
-extern HMODULE beam_module;
-
-static unsigned __stdcall
-ConThreadInit(LPVOID param)
-{
- MSG msg;
- WNDCLASSEX wndclass;
- int iCmdShow;
- STARTUPINFO StartupInfo;
- HACCEL hAccel;
- int x, y, w, h;
- struct title_buf title;
-
- /*DebugBreak();*/
-#ifdef HARDDEBUG
- if(AttachConsole(ATTACH_PARENT_PROCESS) || AllocConsole()) {
- freopen("CONOUT$", "w", stdout);
- freopen("CONOUT$", "w", stderr);
- }
-#endif
-
- hInstance = GetModuleHandle(NULL);
- StartupInfo.dwFlags = 0;
- GetStartupInfo(&StartupInfo);
- iCmdShow = StartupInfo.dwFlags & STARTF_USESHOWWINDOW ?
- StartupInfo.wShowWindow : SW_SHOWDEFAULT;
-
- LoadUserPreferences();
-
- /* frame window class */
- wndclass.cbSize = sizeof (wndclass);
- wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_BYTEALIGNCLIENT;
- wndclass.lpfnWndProc = FrameWndProc;
- wndclass.cbClsExtra = 0;
- wndclass.cbWndExtra = 0;
- wndclass.hInstance = hInstance;
- wndclass.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(1));
- wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
- wndclass.hbrBackground = NULL;
- wndclass.lpszMenuName = NULL;
- wndclass.lpszClassName = szFrameClass;
- wndclass.hIconSm = LoadIcon (hInstance, MAKEINTRESOURCE(1));
- RegisterClassExW (&wndclass);
-
- /* client window class */
- wndclass.cbSize = sizeof (wndclass);
- wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
- wndclass.lpfnWndProc = ClientWndProc;
- wndclass.cbClsExtra = 0;
- wndclass.cbWndExtra = 0;
- wndclass.hInstance = hInstance;
- wndclass.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(1));
- wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
- wndclass.hbrBackground = CreateSolidBrush(bkgColor);
- wndclass.lpszMenuName = NULL;
- wndclass.lpszClassName = szClientClass;
- wndclass.hIconSm = LoadIcon (hInstance, MAKEINTRESOURCE(1));
- RegisterClassExW (&wndclass);
-
- InitCommonControls();
- init_buffers();
-
- nBufLines = 0;
- buffer_top = cur_line = ConNewLine();
- cur_line->next = buffer_bottom = ConNewLine();
- buffer_bottom->prev = cur_line;
-
- /* Create Frame Window */
- window_title(&title);
- hFrameWnd = CreateWindowEx(0, szFrameClass, title.name,
- WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,
- CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
- NULL,LoadMenu(beam_module,MAKEINTRESOURCE(1)),
- hInstance,NULL);
- free_window_title(&title);
-
- /* XXX OTP-5522:
- The window position is not saved correctly and if the window
- is closed when minimized, it's not possible to start werl again
- with the window open. Temporary fix so far is to ignore saved values
- and always start with initial settings. */
- /* Original: if (winPos.left == -1) { */
- /* Temporary: if (1) { */
- if (1) {
-
- /* initial window position */
- x = 0;
- y = 0;
- w = cxChar*LINE_LENGTH+FRAME_WIDTH+GetSystemMetrics(SM_CXVSCROLL);
- h = cyChar*30+FRAME_HEIGHT;
- } else {
- /* saved window position */
- x = winPos.left;
- y = winPos.top;
- w = winPos.right - x;
- h = winPos.bottom - y;
- }
- SetWindowPos(hFrameWnd, NULL, x, y, w, h, SWP_NOZORDER);
-
- ShowWindow(hFrameWnd, iCmdShow);
- UpdateWindow(hFrameWnd);
-
- hAccel = LoadAccelerators(beam_module,MAKEINTRESOURCE(1));
-
- ReleaseSemaphore(console_input, 1, NULL);
- ReleaseSemaphore(console_output, 1, NULL);
-
-
- /* Main message loop */
- while (GetMessage (&msg, NULL, 0, 0))
- {
- if (!TranslateAccelerator(hFrameWnd,hAccel,&msg))
- {
- TranslateMessage (&msg);
- DispatchMessage (&msg);
- }
- }
- /*
- PostQuitMessage() results in WM_QUIT which makes GetMessage()
- return 0 (which stops the main loop). Before we return from
- the console thread, the ctrl_handler is called to do erts_exit.
- */
- (*ctrl_handler)(CTRL_CLOSE_EVENT);
- return msg.wParam;
-}
-
-static LRESULT CALLBACK
-FrameWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
-{
- RECT r;
- int cy,i,bufsize;
- TCHAR c;
- unsigned long l;
- TCHAR buf[128];
- struct title_buf title;
-
- switch (iMsg) {
- case WM_CREATE:
- /* client window creation */
- window_title(&title);
- hClientWnd = CreateWindowEx(0, szClientClass, title.name,
- WS_CHILD|WS_VISIBLE|WS_VSCROLL|WS_HSCROLL,
- CW_USEDEFAULT, CW_USEDEFAULT,
- CW_USEDEFAULT, CW_USEDEFAULT,
- hwnd, (HMENU)0, hInstance, NULL);
- free_window_title(&title);
- hTBWnd = InitToolBar(hwnd);
- UpdateWindow (hClientWnd);
- return 0;
- case WM_SIZE :
- if (IsWindowVisible(hTBWnd)) {
- SendMessage(hTBWnd,TB_AUTOSIZE,0,0L);
- GetWindowRect(hTBWnd,&r);
- cy = r.bottom-r.top;
- } else cy = 0;
- MoveWindow(hClientWnd,0,cy,LOWORD(lParam),HIWORD(lParam)-cy,TRUE);
- return 0;
- case WM_ERASEBKGND:
- return 1;
- case WM_SETFOCUS :
- CreateCaret(hClientWnd, NULL, cxChar, cyChar);
- SetCaretPos(GetXFromCurrentY(GetDC(hClientWnd),iHscrollPos,cur_x), (cur_y-iVscrollPos)*cyChar);
- ShowCaret(hClientWnd);
- return 0;
- case WM_KILLFOCUS:
- HideCaret(hClientWnd);
- DestroyCaret();
- return 0;
- case WM_INITMENUPOPUP :
- if (lParam == 0) /* File popup menu */
- {
- EnableMenuItem((HMENU)wParam, IDMENU_STARTLOG,
- logfile ? MF_GRAYED : MF_ENABLED);
- EnableMenuItem((HMENU)wParam, IDMENU_STOPLOG,
- logfile ? MF_ENABLED : MF_GRAYED);
- return 0;
- }
- else if (lParam == 1) /* Edit popup menu */
- {
- EnableMenuItem((HMENU)wParam, IDMENU_COPY,
- fTextSelected ? MF_ENABLED : MF_GRAYED);
- EnableMenuItem((HMENU)wParam, IDMENU_PASTE,
- IsClipboardFormatAvailable(CF_TEXT) ? MF_ENABLED : MF_GRAYED);
- return 0;
- }
- else if (lParam == 3) /* View popup menu */
- {
- CheckMenuItem((HMENU)wParam,IDMENU_TOOLBAR,
- IsWindowVisible(hTBWnd) ? MF_CHECKED : MF_UNCHECKED);
- return 0;
- }
- break;
- case WM_NOTIFY:
- switch (((LPNMHDR) lParam)->code) {
- case TTN_NEEDTEXT:
- {
- LPTOOLTIPTEXT lpttt;
- lpttt = (LPTOOLTIPTEXT) lParam;
- lpttt->hinst = hInstance;
- /* check for combobox handle */
- if (lpttt->uFlags&TTF_IDISHWND) {
- if ((lpttt->hdr.idFrom == (UINT_PTR) hComboWnd)) {
- lstrcpy(lpttt->lpszText,TEXT("Command History"));
- break;
- }
- }
- /* check for toolbar buttons */
- switch (lpttt->hdr.idFrom) {
- case IDMENU_COPY:
- lstrcpy(lpttt->lpszText,TEXT("Copy (Ctrl+C)"));
- break;
- case IDMENU_PASTE:
- lstrcpy(lpttt->lpszText,TEXT("Paste (Ctrl+V)"));
- break;
- case IDMENU_FONT:
- lstrcpy(lpttt->lpszText,TEXT("Fonts"));
- break;
- case IDMENU_ABOUT:
- lstrcpy(lpttt->lpszText,TEXT("Help"));
- break;
- }
- }
- }
- break;
- case WM_COMMAND:
- switch(LOWORD(wParam))
- {
- case IDMENU_STARTLOG:
- OpenLogFile(hwnd);
- return 0;
- case IDMENU_STOPLOG:
- CloseLogFile(hwnd);
- return 0;
- case IDMENU_EXIT:
- SendMessage(hwnd, WM_CLOSE, 0, 0L);
- return 0;
- case IDMENU_COPY:
- if (fTextSelected)
- OnEditCopy(hClientWnd);
- return 0;
- case IDMENU_PASTE:
- OnEditPaste(hClientWnd);
- return 0;
- case IDMENU_SELALL:
- OnEditSelAll(hClientWnd);
- return 0;
- case IDMENU_FONT:
- if (ConChooseFont(hClientWnd)) {
- ConSetFont(hClientWnd);
- }
- SaveUserPreferences();
- return 0;
- case IDMENU_SELECTBKG:
- ConChooseColor(hClientWnd);
- SaveUserPreferences();
- return 0;
- case IDMENU_TOOLBAR:
- if (toolbarVisible) {
- ShowWindow(hTBWnd,SW_HIDE);
- toolbarVisible = FALSE;
- } else {
- ShowWindow(hTBWnd,SW_SHOW);
- toolbarVisible = TRUE;
- }
- GetClientRect(hwnd,&r);
- PostMessage(hwnd,WM_SIZE,0,MAKELPARAM(r.right,r.bottom));
- return 0;
- case IDMENU_ABOUT:
- DialogBox(beam_module,TEXT("AboutBox"),hwnd,AboutDlgProc);
- return 0;
- case ID_COMBOBOX:
- switch (HIWORD(wParam)) {
- case CBN_SELENDOK:
- i = SendMessage(hComboWnd,CB_GETCURSEL,0,0);
- if (i != CB_ERR) {
- buf[0] = 0x01; /* CTRL+A */
- buf[1] = 0x0B; /* CTRL+K */
- bufsize = SendMessage(hComboWnd,CB_GETLBTEXT,i,(LPARAM)&buf[2]);
- if (bufsize != CB_ERR)
- write_inbuf(buf,bufsize+2);
- SetFocus(hwnd);
- }
- break;
- case CBN_SELENDCANCEL:
- break;
- }
- break;
- case ID_BREAK: /* CTRL+BRK */
- /* pass on break char if the ctrl_handler is disabled */
- if ((*ctrl_handler)(CTRL_C_EVENT) == FALSE) {
- c = 0x03;
- write_inbuf(&c,1);
- }
- return 0;
- }
- break;
- case WM_KEYDOWN :
- switch (wParam) {
- case VK_UP: c = 'P'-'@'; break;
- case VK_DOWN : c = 'N'-'@'; break;
- case VK_RIGHT : c = 'F'-'@'; break;
- case VK_LEFT : c = 'B'-'@'; break;
- case VK_DELETE : c = 'D' -'@'; break;
- case VK_HOME : c = 'A'-'@'; break;
- case VK_END : c = 'E'-'@'; break;
- case VK_RETURN : AddToCmdHistory(); return 0;
- case VK_PRIOR : /* PageUp */
- PostMessage(hClientWnd, WM_VSCROLL, SB_PAGEUP, 0);
- return 0;
- case VK_NEXT : /* PageDown */
- PostMessage(hClientWnd, WM_VSCROLL, SB_PAGEDOWN, 0);
- return 0;
- default: return 0;
- }
- write_inbuf(&c, 1);
- return 0;
- case WM_MOUSEWHEEL:
- {
- int delta = GET_WHEEL_DELTA_WPARAM(wParam);
- if (delta < 0) {
- PostMessage(hClientWnd, WM_VSCROLL, MAKELONG(SB_THUMBTRACK,
- (iVscrollPos + 5)),0);
- } else {
- WORD pos = ((iVscrollPos - 5) < 0) ? 0 : (iVscrollPos - 5);
- PostMessage(hClientWnd, WM_VSCROLL, MAKELONG(SB_THUMBTRACK,pos),0);
- }
- return 0;
- }
- case WM_CHAR:
- c = (TCHAR)wParam;
- write_inbuf(&c,1);
- return 0;
- case WM_CLOSE :
- break;
- case WM_DESTROY :
- SaveUserPreferences();
- destroyed = TRUE;
- PostQuitMessage(0);
- return 0;
- case WM_SAVE_PREFS :
- SaveUserPreferences();
- return 0;
- }
- return DefWindowProc(hwnd, iMsg, wParam, lParam);
-}
-
-static BOOL
-Client_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
-{
- ConFontInitialize(hwnd);
- cur_x = cur_y = 0;
- iVscrollPos = 0;
- iHscrollPos = 0;
- return TRUE;
-}
-
-static void
-Client_OnPaint(HWND hwnd)
-{
- ScreenLine_t *pLine;
- int x,y,i,iTop,iBot;
- PAINTSTRUCT ps;
- RECT rcInvalid;
- HDC hdc;
-
- hdc = BeginPaint(hwnd, &ps);
- rcInvalid = ps.rcPaint;
- hdc = ps.hdc;
- iTop = max(0, iVscrollPos + rcInvalid.top/cyChar);
- iBot = min(nBufLines, iVscrollPos + rcInvalid.bottom/cyChar+1);
- pLine = GetLineFromY(iTop);
- for (i = iTop; i < iBot && pLine != NULL; i++) {
- y = cyChar*(i-iVscrollPos);
- x = -cxChar*iHscrollPos;
- TextOut(hdc, x, y, &pLine->text[0], pLine->width);
- pLine = pLine->next;
- }
- if (fTextSelected || fSelecting) {
- InvertSelectionArea(hwnd);
- }
- SetCaretPos(GetXFromCurrentY(hdc,iHscrollPos,cur_x), (cur_y-iVscrollPos)*cyChar);
- EndPaint(hwnd, &ps);
-}
-#ifdef HARDDEBUG
-static void dump_linebufs(void) {
- char *buff;
- ScreenLine_t *s = buffer_top;
- fprintf(stderr,"LinebufDump------------------------\n");
- while(s) {
- if (s == buffer_top) fprintf(stderr,"BT-> ");
- if (s == buffer_bottom) fprintf(stderr,"BB-> ");
- if (s == cur_line) fprintf(stderr,"CL-> ");
-
- buff = (char *) ALLOC(s->width+1);
- memcpy(buff,s->text,s->width);
- buff[s->width] = '\0';
- fprintf(stderr,"{\"%s\",%d,%d}\n",buff,s->newline,s->allocated);
- FREE(buff);
- s = s->next;
- }
- fprintf(stderr,"LinebufDumpEnd---------------------\n");
- fflush(stderr);
-}
-#endif
-
-static void reorganize_linebufs(HWND hwnd) {
- ScreenLine_t *otop = buffer_top;
- ScreenLine_t *obot = buffer_bottom;
- ScreenLine_t *next;
- int i,cpos;
-
- cpos = 0;
- i = nBufLines - cur_y;
- while (i > 1) {
- cpos += obot->width;
- obot = obot->prev;
- i--;
- }
- cpos += (obot->width - cur_x);
-#ifdef HARDDEBUG
- fprintf(stderr,"nBufLines = %d, cur_x = %d, cur_y = %d, cpos = %d\n",
- nBufLines,cur_x,cur_y,cpos);
- fflush(stderr);
-#endif
-
-
- nBufLines = 0;
- buffer_top = cur_line = ConNewLine();
- cur_line->next = buffer_bottom = ConNewLine();
- buffer_bottom->prev = cur_line;
-
- cur_x = cur_y = 0;
- iVscrollPos = 0;
- iHscrollPos = 0;
-
- while(otop) {
- for(i=0;iwidth;++i) {
- cur_line->text[cur_x] = otop->text[i];
- cur_x++;
- if (cur_x > cur_line->width)
- cur_line->width = cur_x;
- if (GetXFromCurrentY(GetDC(hwnd),0,cur_x) + cxChar >
- (LINE_LENGTH * cxChar)) {
- ConCarriageFeed(0);
- }
- }
- if (otop->newline) {
- ConCarriageFeed(1);
- /*ConScrollScreen();*/
- }
- next = otop->next;
- FREE(otop->text);
- FREE(otop);
- otop = next;
- }
- while (cpos) {
- cur_x--;
- if (cur_x < 0) {
- cur_y--;
- cur_line = cur_line->prev;
- cur_x = cur_line->width-1;
- }
- cpos--;
- }
- SetCaretPos(GetXFromCurrentY(GetDC(hwnd),iHscrollPos,cur_x), (cur_y-iVscrollPos)*cyChar);
-#ifdef HARDDEBUG
- fprintf(stderr,"canvasColumns = %d,nBufLines = %d, cur_x = %d, cur_y = %d\n",
- canvasColumns,nBufLines,cur_x,cur_y);
- fflush(stderr);
-#endif
-}
-
-
-static void
-Client_OnSize(HWND hwnd, UINT state, int cx, int cy)
-{
- RECT r;
- SCROLLBARINFO sbi;
- int w,h,columns;
- int scrollheight;
- cxClient = cx;
- cyClient = cy;
- set_scroll_info(hwnd);
- GetClientRect(hwnd,&r);
- w = r.right - r.left;
- h = r.bottom - r.top;
- sbi.cbSize = sizeof(SCROLLBARINFO);
- if (!GetScrollBarInfo(hwnd, OBJID_HSCROLL,&sbi) ||
- (sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE)) {
- scrollheight = 0;
- } else {
- scrollheight = sbi.rcScrollBar.bottom - sbi.rcScrollBar.top;
- }
- canvasRows = (h - scrollheight) / cyChar;
- if (canvasRows < DEF_CANVAS_ROWS) {
- canvasRows = DEF_CANVAS_ROWS;
- }
- columns = (w - GetSystemMetrics(SM_CXVSCROLL)) /cxChar;
- if (columns < DEF_CANVAS_COLUMNS)
- columns = DEF_CANVAS_COLUMNS;
- if (columns != canvasColumns) {
- canvasColumns = columns;
- /*dump_linebufs();*/
- reorganize_linebufs(hwnd);
- fSelecting = fTextSelected = FALSE;
- InvalidateRect(hwnd, NULL, TRUE);
-#ifdef HARDDEBUG
- fprintf(stderr,"Paint: cols = %d, rows = %d\n",canvasColumns,canvasRows);
- fflush(stderr);
-#endif
- }
-
- SetCaretPos(GetXFromCurrentY(GetDC(hwnd),iHscrollPos,cur_x), (cur_y-iVscrollPos)*cyChar);
-}
-
-static void calc_charpoint_from_point(HDC dc, int x, int y, int y_offset, POINT *pt)
-{
- int r;
- int hscrollPix = iHscrollPos * cxChar;
-
- pt->y = y/cyChar + iVscrollPos + y_offset;
-
- if (x > (LINE_LENGTH-iHscrollPos) * cxChar) {
- x = (LINE_LENGTH-iHscrollPos) * cxChar;
- }
- if (pt->y - y_offset > 0 && GetLineFromY(pt->y - y_offset) == NULL) {
- pt->y = nBufLines - 1 + y_offset;
- pt->x = GetLineFromY(pt->y - y_offset)->width;
- } else {
- for (pt->x = 1;
- (r = GetXFromLine(dc, 0, pt->x, GetLineFromY(pt->y - y_offset))) != 0 &&
- (r - hscrollPix) < x;
- ++(pt->x))
- ;
- if ((r - hscrollPix) > x)
- --(pt->x);
-#ifdef HARD_SEL_DEBUG
- fprintf(stderr,"pt->x = %d, iHscrollPos = %d\n",(int) pt->x, iHscrollPos);
- fflush(stderr);
-#endif
- if (pt->x <= 0) {
- pt->x = x/cxChar + iHscrollPos;
- }
- }
-}
-
-
-static void
-Client_OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags)
-{
- int r;
- SetFocus(GetParent(hwnd)); /* In case combobox steals the focus */
-#ifdef HARD_SEL_DEBUG
- fprintf(stderr,"OnLButtonDown fSelecting = %d, fTextSelected = %d:\n",
- fSelecting,fTextSelected);
- fflush(stderr);
-#endif
- if (fTextSelected) {
- InvertSelectionArea(hwnd);
- }
- fTextSelected = FALSE;
-
- calc_charpoint_from_point(GetDC(hwnd), x, y, 0, &editBeg);
-
- editEnd.x = editBeg.x;
- editEnd.y = editBeg.y + 1;
- fSelecting = TRUE;
- SetCapture(hwnd);
-}
-
-static void
-Client_OnRButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags)
-{
- if (fTextSelected) {
- fSelecting = TRUE;
- Client_OnMouseMove(hwnd,x,y,keyFlags);
- fSelecting = FALSE;
- }
-}
-
-static void
-Client_OnLButtonUp(HWND hwnd, int x, int y, UINT keyFlags)
-{
-#ifdef HARD_SEL_DEBUG
- fprintf(stderr,"OnLButtonUp fSelecting = %d, fTextSelected = %d:\n",
- fSelecting,fTextSelected);
- fprintf(stderr,"(Beg.x = %d, Beg.y = %d, "
- "End.x = %d, End.y = %d)\n",editBeg.x,editBeg.y,
- editEnd.x,editEnd.y);
-#endif
- if (fSelecting &&
- !(editBeg.x == editEnd.x && editBeg.y == (editEnd.y - 1))) {
- fTextSelected = TRUE;
- }
-#ifdef HARD_SEL_DEBUG
- fprintf(stderr,"OnLButtonUp fTextSelected = %d:\n",
- fTextSelected);
- fflush(stderr);
-#endif
- fSelecting = FALSE;
- ReleaseCapture();
-}
-
-#define EMPTY_RECT(R) \
-(((R).bottom - (R).top == 0) || ((R).right - (R).left == 0))
-#define ABS(X) (((X)< 0) ? -1 * (X) : X)
-#define DIFF(A,B) ABS(((int)(A)) - ((int)(B)))
-
-static int diff_sel_area(RECT old[3], RECT new[3], RECT result[6])
-{
- int absposold = old[0].left + old[0].top * canvasColumns;
- int absposnew = new[0].left + new[0].top * canvasColumns;
- int absendold = absposold, absendnew = absposnew;
- int i, x, ret = 0;
- int abspos[2],absend[2];
- for(i = 0; i < 3; ++i) {
- if (!EMPTY_RECT(old[i])) {
- absendold += (old[i].right - old[i].left) *
- (old[i].bottom - old[i].top);
- }
- if (!EMPTY_RECT(new[i])) {
- absendnew += (new[i].right - new[i].left) *
- (new[i].bottom - new[i].top);
- }
- }
- abspos[0] = min(absposold, absposnew);
- absend[0] = DIFF(absposold, absposnew) + abspos[0];
- abspos[1] = min(absendold, absendnew);
- absend[1] = DIFF(absendold, absendnew) + abspos[1];
-#ifdef HARD_SEL_DEBUG
- fprintf(stderr,"abspos[0] = %d, absend[0] = %d, abspos[1] = %d, absend[1] = %d\n",abspos[0],absend[0],abspos[1],absend[1]);
- fflush(stderr);
-#endif
- i = 0;
- for (x = 0; x < 2; ++x) {
- if (abspos[x] != absend[x]) {
- int consumed = 0;
- result[i].left = abspos[x] % canvasColumns;
- result[i].top = abspos[x] / canvasColumns;
- result[i].bottom = result[i].top + 1;
- if ((absend[x] - abspos[x]) + result[i].left < canvasColumns) {
-#ifdef HARD_SEL_DEBUG
- fprintf(stderr,"Nowrap, %d < canvasColumns\n",
- (absend[x] - abspos[x]) + result[i].left);
- fflush(stderr);
-#endif
- result[i].right = (absend[x] - abspos[x]) + result[i].left;
- consumed += result[i].right - result[i].left;
- } else {
-#ifdef HARD_SEL_DEBUG
- fprintf(stderr,"Wrap, %d >= canvasColumns\n",
- (absend[x] - abspos[x]) + result[i].left);
- fflush(stderr);
-#endif
- result[i].right = canvasColumns;
- consumed += result[i].right - result[i].left;
- if (absend[x] - abspos[x] - consumed >= canvasColumns) {
- ++i;
- result[i].top = result[i-1].bottom;
- result[i].left = 0;
- result[i].right = canvasColumns;
- result[i].bottom = (absend[x] - abspos[x] - consumed) / canvasColumns + result[i].top;
- consumed += (result[i].bottom - result[i].top) * canvasColumns;
- }
- if (absend[x] - abspos[x] - consumed > 0) {
- ++i;
- result[i].top = result[i-1].bottom;
- result[i].bottom = result[i].top + 1;
- result[i].left = 0;
- result[i].right = absend[x] - abspos[x] - consumed;
- }
- }
- ++i;
- }
- }
-#ifdef HARD_SEL_DEBUG
- if (i > 2) {
- int x;
- fprintf(stderr,"i = %d\n",i);
- fflush(stderr);
- for (x = 0; x < i; ++x) {
- fprintf(stderr, "result[%d]: top = %d, left = %d, "
- "bottom = %d. right = %d\n",
- x, result[x].top, result[x].left,
- result[x].bottom, result[x].right);
- }
- }
-#endif
- return i;
-}
-
-
-
-static void calc_sel_area(RECT rects[3], POINT beg, POINT end)
-{
- /* These are not really rects and points, these are character
- based positions, need to be multiplied by cxChar and cyChar to
- make up canvas coordinates */
- memset(rects,0,3*sizeof(RECT));
- rects[0].left = beg.x;
- rects[0].top = beg.y;
- rects[0].bottom = beg.y+1;
- if (end.y - beg.y == 1) { /* Only one row */
- rects[0].right = end.x;
- goto out;
- }
- rects[0].right = canvasColumns;
- if (end.y - beg.y > 2) {
- rects[1].left = 0;
- rects[1].top = rects[0].bottom;
- rects[1].right = canvasColumns;
- rects[1].bottom = end.y - 1;
- }
- rects[2].left = 0;
- rects[2].top = end.y - 1;
- rects[2].bottom = end.y;
- rects[2].right = end.x;
-
- out:
-#ifdef HARD_SEL_DEBUG
- {
- int i;
- fprintf(stderr,"beg.x = %d, beg.y = %d, end.x = %d, end.y = %d\n",
- beg.x,beg.y,end.x,end.y);
- for (i = 0; i < 3; ++i) {
- fprintf(stderr,"[%d] left = %d, top = %d, "
- "right = %d, bottom = %d\n",
- i, rects[i].left, rects[i].top,
- rects[i].right, rects[i].bottom);
- }
- fflush(stderr);
- }
-#endif
- return;
-}
-
-static void calc_sel_area_turned(RECT rects[3], POINT eBeg, POINT eEnd) {
- POINT from,to;
- if (eBeg.y >= eEnd.y ||
- (eBeg.y == eEnd.y - 1 && eBeg.x > eEnd.x)) {
-#ifdef HARD_SEL_DEBUG
- fprintf(stderr,"Reverting (Beg.x = %d, Beg.y = %d, "
- "End.x = %d, End.y = %d)\n",eBeg.x,eBeg.y,
- eEnd.x,eEnd.y);
- fflush(stderr);
-#endif
- from.x = eEnd.x;
- from.y = eEnd.y - 1;
- to.x = eBeg.x;
- to.y = eBeg.y + 1;
- calc_sel_area(rects,from,to);
- } else {
- calc_sel_area(rects,eBeg,eEnd);
- }
-}
-
-
-static void InvertSelectionArea(HWND hwnd)
-{
- RECT rects[3];
- POINT from,to;
- int i;
- calc_sel_area_turned(rects,editBeg,editEnd);
- for (i = 0; i < 3; ++i) {
- if (!EMPTY_RECT(rects[i])) {
- from.x = rects[i].left;
- to.x = rects[i].right;
- from.y = rects[i].top;
- to.y = rects[i].bottom;
- DrawSelection(hwnd,from,to);
- }
- }
-}
-
-static void
-Client_OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags)
-{
- if (fSelecting) {
- RECT rold[3], rnew[3], rupdate[6];
- int num_updates,i,r;
- POINT from,to;
- calc_sel_area_turned(rold,editBeg,editEnd);
-
- calc_charpoint_from_point(GetDC(hwnd), x, y, 1, &editEnd);
-
- calc_sel_area_turned(rnew,editBeg,editEnd);
- num_updates = diff_sel_area(rold,rnew,rupdate);
- for (i = 0; i < num_updates;++i) {
- from.x = rupdate[i].left;
- to.x = rupdate[i].right;
- from.y = rupdate[i].top;
- to.y = rupdate[i].bottom;
-#ifdef HARD_SEL_DEBUG
- fprintf(stderr,"from: x=%d,y=%d, to: x=%d, y=%d\n",
- from.x, from.y,to.x,to.y);
- fflush(stderr);
-#endif
- DrawSelection(hwnd,from,to);
- }
- }
-}
-
-static void
-Client_OnVScroll(HWND hwnd, HWND hwndCtl, UINT code, int pos)
-{
- int iVscroll;
-
- switch(code) {
- case SB_LINEDOWN:
- iVscroll = 1;
- break;
- case SB_LINEUP:
- iVscroll = -1;
- break;
- case SB_PAGEDOWN:
- iVscroll = max(1, cyClient/cyChar);
- break;
- case SB_PAGEUP:
- iVscroll = min(-1, -cyClient/cyChar);
- break;
- case SB_THUMBTRACK:
- iVscroll = pos - iVscrollPos;
- break;
- default:
- iVscroll = 0;
- }
- iVscroll = max(-iVscrollPos, min(iVscroll, iVscrollMax-iVscrollPos));
- if (iVscroll != 0) {
- iVscrollPos += iVscroll;
- ScrollWindowEx(hwnd, 0, -cyChar*iVscroll, NULL, NULL,
- NULL, NULL, SW_ERASE | SW_INVALIDATE);
- SetScrollPos(hwnd, SB_VERT, iVscrollPos, TRUE);
- iVscroll = GetScrollPos(hwnd, SB_VERT);
- UpdateWindow(hwnd);
- }
-}
-
-static void
-Client_OnHScroll(HWND hwnd, HWND hwndCtl, UINT code, int pos)
-{
- int iHscroll, curCharWidth = cxClient/cxChar;
-
- switch(code) {
- case SB_LINEDOWN:
- iHscroll = 1;
- break;
- case SB_LINEUP:
- iHscroll = -1;
- break;
- case SB_PAGEDOWN:
- iHscroll = max(1,curCharWidth-1);
- break;
- case SB_PAGEUP:
- iHscroll = min(-1,-(curCharWidth-1));
- break;
- case SB_THUMBTRACK:
- iHscroll = pos - iHscrollPos;
- break;
- default:
- iHscroll = 0;
- }
- iHscroll = max(-iHscrollPos, min(iHscroll, iHscrollMax-iHscrollPos-(curCharWidth-1)));
- if (iHscroll != 0) {
- iHscrollPos += iHscroll;
- ScrollWindow(hwnd, -cxChar*iHscroll, 0, NULL, NULL);
- SetScrollPos(hwnd, SB_HORZ, iHscrollPos, TRUE);
- UpdateWindow(hwnd);
- }
-}
-
-static LRESULT CALLBACK
-ClientWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
-{
- switch (iMsg) {
- HANDLE_MSG(hwnd, WM_CREATE, Client_OnCreate);
- HANDLE_MSG(hwnd, WM_SIZE, Client_OnSize);
- HANDLE_MSG(hwnd, WM_PAINT, Client_OnPaint);
- HANDLE_MSG(hwnd, WM_LBUTTONDOWN, Client_OnLButtonDown);
- HANDLE_MSG(hwnd, WM_RBUTTONDOWN, Client_OnRButtonDown);
- HANDLE_MSG(hwnd, WM_LBUTTONUP, Client_OnLButtonUp);
- HANDLE_MSG(hwnd, WM_MOUSEMOVE, Client_OnMouseMove);
- HANDLE_MSG(hwnd, WM_VSCROLL, Client_OnVScroll);
- HANDLE_MSG(hwnd, WM_HSCROLL, Client_OnHScroll);
- case WM_CONBEEP:
- if (0) Beep(440, 400);
- return 0;
- case WM_CONTEXT:
- ConDrawText(hwnd);
- return 0;
- case WM_CLOSE:
- break;
- case WM_DESTROY:
- PostQuitMessage(0);
- return 0;
- }
- return DefWindowProc (hwnd, iMsg, wParam, lParam);
-}
-
-static void
-LoadUserPreferences(void)
-{
- DWORD size;
- DWORD res;
- DWORD type;
- HFONT hfont;
- /* default prefs */
- hfont = CreateFont(0,0, 0,0, 0, FALSE,FALSE,FALSE,
- ANSI_CHARSET, OUT_TT_ONLY_PRECIS, CLIP_DEFAULT_PRECIS,
- CLEARTYPE_QUALITY, FIXED_PITCH, TEXT("Consolas"));
- if(hfont) {
- GetObject(hfont, sizeof(LOGFONT), (PSTR)&logfont);
- DeleteObject(hfont);
- } else {
- GetObject(GetStockObject(SYSTEM_FIXED_FONT),sizeof(LOGFONT),(PSTR)&logfont);
- }
- fgColor = GetSysColor(COLOR_WINDOWTEXT);
- bkgColor = GetSysColor(COLOR_WINDOW);
- winPos.left = -1;
- toolbarVisible = FALSE;
-
- if (RegCreateKeyEx(HKEY_CURRENT_USER, USER_KEY, 0, 0,
- REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
- &key, &res) != ERROR_SUCCESS)
- return;
- has_key = TRUE;
- if (res == REG_CREATED_NEW_KEY)
- return;
- size = sizeof(logfont);
- res = RegQueryValueEx(key,TEXT("Font"),NULL,&type,(LPBYTE)&logfont,&size);
- size = sizeof(fgColor);
- res = RegQueryValueEx(key,TEXT("FgColor"),NULL,&type,(LPBYTE)&fgColor,&size);
- size = sizeof(bkgColor);
- res = RegQueryValueEx(key,TEXT("BkColor"),NULL,&type,(LPBYTE)&bkgColor,&size);
- size = sizeof(winPos);
- res = RegQueryValueEx(key,TEXT("Pos"),NULL,&type,(LPBYTE)&winPos,&size);
- size = sizeof(toolbarVisible);
- res = RegQueryValueEx(key,TEXT("Toolbar"),NULL,&type,(LPBYTE)&toolbarVisible,&size);
-}
-
-static void
-SaveUserPreferences(void)
-{
- WINDOWPLACEMENT wndPlace;
-
- if (has_key == TRUE) {
- RegSetValueEx(key,TEXT("Font"),0,REG_BINARY,(CONST BYTE *)&logfont,sizeof(LOGFONT));
- RegSetValueEx(key,TEXT("FgColor"),0,REG_DWORD,(CONST BYTE *)&fgColor,sizeof(fgColor));
- RegSetValueEx(key,TEXT("BkColor"),0,REG_DWORD,(CONST BYTE *)&bkgColor,sizeof(bkgColor));
- RegSetValueEx(key,TEXT("Toolbar"),0,REG_DWORD,(CONST BYTE *)&toolbarVisible,sizeof(toolbarVisible));
-
- wndPlace.length = sizeof(WINDOWPLACEMENT);
- GetWindowPlacement(hFrameWnd,&wndPlace);
- /* If wndPlace.showCmd == SW_MINIMIZE, then the window is minimized.
- We don't care, wndPlace.rcNormalPosition always holds the last known position. */
- winPos = wndPlace.rcNormalPosition;
- RegSetValueEx(key,TEXT("Pos"),0,REG_BINARY,(CONST BYTE *)&winPos,sizeof(winPos));
- }
-}
-
-
-static void
-set_scroll_info(HWND hwnd)
-{
- SCROLLINFO info;
- int hScrollBy;
- /*
- * Set vertical scrolling range and scroll box position.
- */
-
- iVscrollMax = nBufLines-1;
- iVscrollPos = min(iVscrollPos, iVscrollMax);
- info.cbSize = sizeof(info);
- info.fMask = SIF_PAGE|SIF_RANGE|SIF_POS;
- info.nMin = 0;
- info.nPos = iVscrollPos;
- info.nPage = min(cyClient/cyChar, iVscrollMax);
- info.nMax = iVscrollMax;
- SetScrollInfo(hwnd, SB_VERT, &info, TRUE);
-
- /*
- * Set horizontal scrolling range and scroll box position.
- */
-
- iHscrollMax = LINE_LENGTH-1;
- hScrollBy = max(0, (iHscrollPos - (iHscrollMax-cxClient/cxChar))*cxChar);
- iHscrollPos = min(iHscrollPos, iHscrollMax);
- info.nPos = iHscrollPos;
- info.nPage = cxClient/cxChar;
- info.nMax = iHscrollMax;
- SetScrollInfo(hwnd, SB_HORZ, &info, TRUE);
- /*ScrollWindow(hwnd, hScrollBy, 0, NULL, NULL);*/
-}
-
-
-static void
-ensure_line_below(void)
-{
- if (cur_line->next == NULL) {
- if (nBufLines >= lines_to_save) {
- ScreenLine_t* pLine = buffer_top->next;
- FREE(buffer_top->text);
- FREE(buffer_top);
- buffer_top = pLine;
- buffer_top->prev = NULL;
- nBufLines--;
- }
- cur_line->next = ConNewLine();
- cur_line->next->prev = cur_line;
- buffer_bottom = cur_line->next;
- set_scroll_info(hClientWnd);
- }
-}
-
-static ScreenLine_t*
-ConNewLine(void)
-{
- ScreenLine_t *pLine;
-
- pLine = (ScreenLine_t *)ALLOC(sizeof(ScreenLine_t));
- if (!pLine)
- return NULL;
- pLine->text = (TCHAR *) ALLOC(canvasColumns * sizeof(TCHAR));
-#ifdef HARDDEBUG
- pLine->allocated = canvasColumns;
-#endif
- pLine->width = 0;
- pLine->prev = pLine->next = NULL;
- pLine->newline = 0;
- nBufLines++;
- return pLine;
-}
-
-static ScreenLine_t*
-GetLineFromY(int y)
-{
- ScreenLine_t *pLine = buffer_top;
- int i;
-
- for (i = 0; i < nBufLines && pLine != NULL; i++) {
- if (i == y)
- return pLine;
- pLine = pLine->next;
- }
- return NULL;
-}
-
-void ConCarriageFeed(int hard_newline)
-{
- cur_x = 0;
- ensure_line_below();
- cur_line->newline = hard_newline;
- cur_line = cur_line->next;
- if (cur_y < nBufLines-1) {
- cur_y++;
- } else if (iVscrollPos > 0) {
- iVscrollPos--;
- }
-}
-
-/*
- * Scroll screen if cursor is not visible.
- */
-static void
-ConScrollScreen(void)
-{
- if (cur_y >= iVscrollPos + cyClient/cyChar) {
- int iVscroll;
-
- iVscroll = cur_y - iVscrollPos - cyClient/cyChar + 1;
- iVscrollPos += iVscroll;
- ScrollWindowEx(hClientWnd, 0, -cyChar*iVscroll, NULL, NULL,
- NULL, NULL, SW_ERASE | SW_INVALIDATE);
- SetScrollPos(hClientWnd, SB_VERT, iVscrollPos, TRUE);
- UpdateWindow(hClientWnd);
- }
-}
-
-static void
-DrawSelection(HWND hwnd, POINT pt1, POINT pt2)
-{
- HDC hdc;
- int width,height;
-#ifdef HARD_SEL_DEBUG
- fprintf(stderr,"pt1.x = %d, pt1.y = %d, pt2.x = %d, pt2.y = %d\n",
- (int) pt1.x, (int) pt1.y, (int) pt2.x, (int) pt2.y);
-#endif
- pt1.x = GetXFromLine(GetDC(hwnd),iHscrollPos,pt1.x,GetLineFromY(pt1.y));
- pt2.x = GetXFromLine(GetDC(hwnd),iHscrollPos,pt2.x,GetLineFromY(pt2.y-1));
- pt1.y -= iVscrollPos;
- pt2.y -= iVscrollPos;
- pt1.y *= cyChar;
- pt2.y *= cyChar;
-#ifdef HARD_SEL_DEBUG
- fprintf(stderr,"pt1.x = %d, pt1.y = %d, pt2.x = %d, pt2.y = %d\n",
- (int) pt1.x, (int) pt1.y, (int) pt2.x, (int) pt2.y);
- fflush(stderr);
-#endif
- width = pt2.x-pt1.x;
- height = pt2.y - pt1.y;
- hdc = GetDC(hwnd);
- PatBlt(hdc,pt1.x,pt1.y,width,height,DSTINVERT);
- ReleaseDC(hwnd,hdc);
-}
-
-static void
-OnEditCopy(HWND hwnd)
-{
- HGLOBAL hMem;
- TCHAR *pMem;
- ScreenLine_t *pLine;
- RECT rects[3];
- POINT from,to;
- int i,j,sum,len;
- if (editBeg.y >= editEnd.y ||
- (editBeg.y == editEnd.y - 1 && editBeg.x > editEnd.x)) {
-#ifdef HARD_SEL_DEBUG
- fprintf(stderr,"CopyReverting (Beg.x = %d, Beg.y = %d, "
- "End.x = %d, End.y = %d)\n",editBeg.x,editBeg.y,
- editEnd.x,editEnd.y);
- fflush(stderr);
-#endif
- from.x = editEnd.x;
- from.y = editEnd.y - 1;
- to.x = editBeg.x;
- to.y = editBeg.y + 1;
- calc_sel_area(rects,from,to);
- } else {
- calc_sel_area(rects,editBeg,editEnd);
- }
- sum = 1;
- for (i = 0; i < 3; ++i) {
- if (!EMPTY_RECT(rects[i])) {
- pLine = GetLineFromY(rects[i].top);
- for (j = rects[i].top; j < rects[i].bottom ;++j) {
- if (pLine == NULL) {
- sum += 2;
- break;
- }
- if (pLine->width > rects[i].left) {
- sum += (pLine->width < rects[i].right) ?
- pLine->width - rects[i].left :
- rects[i].right - rects[i].left;
- }
- if(pLine->newline && rects[i].right >= pLine->width) {
- sum += 2;
- }
- pLine = pLine->next;
- }
- }
- }
-#ifdef HARD_SEL_DEBUG
- fprintf(stderr,"sum = %d\n",sum);
- fflush(stderr);
-#endif
- hMem = GlobalAlloc(GHND, sum * sizeof(TCHAR));
- pMem = GlobalLock(hMem);
- for (i = 0; i < 3; ++i) {
- if (!EMPTY_RECT(rects[i])) {
- pLine = GetLineFromY(rects[i].top);
- for (j = rects[i].top; j < rects[i].bottom; ++j) {
- if (pLine == NULL) {
- memcpy(pMem,TEXT("\r\n"),2 * sizeof(TCHAR));
- pMem += 2;
- break;
- }
- if (pLine->width > rects[i].left) {
- len = (pLine->width < rects[i].right) ?
- pLine->width - rects[i].left :
- rects[i].right - rects[i].left;
- memcpy(pMem,pLine->text + rects[i].left,len * sizeof(TCHAR));
- pMem +=len;
- }
- if(pLine->newline && rects[i].right >= pLine->width) {
- memcpy(pMem,TEXT("\r\n"),2 * sizeof(TCHAR));
- pMem += 2;
- }
- pLine = pLine->next;
- }
- }
- }
- *pMem = TEXT('\0');
- /* Flash de selection area to give user feedback about copying */
- InvertSelectionArea(hwnd);
- Sleep(100);
- InvertSelectionArea(hwnd);
-
- OpenClipboard(hwnd);
- EmptyClipboard();
- GlobalUnlock(hMem);
- SetClipboardData(CF_UNICODETEXT,hMem);
- CloseClipboard();
-}
-
-/* XXX:PaN Tchar or char? */
-static void
-OnEditPaste(HWND hwnd)
-{
- HANDLE hClipMem;
- TCHAR *pClipMem,*pMem,*pMem2;
- if (!OpenClipboard(hwnd))
- return;
- if ((hClipMem = GetClipboardData(CF_UNICODETEXT)) != NULL) {
- pClipMem = GlobalLock(hClipMem);
- pMem = (TCHAR *)ALLOC(GlobalSize(hClipMem) * sizeof(TCHAR));
- pMem2 = pMem;
- while ((*pMem2 = *pClipMem) != TEXT('\0')) {
- if (*pClipMem == TEXT('\r'))
- *pMem2 = TEXT('\n');
- ++pMem2;
- ++pClipMem;
- }
- GlobalUnlock(hClipMem);
- write_inbuf(pMem, _tcsclen(pMem));
- }
- CloseClipboard();
-}
-
-static void
-OnEditSelAll(HWND hwnd)
-{
- editBeg.x = 0;
- editBeg.y = 0;
- editEnd.x = LINE_LENGTH-1;
- editEnd.y = cur_y;
- fTextSelected = TRUE;
- InvalidateRect(hwnd, NULL, TRUE);
-}
-
-CF_HOOK_RET APIENTRY CFHookProc(HWND hDlg,UINT iMsg,WPARAM wParam,LPARAM lParam)
-{
- /* Hook procedure for font dialog box */
- HWND hOwner;
- RECT rc,rcOwner,rcDlg;
- switch (iMsg) {
- case WM_INITDIALOG:
- /* center dialogbox within its owner window */
- if ((hOwner = GetParent(hDlg)) == NULL)
- hOwner = GetDesktopWindow();
- GetWindowRect(hOwner, &rcOwner);
- GetWindowRect(hDlg, &rcDlg);
- CopyRect(&rc, &rcOwner);
- OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top);
- OffsetRect(&rc, -rc.left, -rc.top);
- OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom);
- SetWindowPos(hDlg,HWND_TOP,rcOwner.left + (rc.right / 2),
- rcOwner.top + (rc.bottom / 2),0,0,SWP_NOSIZE);
- return (CF_HOOK_RET) 1;
- default:
- break;
- }
- return (CF_HOOK_RET) 0; /* Let the default procedure process the message */
-}
-
-static BOOL
-ConChooseFont(HWND hwnd)
-{
- HDC hdc;
- hdc = GetDC(hwnd);
- cf.lStructSize = sizeof(CHOOSEFONT);
- cf.hwndOwner = hwnd;
- cf.hDC = NULL;
- cf.lpLogFont = &logfont;
- cf.iPointSize = 0;
- cf.Flags = CF_INITTOLOGFONTSTRUCT|CF_SCREENFONTS|CF_FIXEDPITCHONLY|CF_EFFECTS|CF_ENABLEHOOK;
- cf.rgbColors = GetTextColor(hdc);
- cf.lCustData = 0L;
- cf.lpfnHook = CFHookProc;
- cf.lpTemplateName = NULL;
- cf.hInstance = NULL;
- cf.lpszStyle = NULL;
- cf.nFontType = 0;
- cf.nSizeMin = 0;
- cf.nSizeMax = 0;
- ReleaseDC(hwnd,hdc);
- return ChooseFont(&cf);
-}
-
-static void
-ConFontInitialize(HWND hwnd)
-{
- HDC hdc;
- TEXTMETRIC tm;
- HFONT hFont;
-
- hFont = CreateFontIndirect(&logfont);
- hdc = GetDC(hwnd);
- SelectObject(hdc, hFont);
- SetTextColor(hdc,fgColor);
- SetBkColor(hdc,bkgColor);
- GetTextMetrics(hdc, &tm);
- cxChar = tm.tmAveCharWidth;
- cxCharMax = tm.tmMaxCharWidth;
- cyChar = tm.tmHeight + tm.tmExternalLeading;
- ReleaseDC(hwnd, hdc);
-}
-
-static void
-ConSetFont(HWND hwnd)
-{
- HDC hdc;
- TEXTMETRIC tm;
- HFONT hFontNew;
-
- hFontNew = CreateFontIndirect(&logfont);
- SendMessage(hComboWnd,WM_SETFONT,(WPARAM)hFontNew,
- MAKELPARAM(1,0));
- hdc = GetDC(hwnd);
- DeleteObject(SelectObject(hdc, hFontNew));
- GetTextMetrics(hdc, &tm);
- cxChar = tm.tmAveCharWidth;
- cxCharMax = tm.tmMaxCharWidth;
- cyChar = tm.tmHeight + tm.tmExternalLeading;
- fgColor = cf.rgbColors;
- SetTextColor(hdc,fgColor);
- ReleaseDC(hwnd, hdc);
- set_scroll_info(hwnd);
- HideCaret(hwnd);
- if (DestroyCaret()) {
- CreateCaret(hwnd, NULL, cxChar, cyChar);
- SetCaretPos(GetXFromCurrentY(hdc,iHscrollPos,cur_x), (cur_y-iVscrollPos)*cyChar);
- }
- ShowCaret(hwnd);
- InvalidateRect(hwnd, NULL, TRUE);
-}
-
-CC_HOOK_RET APIENTRY
-CCHookProc(HWND hDlg,UINT iMsg,WPARAM wParam,LPARAM lParam)
-{
- /* Hook procedure for choose color dialog box */
- HWND hOwner;
- RECT rc,rcOwner,rcDlg;
- switch (iMsg) {
- case WM_INITDIALOG:
- /* center dialogbox within its owner window */
- if ((hOwner = GetParent(hDlg)) == NULL)
- hOwner = GetDesktopWindow();
- GetWindowRect(hOwner, &rcOwner);
- GetWindowRect(hDlg, &rcDlg);
- CopyRect(&rc, &rcOwner);
- OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top);
- OffsetRect(&rc, -rc.left, -rc.top);
- OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom);
- SetWindowPos(hDlg,HWND_TOP,rcOwner.left + (rc.right / 2),
- rcOwner.top + (rc.bottom / 2),0,0,SWP_NOSIZE);
- return (CC_HOOK_RET) 1;
- default:
- break;
- }
- return (CC_HOOK_RET) 0; /* Let the default procedure process the message */
-}
-
-void ConChooseColor(HWND hwnd)
-{
- CHOOSECOLOR cc;
- static COLORREF acrCustClr[16];
- HBRUSH hbrush;
- HDC hdc;
-
- /* Initialize CHOOSECOLOR */
- ZeroMemory(&cc, sizeof(CHOOSECOLOR));
- cc.lStructSize = sizeof(CHOOSECOLOR);
- cc.hwndOwner = hwnd;
- cc.lpCustColors = (LPDWORD) acrCustClr;
- cc.rgbResult = bkgColor;
- cc.lpfnHook = CCHookProc;
- cc.Flags = CC_FULLOPEN|CC_RGBINIT|CC_SOLIDCOLOR|CC_ENABLEHOOK;
-
- if (ChooseColor(&cc)==TRUE) {
- bkgColor = cc.rgbResult;
- hdc = GetDC(hwnd);
- SetBkColor(hdc,bkgColor);
- ReleaseDC(hwnd,hdc);
- hbrush = CreateSolidBrush(bkgColor);
- DeleteObject((HBRUSH)SetClassLongPtr(hClientWnd,GCL_HBRBACKGROUND,(LONG_PTR)hbrush));
- InvalidateRect(hwnd,NULL,TRUE);
- }
-}
-
-OFN_HOOK_RET APIENTRY OFNHookProc(HWND hwndDlg,UINT iMsg,
- WPARAM wParam,LPARAM lParam)
-{
- /* Hook procedure for open file dialog box */
- HWND hOwner,hDlg;
- RECT rc,rcOwner,rcDlg;
- hDlg = GetParent(hwndDlg);
- switch (iMsg) {
- case WM_INITDIALOG:
- /* center dialogbox within its owner window */
- if ((hOwner = GetParent(hDlg)) == NULL)
- hOwner = GetDesktopWindow();
- GetWindowRect(hOwner, &rcOwner);
- GetWindowRect(hDlg, &rcDlg);
- CopyRect(&rc, &rcOwner);
- OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top);
- OffsetRect(&rc, -rc.left, -rc.top);
- OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom);
- SetWindowPos(hDlg,HWND_TOP,rcOwner.left + (rc.right / 2),
- rcOwner.top + (rc.bottom / 2),0,0,SWP_NOSIZE);
- return (OFN_HOOK_RET) 1;
- default:
- break;
- }
- return (OFN_HOOK_RET) 0; /* the let default procedure process the message */
-}
-
-static void
-GetFileName(HWND hwnd, TCHAR *pFile)
-{
- /* Open the File Open dialog box and */
- /* retrieve the file name */
- OPENFILENAME ofn;
- TCHAR szFilterSpec [128] = TEXT("logfiles (*.log)\0*.log\0All files (*.*)\0*.*\0\0");
- #define MAXFILENAME 256
- TCHAR szFileName[MAXFILENAME];
- TCHAR szFileTitle[MAXFILENAME];
-
- /* these need to be filled in */
- _tcscpy(szFileName, TEXT("erlshell.log"));
- _tcscpy(szFileTitle, TEXT("")); /* must be NULL */
-
- ofn.lStructSize = sizeof(OPENFILENAME);
- ofn.hwndOwner = NULL;
- ofn.lpstrFilter = szFilterSpec;
- ofn.lpstrCustomFilter = NULL;
- ofn.nMaxCustFilter = 0;
- ofn.nFilterIndex = 0;
- ofn.lpstrFile = szFileName;
- ofn.nMaxFile = MAXFILENAME;
- ofn.lpstrInitialDir = NULL;
- ofn.lpstrFileTitle = szFileTitle;
- ofn.nMaxFileTitle = MAXFILENAME;
- ofn.lpstrTitle = TEXT("Open logfile");
- ofn.lpstrDefExt = TEXT("log");
- ofn.Flags = OFN_CREATEPROMPT|OFN_HIDEREADONLY|OFN_EXPLORER|OFN_ENABLEHOOK|OFN_NOCHANGEDIR; /* OFN_NOCHANGEDIR only works in Vista :( */
- ofn.lpfnHook = OFNHookProc;
-
- if (!GetOpenFileName ((LPOPENFILENAME)&ofn)){
- *pFile = TEXT('\0');
- } else {
- _tcscpy(pFile, ofn.lpstrFile);
- }
-}
-
-void OpenLogFile(HWND hwnd)
-{
- /* open a file for logging */
- TCHAR filename[_MAX_PATH];
-
- GetFileName(hwnd, filename);
- if (filename[0] == '\0')
- return;
- if (NULL == (logfile = _tfopen(filename,TEXT("w,ccs=UNICODE"))))
- return;
-}
-
-void CloseLogFile(HWND hwnd)
-{
- /* close log file */
- fclose(logfile);
- logfile = NULL;
-}
-
-void LogFileWrite(TCHAR *buf, int num_chars)
-{
- /* write to logfile */
- int from,to;
- while (num_chars-- > 0) {
- switch (*buf) {
- case SET_CURSOR:
- buf++;
- from = *((int *)buf);
- buf += sizeof(int)/sizeof(TCHAR);
- to = *((int *)buf);
- buf += (sizeof(int)/sizeof(TCHAR))-1;
- num_chars -= 2 * (sizeof(int)/sizeof(TCHAR));
- // Won't seek in Unicode file, sorry...
- // fseek(logfile,to-from *sizeof(TCHAR),SEEK_CUR);
- break;
- default:
- _fputtc(*buf,logfile);
- break;
- }
- buf++;
- }
-}
-
-static void
-init_buffers(void)
-{
- inbuf.data = (TCHAR *) ALLOC(BUFSIZE * sizeof(TCHAR));
- outbuf.data = (TCHAR *) ALLOC(BUFSIZE * sizeof(TCHAR));
- inbuf.size = BUFSIZE;
- inbuf.rdPos = inbuf.wrPos = 0;
- outbuf.size = BUFSIZE;
- outbuf.rdPos = outbuf.wrPos = 0;
-}
-
-static int
-check_realloc(buffer_t *buf, int num_chars)
-{
- if (buf->wrPos + num_chars >= buf->size) {
- if (buf->size > MAXBUFSIZE)
- return 0;
- buf->size += num_chars + BUFSIZE;
- if (!(buf->data = (TCHAR *)REALLOC(buf->data, buf->size * sizeof(TCHAR)))) {
- buf->size = buf->rdPos = buf->wrPos = 0;
- return 0;
- }
- }
- return 1;
-}
-
-static int
-write_inbuf(TCHAR *data, int num_chars)
-{
- TCHAR *buf;
- int nwrite;
- WaitForSingleObject(console_input,INFINITE);
- if (!check_realloc(&inbuf,num_chars)) {
- ReleaseSemaphore(console_input,1,NULL);
- return -1;
- }
- buf = &inbuf.data[inbuf.wrPos];
- inbuf.wrPos += num_chars;
- nwrite = num_chars;
- while (nwrite--)
- *buf++ = *data++;
- SetEvent(console_input_event);
- ReleaseSemaphore(console_input,1,NULL);
- return num_chars;
-}
-
-static int
-write_outbuf(TCHAR *data, int num_chars)
-{
- TCHAR *buf;
- int nwrite;
-
- WaitForSingleObject(console_output,INFINITE);
- if (!check_realloc(&outbuf, num_chars)) {
- ReleaseSemaphore(console_output,1,NULL);
- return -1;
- }
- if (outbuf.rdPos == outbuf.wrPos)
- PostMessage(hClientWnd, WM_CONTEXT, 0L, 0L);
- buf = &outbuf.data[outbuf.wrPos];
- outbuf.wrPos += num_chars;
- nwrite = num_chars;
- while (nwrite--)
- *buf++ = *data++;
- ReleaseSemaphore(console_output,1,NULL);
- return num_chars;
-}
-
-DIALOG_PROC_RET CALLBACK AboutDlgProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
-{
- HWND hOwner;
- RECT rc,rcOwner,rcDlg;
-
- switch (iMsg) {
- case WM_INITDIALOG:
- /* center dialogbox within its owner window */
- if ((hOwner = GetParent(hDlg)) == NULL)
- hOwner = GetDesktopWindow();
- GetWindowRect(hOwner, &rcOwner);
- GetWindowRect(hDlg, &rcDlg);
- CopyRect(&rc, &rcOwner);
- OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top);
- OffsetRect(&rc, -rc.left, -rc.top);
- OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom);
- SetWindowPos(hDlg,HWND_TOP,rcOwner.left + (rc.right / 2),
- rcOwner.top + (rc.bottom / 2),0,0,SWP_NOSIZE);
- SetDlgItemText(hDlg, ID_OTP_VERSIONSTRING,
- TEXT("OTP version ") TEXT(ERLANG_OTP_VERSION));
- SetDlgItemText(hDlg, ID_ERTS_VERSIONSTRING,
- TEXT("Erlang emulator version ") TEXT(ERLANG_VERSION));
- return (DIALOG_PROC_RET) TRUE;
- case WM_COMMAND:
- switch (LOWORD(wParam)) {
- case IDOK:
- case IDCANCEL:
- EndDialog(hDlg,0);
- return (DIALOG_PROC_RET) TRUE;
- }
- break;
- }
- return (DIALOG_PROC_RET) FALSE;
-}
-
-static void
-ConDrawText(HWND hwnd)
-{
- int num_chars;
- int nchars;
- TCHAR *buf;
- int from, to;
- int dl;
- int dc;
- RECT rc;
-
- WaitForSingleObject(console_output, INFINITE);
- nchars = 0;
- num_chars = outbuf.wrPos - outbuf.rdPos;
- buf = &outbuf.data[outbuf.rdPos];
- if (logfile != NULL)
- LogFileWrite(buf, num_chars);
-
-
-#ifdef HARDDEBUG
- {
- TCHAR *bu = (TCHAR *) ALLOC((num_chars+1) * sizeof(TCHAR));
- memcpy(bu,buf,num_chars * sizeof(TCHAR));
- bu[num_chars]='\0';
- fprintf(stderr,"ConDrawText\"%S\"\n",bu);
- FREE(bu);
- fflush(stderr);
- }
-#endif
- /*
- * Don't draw any text in the window; just update the line buffers
- * and invalidate the appropriate part of the window. The window
- * will be updated on the next WM_PAINT message.
- */
-
- while (num_chars-- > 0) {
- switch (*buf) {
- case '\r':
- break;
- case '\n':
- if (nchars > 0) {
- rc.left = GetXFromCurrentY(GetDC(hwnd),iHscrollPos,cur_x - nchars);
- rc.right = rc.left + cxCharMax*nchars;
- rc.top = cyChar * (cur_y-iVscrollPos);
- rc.bottom = rc.top + cyChar;
- InvalidateRect(hwnd, &rc, TRUE);
- nchars = 0;
- }
- ConCarriageFeed(1);
- ConScrollScreen();
- break;
- case SET_CURSOR:
- if (nchars > 0) {
- rc.left = GetXFromCurrentY(GetDC(hwnd),iHscrollPos,cur_x - nchars);
- rc.right = rc.left + cxCharMax*nchars;
- rc.top = cyChar * (cur_y-iVscrollPos);
- rc.bottom = rc.top + cyChar;
- InvalidateRect(hwnd, &rc, TRUE);
- nchars = 0;
- }
- buf++;
- from = *((int *)buf);
- buf += sizeof(int)/sizeof(TCHAR);
- to = *((int *)buf);
- buf += (sizeof(int)/sizeof(TCHAR))-1;
- num_chars -= 2 * (sizeof(int)/sizeof(TCHAR));
- while (to > from) {
- cur_x++;
- if (GetXFromCurrentY(GetDC(hwnd),0,cur_x)+cxChar >
- (LINE_LENGTH * cxChar)) {
- cur_x = 0;
- cur_y++;
- ensure_line_below();
- cur_line = cur_line->next;
- }
- from++;
- }
- while (to < from) {
- cur_x--;
- if (cur_x < 0) {
- cur_y--;
- cur_line = cur_line->prev;
- cur_x = cur_line->width-1;
- }
- from--;
- }
-
- break;
- default:
- nchars++;
- cur_line->text[cur_x] = *buf;
- cur_x++;
- if (cur_x > cur_line->width)
- cur_line->width = cur_x;
- if (GetXFromCurrentY(GetDC(hwnd),0,cur_x)+cxChar >
- (LINE_LENGTH * cxChar)) {
- if (nchars > 0) {
- rc.left = GetXFromCurrentY(GetDC(hwnd),iHscrollPos,cur_x - nchars);
- rc.right = rc.left + cxCharMax*nchars;
- rc.top = cyChar * (cur_y-iVscrollPos);
- rc.bottom = rc.top + cyChar;
- InvalidateRect(hwnd, &rc, TRUE);
- }
- ConCarriageFeed(0);
- nchars = 0;
- }
- }
- buf++;
- }
- if (nchars > 0) {
- rc.left = GetXFromCurrentY(GetDC(hwnd),iHscrollPos,cur_x - nchars);
- rc.right = rc.left + cxCharMax*nchars;
- rc.top = cyChar * (cur_y-iVscrollPos);
- rc.bottom = rc.top + cyChar;
- InvalidateRect(hwnd, &rc, TRUE);
- }
- ConScrollScreen();
- SetCaretPos(GetXFromCurrentY(GetDC(hwnd),iHscrollPos,cur_x), (cur_y-iVscrollPos)*cyChar);
- outbuf.wrPos = outbuf.rdPos = 0;
- ReleaseSemaphore(console_output, 1, NULL);
-}
-
-static void
-AddToCmdHistory(void)
-{
- int i;
- int size;
- Uint32 *buf;
- wchar_t cmdBuf[128];
-
- if (llen != 0) {
- for (i = 0, size = 0; i < llen-1; i++) {
- /*
- * Find end of prompt.
- */
- if ((lbuf[i] == '>') && lbuf[i+1] == ' ') {
- buf = &lbuf[i+2];
- size = llen-i-2;
- break;
- }
- }
- if (size > 0 && size < 128) {
- for (i = 0;i < size; ++i) {
- cmdBuf[i] = (wchar_t) buf[i];
- }
- cmdBuf[size] = 0;
- SendMessage(hComboWnd,CB_INSERTSTRING,0,(LPARAM)cmdBuf);
- }
- }
-}
-
-/*static TBBUTTON tbb[] =
-{
- 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
- 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
- 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
- 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
- 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
- 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
- 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
- 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
- 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
- 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
- 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
- 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
- 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
- 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
- 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
- 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
- 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
- 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
- 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
- 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
- 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
- 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
- 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
- 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
- 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
- 0, IDMENU_COPY, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE, 0, 0, 0, 0,
- 1, IDMENU_PASTE, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE, 0, 0, 0, 0,
- 2, IDMENU_FONT, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE, 0, 0, 0, 0,
- 3, IDMENU_ABOUT, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE, 0, 0, 0, 0,
- 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
- };*/
-static TBBUTTON tbb[] =
-{
- {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP},
- {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP},
- {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP},
- {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP},
- {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP},
- {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP},
- {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP},
- {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP},
- {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP},
- {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP},
- {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP},
- {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP},
- {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP},
- {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP},
- {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP},
- {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP},
- {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP},
- {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP},
- {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP},
- {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP},
- {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP},
- {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP},
- {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP},
- {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP},
- {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP},
- {0, IDMENU_COPY, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE},
- {1, IDMENU_PASTE, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE},
- {2, IDMENU_FONT, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE},
- {3, IDMENU_ABOUT, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE},
- {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP}
-};
-
-static TBADDBITMAP tbbitmap =
-{
- HINST_COMMCTRL, IDB_STD_SMALL_COLOR,
-};
-
-
-static HWND
-InitToolBar(HWND hwndParent)
-{
- int x,y,cx;
- HWND hwndTB,hwndTT;
- RECT r;
- TOOLINFO ti;
- HFONT hFontNew;
- DWORD backgroundColor = GetSysColor(COLOR_BTNFACE);
- COLORMAP colorMap;
- colorMap.from = RGB(192, 192, 192);
- colorMap.to = backgroundColor;
- /* Create toolbar window with tooltips */
- hwndTB = CreateWindowEx(0,TOOLBARCLASSNAME,(TCHAR *)NULL,
- WS_CHILD|CCS_TOP|WS_CLIPSIBLINGS|TBSTYLE_TOOLTIPS,
- 0,0,0,0,hwndParent,
- (HMENU)2,hInstance,NULL);
- SendMessage(hwndTB,TB_BUTTONSTRUCTSIZE,
- (WPARAM) sizeof(TBBUTTON),0);
- tbbitmap.hInst = NULL;
- tbbitmap.nID = (UINT_PTR) CreateMappedBitmap(beam_module, 1,0, &colorMap, 1);
- SendMessage(hwndTB, TB_ADDBITMAP, (WPARAM) 4,
- (LPARAM) &tbbitmap);
-
- SendMessage(hwndTB,TB_ADDBUTTONS, (WPARAM) 30,
- (LPARAM) tbb);
- if (toolbarVisible)
- ShowWindow(hwndTB, SW_SHOW);
-
- /* Create combobox window */
- SendMessage(hwndTB,TB_GETITEMRECT,0,(LPARAM)&r);
- x = r.left; y = r.top;
- SendMessage(hwndTB,TB_GETITEMRECT,23,(LPARAM)&r);
- cx = r.right - x + 1;
- hComboWnd = CreateWindow(TEXT("combobox"),NULL,WS_VSCROLL|WS_CHILD|WS_VISIBLE|CBS_DROPDOWNLIST,
- x,y,cx,100,hwndParent,(HMENU)ID_COMBOBOX, hInstance,NULL);
- SetParent(hComboWnd,hwndTB);
- hFontNew = CreateFontIndirect(&logfont);
- SendMessage(hComboWnd,WM_SETFONT,(WPARAM)hFontNew,
- MAKELPARAM(1,0));
-
- /* Add tooltip for combo box */
- ZeroMemory(&ti,sizeof(TOOLINFO));
- ti.cbSize = sizeof(TOOLINFO);
- ti.uFlags = TTF_IDISHWND|TTF_CENTERTIP|TTF_SUBCLASS;
- ti.hwnd = hwndTB;;
- ti.uId = (UINT_PTR)hComboWnd;
- ti.lpszText = LPSTR_TEXTCALLBACK;
- hwndTT = (HWND)SendMessage(hwndTB,TB_GETTOOLTIPS,0,0);
- SendMessage(hwndTT,TTM_ADDTOOL,0,(LPARAM)&ti);
-
- return hwndTB;
-}
-
-static void
-window_title(struct title_buf *tbuf)
-{
- int res, i;
- size_t bufsz = TITLE_BUF_SZ;
- unsigned char charbuff[TITLE_BUF_SZ];
-
- res = erl_drv_getenv("ERL_WINDOW_TITLE", charbuff, &bufsz);
- if (res < 0)
- tbuf->name = erlang_window_title;
- else if (res == 0) {
- for (i = 0; i < bufsz; ++i) {
- tbuf->buf[i] = charbuff[i];
- }
- tbuf->buf[bufsz - 1] = 0;
- tbuf->name = &tbuf->buf[0];
- } else {
- char *buf = ALLOC(bufsz);
- if (!buf)
- tbuf->name = erlang_window_title;
- else {
- while (1) {
- char *newbuf;
- res = erl_drv_getenv("ERL_WINDOW_TITLE", buf, &bufsz);
- if (res <= 0) {
- if (res == 0) {
- TCHAR *wbuf = ALLOC(bufsz *sizeof(TCHAR));
- for (i = 0; i < bufsz ; ++i) {
- wbuf[i] = buf[i];
- }
- wbuf[bufsz - 1] = 0;
- FREE(buf);
- tbuf->name = wbuf;
- } else {
- tbuf->name = erlang_window_title;
- FREE(buf);
- }
- break;
- }
- newbuf = REALLOC(buf, bufsz);
- if (newbuf)
- buf = newbuf;
- else {
- tbuf->name = erlang_window_title;
- FREE(buf);
- break;
- }
- }
- }
- }
-}
-
-static void
-free_window_title(struct title_buf *tbuf)
-{
- if (tbuf->name != erlang_window_title && tbuf->name != &tbuf->buf[0])
- FREE(tbuf->name);
-}
diff --git a/erts/emulator/drivers/win32/win_con.h b/erts/emulator/drivers/win32/win_con.h
deleted file mode 100644
index 7a642cd7edb7..000000000000
--- a/erts/emulator/drivers/win32/win_con.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2007-2016. All Rights Reserved.
- *
- * Licensed 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.
- *
- * %CopyrightEnd%
- */
-
-/*
- * External API for the windows console (aka werl window)
- * used by ttsl_drv.c
- */
-#ifndef _WIN_CON_H_VISITED
-#define _WIN_CON_H_VISITED 1
-void ConNormalExit(void);
-void ConWaitForExit(void);
-void ConSetCtrlHandler(BOOL (WINAPI *handler)(DWORD));
-int ConPutChar(Uint32 c);
-void ConSetCursor(int from, int to);
-void ConPrintf(char *format, ...);
-void ConVprintf(char *format, va_list va);
-void ConBeep(void);
-int ConReadInput(Uint32 *data, int nbytes);
-int ConGetKey(void);
-int ConGetColumns(void);
-int ConGetRows(void);
-void ConInit(void);
-#endif /* _WIN_CON_H_VISITED */
diff --git a/erts/emulator/nifs/common/prim_tty_nif.c b/erts/emulator/nifs/common/prim_tty_nif.c
new file mode 100644
index 000000000000..309549bbd9cc
--- /dev/null
+++ b/erts/emulator/nifs/common/prim_tty_nif.c
@@ -0,0 +1,1012 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson 2015-2021. All Rights Reserved.
+ *
+ * Licensed 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.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Purpose: NIF library for interacting with the tty
+ *
+ */
+
+#define STATIC_ERLANG_NIF 1
+
+#ifndef WANT_NONBLOCKING
+#define WANT_NONBLOCKING
+#endif
+
+#include "config.h"
+#include "sys.h"
+#include "erl_nif.h"
+#include "erl_driver.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#ifdef HAVE_TERMCAP
+ #include
+ #include
+ #include
+#endif
+#ifndef __WIN32__
+ #include
+ #include
+#endif
+#ifdef HAVE_SYS_UIO_H
+ #include
+#endif
+
+#if defined IOV_MAX
+#define MAXIOV IOV_MAX
+#elif defined UIO_MAXIOV
+#define MAXIOV UIO_MAXIOV
+#else
+#define MAXIOV 16
+#endif
+
+#if !defined(HAVE_SETLOCALE) || !defined(HAVE_NL_LANGINFO) || !defined(HAVE_LANGINFO_H)
+#define PRIMITIVE_UTF8_CHECK 1
+#else
+#include
+#endif
+
+#ifdef VALGRIND
+# include
+#endif
+
+#define DEF_HEIGHT 24
+#define DEF_WIDTH 80
+
+typedef struct {
+#ifdef __WIN32__
+ HANDLE ofd;
+ HANDLE ifd;
+#else
+ int ofd; /* stdout */
+ int ifd; /* stdin */
+#endif
+ ErlNifPid self;
+ ErlNifPid reader;
+ int tty; /* if the tty is initialized */
+#ifdef THREADED_READER
+ ErlNifTid reader_tid;
+#endif
+#ifndef __WIN32__
+ int signal[2]; /* Pipe used for signal (winch + cont) notifications */
+#endif
+#ifdef HAVE_TERMCAP
+ struct termios tty_smode;
+ struct termios tty_rmode;
+#endif
+} TTYResource;
+
+static ErlNifResourceType *tty_rt;
+
+/* The NIFs: */
+static ERL_NIF_TERM isatty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM tty_create_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM tty_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM tty_set_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM setlocale_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM tty_select_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM tty_write_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM tty_read_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM isprint_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM wcwidth_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM wcswidth_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM sizeof_wchar_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM tty_window_size_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM tty_tgetent_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM tty_tgetnum_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM tty_tgetflag_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM tty_tgetstr_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM tty_tgoto_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM tty_read_signal_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+
+static ErlNifFunc nif_funcs[] = {
+ {"isatty", 1, isatty_nif},
+ {"tty_create", 0, tty_create_nif},
+ {"tty_init", 3, tty_init_nif},
+ {"tty_set", 1, tty_set_nif},
+ {"tty_read_signal", 2, tty_read_signal_nif},
+ {"setlocale", 0, setlocale_nif},
+ {"tty_select", 3, tty_select_nif},
+ {"tty_window_size", 1, tty_window_size_nif},
+ {"write_nif", 2, tty_write_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"read_nif", 2, tty_read_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"isprint", 1, isprint_nif},
+ {"wcwidth", 1, wcwidth_nif},
+ {"wcswidth", 1, wcswidth_nif},
+ {"sizeof_wchar", 0, sizeof_wchar_nif},
+ {"tgetent_nif", 1, tty_tgetent_nif},
+ {"tgetnum_nif", 1, tty_tgetnum_nif},
+ {"tgetflag_nif", 1, tty_tgetflag_nif},
+ {"tgetstr_nif", 1, tty_tgetstr_nif},
+ {"tgoto_nif", 2, tty_tgoto_nif},
+ {"tgoto_nif", 3, tty_tgoto_nif}
+};
+
+/* NIF interface declarations */
+static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info);
+static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info);
+static void unload(ErlNifEnv* env, void* priv_data);
+
+ERL_NIF_INIT(prim_tty, nif_funcs, load, NULL, upgrade, unload)
+
+#define ATOMS \
+ ATOM_DECL(canon); \
+ ATOM_DECL(echo); \
+ ATOM_DECL(ebadf); \
+ ATOM_DECL(undefined); \
+ ATOM_DECL(error); \
+ ATOM_DECL(true); \
+ ATOM_DECL(ok); \
+ ATOM_DECL(input); \
+ ATOM_DECL(false); \
+ ATOM_DECL(stdin); \
+ ATOM_DECL(stdout); \
+ ATOM_DECL(stderr); \
+ ATOM_DECL(sig);
+
+
+#define ATOM_DECL(A) static ERL_NIF_TERM atom_##A
+ATOMS
+#undef ATOM_DECL
+
+static ERL_NIF_TERM make_error(ErlNifEnv *env, ERL_NIF_TERM reason) {
+ return enif_make_tuple2(env, atom_error, reason);
+}
+
+static ERL_NIF_TERM make_enotsup(ErlNifEnv *env) {
+ return make_error(env, enif_make_atom(env, "enotsup"));
+}
+
+static ERL_NIF_TERM make_errno_error(ErlNifEnv *env, const char *function) {
+ ERL_NIF_TERM errorInfo;
+#ifdef __WIN32__
+ errorInfo = enif_make_atom(env, last_error());
+#else
+ errorInfo = enif_make_atom(env, erl_errno_id(errno));
+#endif
+ return make_error(
+ env, enif_make_tuple2(
+ env, enif_make_atom(env, function), errorInfo));
+}
+
+static int tty_get_fd(ErlNifEnv *env, ERL_NIF_TERM atom, int *fd) {
+ if (enif_is_identical(atom, atom_stdout)) {
+ *fd = fileno(stdout);
+ } else if (enif_is_identical(atom, atom_stdin)) {
+ *fd = fileno(stdin);
+ } else if (enif_is_identical(atom, atom_stderr)) {
+ *fd = fileno(stderr);
+ } else {
+ return 0;
+ }
+ return 1;
+}
+
+static ERL_NIF_TERM isatty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
+ int fd;
+ if (tty_get_fd(env, argv[0], &fd)) {
+ if (isatty(fd)) {
+ return atom_true;
+ } else if (errno == EINVAL || errno == ENOTTY) {
+ return atom_false;
+ } else {
+ return atom_ebadf;
+ }
+ }
+ return enif_make_badarg(env);
+}
+
+static ERL_NIF_TERM isprint_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
+ int i;
+ if (enif_get_int(env, argv[0], &i)) {
+ ASSERT(i > 0 && i < 256);
+ return isprint((char)i) ? atom_true : atom_false;
+ }
+ return enif_make_badarg(env);
+}
+
+static ERL_NIF_TERM wcwidth_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
+ int i;
+ if (enif_get_int(env, argv[0], &i)) {
+#ifndef __WIN32__
+ int width;
+ ASSERT(i > 0 && i < (1l << 21));
+ width = wcwidth((wchar_t)i);
+ if (width == -1) {
+ return make_error(env, enif_make_atom(env, "not_printable"));
+ }
+ return enif_make_int(env, width);
+#else
+ return make_enotsup(env);
+#endif
+ }
+ return enif_make_badarg(env);
+}
+
+static ERL_NIF_TERM wcswidth_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
+ ErlNifBinary bin;
+ if (enif_inspect_iolist_as_binary(env, argv[0], &bin)) {
+ wchar_t *chars = (wchar_t*)bin.data;
+ int width;
+#ifdef DEBUG
+ for (int i = 0; i < bin.size / sizeof(wchar_t); i++) {
+ ASSERT(chars[i] >= 0 && chars[i] < (1l << 21));
+ }
+#endif
+#ifndef __WIN32__
+ width = wcswidth(chars, bin.size / sizeof(wchar_t));
+#else
+ width = bin.size / sizeof(wchar_t);
+#endif
+ if (width == -1) {
+ return make_error(env, enif_make_atom(env, "not_printable"));
+ }
+ return enif_make_tuple2(env, atom_ok, enif_make_int(env, width));
+ }
+ return enif_make_badarg(env);
+}
+
+static ERL_NIF_TERM sizeof_wchar_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
+ return enif_make_int(env, sizeof(wchar_t));
+}
+
+static ERL_NIF_TERM tty_write_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
+ ERL_NIF_TERM head = argv[1], tail;
+ ErlNifIOQueue *q = NULL;
+ ErlNifIOVec vec, *iovec = &vec;
+ SysIOVec *iov;
+ int iovcnt;
+ TTYResource *tty;
+ ssize_t res = 0;
+ size_t size;
+ if (!enif_get_resource(env, argv[0], tty_rt, (void **)&tty))
+ return enif_make_badarg(env);
+
+ while (!enif_is_identical(head, enif_make_list(env, 0))) {
+ if (!enif_inspect_iovec(env, MAXIOV, head, &tail, &iovec))
+ return enif_make_badarg(env);
+
+ head = tail;
+
+ iov = iovec->iov;
+ size = iovec->size;
+ iovcnt = iovec->iovcnt;
+
+ do {
+#ifndef __WIN32__
+ do {
+ res = writev(tty->ofd, iov, iovcnt);
+ } while(res < 0 && (errno == EINTR || errno == EAGAIN));
+#else
+ for (int i = 0; i < iovec->iovcnt; i++) {
+ ssize_t written;
+ BOOL r = WriteFile(tty->ofd, iovec->iov[i].iov_base, iovec->iov[i].iov_len, &written, NULL);
+ if (!r) {
+ res = -1;
+ break;
+ }
+ res += written;
+ }
+#endif
+ if (res < 0) {
+ if (q) enif_ioq_destroy(q);
+ return make_errno_error(env, "writev");
+ }
+ if (res != size) {
+ if (!q) {
+ q = enif_ioq_create(ERL_NIF_IOQ_NORMAL);
+ enif_ioq_enqv(q, iovec, 0);
+ }
+ }
+
+ if (q) {
+ enif_ioq_deq(q, res, &size);
+ if (size == 0) {
+ enif_ioq_destroy(q);
+ q = NULL;
+ } else {
+ iov = enif_ioq_peek(q, &iovcnt);
+ }
+ }
+ } while(q);
+
+ };
+ return atom_ok;
+}
+
+static ERL_NIF_TERM tty_read_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
+ TTYResource *tty;
+ ErlNifBinary bin;
+ ERL_NIF_TERM res_term;
+ ssize_t res = 0;
+ if (!enif_get_resource(env, argv[0], tty_rt, (void **)&tty))
+ return enif_make_badarg(env);
+#ifdef __WIN32__
+ {
+ ssize_t inputs_read, num_characters = 0;
+ wchar_t *characters = NULL;
+ INPUT_RECORD inputs[128];
+ if (!ReadConsoleInputW(tty->ifd, inputs, sizeof(inputs)/sizeof(*inputs),
+ &inputs_read)) {
+ return make_errno_error(env, "ReadConsoleInput");
+ }
+ for (int i = 0; i < inputs_read; i++) {
+ if (inputs[i].EventType == KEY_EVENT) {
+ if (inputs[i].Event.KeyEvent.bKeyDown &&
+ inputs[i].Event.KeyEvent.uChar.UnicodeChar < 256 &&
+ inputs[i].Event.KeyEvent.uChar.UnicodeChar != 0) {
+ num_characters++;
+ }
+ if (!inputs[i].Event.KeyEvent.bKeyDown &&
+ inputs[i].Event.KeyEvent.uChar.UnicodeChar > 255 &&
+ inputs[i].Event.KeyEvent.uChar.UnicodeChar != 0) {
+ num_characters++;
+ }
+ }
+ }
+ enif_alloc_binary(num_characters * sizeof(wchar_t), &bin);
+ characters = (wchar_t*)bin.data;
+ for (int i = 0; i < inputs_read; i++) {
+ switch (inputs[i].EventType)
+ {
+ case KEY_EVENT:
+ if (inputs[i].Event.KeyEvent.bKeyDown &&
+ inputs[i].Event.KeyEvent.uChar.UnicodeChar < 256 &&
+ inputs[i].Event.KeyEvent.uChar.UnicodeChar != 0) {
+ characters[res++] = inputs[i].Event.KeyEvent.uChar.UnicodeChar;
+ }
+ if (!inputs[i].Event.KeyEvent.bKeyDown &&
+ inputs[i].Event.KeyEvent.uChar.UnicodeChar > 255 &&
+ inputs[i].Event.KeyEvent.uChar.UnicodeChar != 0) {
+ characters[res++] = inputs[i].Event.KeyEvent.uChar.UnicodeChar;
+ }
+ break;
+ case WINDOW_BUFFER_SIZE_EVENT:
+ enif_send(env, &tty->self, NULL,
+ enif_make_tuple2(env, enif_make_atom(env, "resize"),
+ enif_make_tuple2(env,
+ enif_make_int(env, inputs[i].Event.WindowBufferSizeEvent.dwSize.Y),
+ enif_make_int(env, inputs[i].Event.WindowBufferSizeEvent.dwSize.X))));
+ break;
+ case MENU_EVENT:
+ case FOCUS_EVENT:
+ /* Should be ignored according to
+ https://docs.microsoft.com/en-us/windows/console/input-record-str */
+ break;
+ default:
+ fprintf(stderr,"Unknown event: %d\r\n", inputs[i].EventType);
+ break;
+ }
+ }
+ res *= sizeof(wchar_t);
+ }
+#else
+ enif_alloc_binary(1024, &bin);
+ res = read(tty->ifd, bin.data, bin.size);
+ if (res < 0) {
+ if (errno != EAGAIN && errno != EINTR) {
+ enif_release_binary(&bin);
+ return make_errno_error(env, "read");
+ }
+ res = 0;
+ } else if (res == 0) {
+ enif_release_binary(&bin);
+ return make_error(env, enif_make_atom(env, "closed"));
+ }
+#endif
+ enif_select(env, tty->ifd, ERL_NIF_SELECT_READ, tty, NULL, argv[1]);
+ if (res == bin.size) {
+ res_term = enif_make_binary(env, &bin);
+ } else if (res < bin.size / 2) {
+ unsigned char *buff = enif_make_new_binary(env, res, &res_term);
+ if (res > 0) {
+ memcpy(buff, bin.data, res);
+ }
+ enif_release_binary(&bin);
+ } else {
+ enif_realloc_binary(&bin, res);
+ res_term = enif_make_binary(env, &bin);
+ }
+
+ return enif_make_tuple2(env, atom_ok, res_term);
+}
+
+static ERL_NIF_TERM setlocale_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
+#ifdef __WIN32__
+ if (!SetConsoleOutputCP(CP_UTF8)) {
+ return make_errno_error(env, "SetConsoleOutputCP");
+ }
+ return atom_true;
+#elif defined(PRIMITIVE_UTF8_CHECK)
+ setlocale(LC_CTYPE, ""); /* Set international environment,
+ ignore result */
+ return enif_make_atom(env, "primitive");
+#else
+ char *l = setlocale(LC_CTYPE, ""); /* Set international environment */
+ if (l != NULL) {
+ if (strcmp(nl_langinfo(CODESET), "UTF-8") == 0)
+ return atom_true;
+ }
+ return atom_false;
+#endif
+}
+
+static ERL_NIF_TERM tty_tgetent_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
+#ifdef HAVE_TERMCAP
+ ErlNifBinary TERM;
+ if (!enif_inspect_iolist_as_binary(env, argv[0], &TERM))
+ return enif_make_badarg(env);
+ if (tgetent((char *)NULL /* ignored */, (char *)TERM.data) <= 0) {
+ return make_errno_error(env, "tgetent");
+ }
+ return atom_ok;
+#else
+ return make_enotsup(env);
+#endif
+}
+
+static ERL_NIF_TERM tty_tgetnum_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
+#ifdef HAVE_TERMCAP
+ ErlNifBinary TERM;
+ if (!enif_inspect_iolist_as_binary(env, argv[0], &TERM))
+ return enif_make_badarg(env);
+ return enif_make_int(env, tgetnum((char*)TERM.data));
+#else
+ return make_enotsup(env);
+#endif
+}
+
+static ERL_NIF_TERM tty_tgetflag_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
+#ifdef HAVE_TERMCAP
+ ErlNifBinary TERM;
+ if (!enif_inspect_iolist_as_binary(env, argv[0], &TERM))
+ return enif_make_badarg(env);
+ if (tgetflag((char*)TERM.data))
+ return atom_true;
+ return atom_false;
+#else
+ return make_enotsup(env);
+#endif
+}
+
+static ERL_NIF_TERM tty_tgetstr_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
+#ifdef HAVE_TERMCAP
+ ErlNifBinary TERM, ret;
+ /* tgetstr seems to use a lot of stack buffer space,
+ so buff needs to be relatively "small" */
+ char *str = NULL;
+ char buff[BUFSIZ] = {0};
+
+ if (!enif_inspect_iolist_as_binary(env, argv[0], &TERM))
+ return enif_make_badarg(env);
+ str = tgetstr((char*)TERM.data, (char**)&buff);
+ if (!str) return atom_false;
+ enif_alloc_binary(strlen(str), &ret);
+ memcpy(ret.data, str, strlen(str));
+ return enif_make_tuple2(
+ env, atom_ok, enif_make_binary(env, &ret));
+#else
+ return make_enotsup(env);
+#endif
+}
+
+#ifdef HAVE_TERMCAP
+static int tputs_buffer_index;
+static unsigned char tputs_buffer[1024];
+
+#if defined(__sun) && defined(__SVR4) /* Solaris */
+static int tty_puts_putc(char c) {
+#else
+static int tty_puts_putc(int c) {
+#endif
+ tputs_buffer[tputs_buffer_index++] = (unsigned char)c;
+ return 0;
+}
+#endif
+
+static ERL_NIF_TERM tty_tgoto_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
+#ifdef HAVE_TERMCAP
+ ErlNifBinary TERM;
+ char *ent;
+ int value1, value2 = 0;
+ if (!enif_inspect_iolist_as_binary(env, argv[0], &TERM) ||
+ !enif_get_int(env, argv[1], &value1))
+ return enif_make_badarg(env);
+ if (argc == 2) {
+ ent = tgoto((char*)TERM.data, 0, value1);
+ } else {
+ ASSERT(argc == 3);
+ ent = tgoto((char*)TERM.data, value1, value2);
+ }
+ if (!ent) return make_errno_error(env, "tgoto");
+
+ tputs_buffer_index = 0;
+ if (tputs(ent, 1, tty_puts_putc)) {
+ return make_errno_error(env, "tputs");
+ } else {
+ ERL_NIF_TERM ret;
+ unsigned char *buff = enif_make_new_binary(env, tputs_buffer_index, &ret);
+ memcpy(buff, tputs_buffer, tputs_buffer_index);
+ return enif_make_tuple2(env, atom_ok, ret);
+ }
+#else
+ return make_enotsup(env);
+#endif
+}
+
+static ERL_NIF_TERM tty_create_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
+
+ TTYResource *tty = enif_alloc_resource(tty_rt, sizeof(TTYResource));
+ ERL_NIF_TERM tty_term;
+ memset(tty, 0, sizeof(*tty));
+#ifndef __WIN32__
+ tty->ifd = 0;
+ tty->ofd = 1;
+#else
+ tty->ifd = GetStdHandle(STD_INPUT_HANDLE);
+ tty->ofd = GetStdHandle(STD_OUTPUT_HANDLE);
+#endif
+
+ tty_term = enif_make_resource(env, tty);
+ enif_release_resource(tty);
+
+ enif_set_pid_undefined(&tty->self);
+ enif_set_pid_undefined(&tty->reader);
+
+ return enif_make_tuple2(env, atom_ok, tty_term);
+}
+
+static ERL_NIF_TERM tty_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
+
+#if defined(HAVE_TERMCAP) || defined(__WIN32__)
+ ERL_NIF_TERM canon, echo, sig;
+ TTYResource *tty;
+ int fd;
+
+ if (argc != 3 ||
+ !tty_get_fd(env, argv[1], &fd) ||
+ !enif_is_map(env, argv[2])) {
+ return enif_make_badarg(env);
+ }
+
+ if (!enif_get_resource(env, argv[0], tty_rt, (void **)&tty))
+ return enif_make_badarg(env);
+
+ if (!enif_get_map_value(env, argv[2], enif_make_atom(env,"canon"), &canon))
+ canon = enif_make_atom(env, "undefined");
+ if (!enif_get_map_value(env, argv[2], enif_make_atom(env,"echo"), &echo))
+ echo = enif_make_atom(env, "undefined");
+ if (!enif_get_map_value(env, argv[2], enif_make_atom(env,"sig"), &sig))
+ sig = enif_make_atom(env, "undefined");
+
+#ifndef __WIN32__
+ if (tcgetattr(fd, &tty->tty_rmode) < 0) {
+ return make_errno_error(env, "tcgetattr");
+ }
+
+ tty->tty_smode = tty->tty_rmode;
+
+ /* Default characteristics for all usage including termcap output. */
+ tty->tty_smode.c_iflag &= ~ISTRIP;
+
+ /* erts_fprintf(stderr,"canon %T\r\n", canon); */
+ /* Turn canonical (line mode) on off. */
+ if (enif_is_identical(canon, atom_true)) {
+ tty->tty_smode.c_iflag |= ICRNL;
+ tty->tty_smode.c_lflag |= ICANON;
+ tty->tty_smode.c_oflag |= OPOST;
+ tty->tty_smode.c_cc[VEOF] = tty->tty_rmode.c_cc[VEOF];
+#ifdef VDSUSP
+ tty->tty_smode.c_cc[VDSUSP] = tty->tty_rmode.c_cc[VDSUSP];
+#endif
+ }
+ if (enif_is_identical(canon, atom_false)) {
+ tty->tty_smode.c_iflag &= ~ICRNL;
+ tty->tty_smode.c_lflag &= ~ICANON;
+ tty->tty_smode.c_oflag &= ~OPOST;
+
+ tty->tty_smode.c_cc[VMIN] = 1;
+ tty->tty_smode.c_cc[VTIME] = 0;
+#ifdef VDSUSP
+ tty->tty_smode.c_cc[VDSUSP] = 0;
+#endif
+ }
+
+ /* Turn echo on or off. */
+ /* erts_fprintf(stderr,"echo %T\r\n", echo); */
+ if (enif_is_identical(echo, atom_true))
+ tty->tty_smode.c_lflag |= ECHO;
+ if (enif_is_identical(echo, atom_false))
+ tty->tty_smode.c_lflag &= ~ECHO;
+
+ /* erts_fprintf(stderr,"sig %T\r\n", sig); */
+ /* Set extra characteristics for "RAW" mode, no signals. */
+ if (enif_is_identical(sig, atom_true)) {
+ /* Ignore IMAXBEL as not POSIX. */
+#ifndef QNX
+ tty->tty_smode.c_iflag |= (BRKINT|IGNPAR|ICRNL|IXON|IXANY);
+#else
+ tty->tty_smode.c_iflag |= (BRKINT|IGNPAR|ICRNL|IXON);
+#endif
+ tty->tty_smode.c_lflag |= (ISIG|IEXTEN);
+ }
+ if (enif_is_identical(sig, atom_false)) {
+ /* Ignore IMAXBEL as not POSIX. */
+#ifndef QNX
+ tty->tty_smode.c_iflag &= ~(BRKINT|IGNPAR|ICRNL|IXON|IXANY);
+#else
+ tty->tty_smode.c_iflag &= ~(BRKINT|IGNPAR|ICRNL|IXON);
+#endif
+ tty->tty_smode.c_lflag &= ~(ISIG|IEXTEN);
+ }
+
+#else
+ /* Set output mode to handle virtual terminal sequences */
+ HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
+ if (hOut == INVALID_HANDLE_VALUE)
+ {
+ return make_errno_error(env, "GetStdHandle");
+ }
+ HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
+ if (hIn == INVALID_HANDLE_VALUE)
+ {
+ return make_errno_error(env, "GetStdHandle");
+ }
+
+ DWORD dwOriginalOutMode = 0;
+ DWORD dwOriginalInMode = 0;
+ if (!GetConsoleMode(hOut, &dwOriginalOutMode))
+ {
+ return make_errno_error(env, "GetConsoleMode");
+ }
+ if (!GetConsoleMode(hIn, &dwOriginalInMode))
+ {
+ return make_errno_error(env, "GetConsoleMode");
+ }
+
+ /* fprintf(stderr, "origOutMode: %x origInMode: %x\r\n", */
+ /* dwOriginalOutMode, dwOriginalInMode); */
+
+ DWORD dwRequestedOutModes = ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN;
+ DWORD dwRequestedInModes = ENABLE_VIRTUAL_TERMINAL_INPUT;
+ DWORD dwDisabledInModes = ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT;
+
+ DWORD dwOutMode = dwOriginalOutMode | dwRequestedOutModes;
+ if (!SetConsoleMode(hOut, dwOutMode))
+ {
+ /* we failed to set both modes, try to step down mode gracefully. */
+ dwRequestedOutModes = ENABLE_VIRTUAL_TERMINAL_PROCESSING;
+ dwOutMode = dwOriginalOutMode | dwRequestedOutModes;
+ if (!SetConsoleMode(hOut, dwOutMode))
+ {
+ /* Failed to set any VT mode, can't do anything here. */
+ return make_errno_error(env, "SetConsoleMode");
+ }
+ }
+
+ DWORD dwInMode = (dwOriginalInMode | dwRequestedInModes) & ~dwDisabledInModes;
+ if (!SetConsoleMode(hIn, dwInMode))
+ {
+ /* Failed to set VT input mode, can't do anything here. */
+ return make_errno_error(env, "SetConsoleMode");
+ }
+
+#endif /* __WIN32__ */
+
+ tty->tty = 1;
+
+ return atom_ok;
+#else
+ return make_enotsup(env);
+#endif
+}
+
+static ERL_NIF_TERM tty_set_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
+#if defined(HAVE_TERMCAP) || defined(__WIN32__)
+ TTYResource *tty;
+ if (!enif_get_resource(env, argv[0], tty_rt, (void **)&tty))
+ return enif_make_badarg(env);
+#ifdef HAVE_TERMCAP
+ if (tty->tty && tcsetattr(tty->ifd, TCSANOW, &tty->tty_smode) < 0) {
+ return make_errno_error(env, "tcsetattr");
+ }
+#endif
+ enif_self(env, &tty->self);
+ enif_monitor_process(env, tty, &tty->self, NULL);
+ return atom_ok;
+#else
+ return make_enotsup(env);
+#endif
+}
+
+static ERL_NIF_TERM tty_window_size_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
+ TTYResource *tty;
+ int width = -1, height = -1;
+ if (!enif_get_resource(env, argv[0], tty_rt, (void **)&tty))
+ return enif_make_badarg(env);
+ {
+#ifdef TIOCGWINSZ
+ struct winsize ws;
+ if (ioctl(tty->ifd,TIOCGWINSZ,&ws) == 0) {
+ if (ws.ws_col > 0)
+ width = ws.ws_col;
+ if (ws.ws_row > 0)
+ height = ws.ws_row;
+ } else if (ioctl(tty->ofd,TIOCGWINSZ,&ws) == 0) {
+ if (ws.ws_col > 0)
+ width = ws.ws_col;
+ if (ws.ws_row > 0)
+ height = ws.ws_row;
+ }
+#elif defined(__WIN32__)
+ CONSOLE_SCREEN_BUFFER_INFOEX buffer_info;
+ buffer_info.cbSize = sizeof(buffer_info);
+ if (GetConsoleScreenBufferInfoEx(tty->ofd, &buffer_info)) {
+ height = buffer_info.dwSize.Y;
+ width = buffer_info.dwSize.X;
+ } else {
+ return make_errno_error(env,"GetConsoleScreenBufferInfoEx");
+ }
+#endif
+ }
+ if (width == -1 && height == -1) {
+ return make_enotsup(env);
+ }
+ return enif_make_tuple2(
+ env, atom_ok,
+ enif_make_tuple2(
+ env,
+ enif_make_int(env, width),
+ enif_make_int(env, height)
+ ));
+}
+
+#ifndef __WIN32__
+
+static int tty_signal_fd = -1;
+
+static RETSIGTYPE tty_cont(int sig)
+{
+ if (tty_signal_fd != 1) {
+ while (write(tty_signal_fd, "c", 1) < 0 && errno == EINTR) { };
+ }
+}
+
+
+static RETSIGTYPE tty_winch(int sig)
+{
+ if (tty_signal_fd != 1) {
+ while (write(tty_signal_fd, "w", 1) < 0 && errno == EINTR) { };
+ }
+}
+
+#endif
+
+static ERL_NIF_TERM tty_read_signal_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
+ TTYResource *tty;
+ char buff[1];
+ ssize_t ret;
+ ERL_NIF_TERM res;
+ if (!enif_get_resource(env, argv[0], tty_rt, (void **)&tty))
+ return enif_make_badarg(env);
+#ifndef __WIN32__
+ do {
+ ret = read(tty->signal[0], buff, 1);
+ } while (ret < 0 && errno == EAGAIN);
+
+ if (ret < 0) {
+ return make_errno_error(env, "read");
+ } else if (ret == 0) {
+ return make_error(env, enif_make_atom(env,"empty"));
+ }
+
+ enif_select(env, tty->signal[0], ERL_NIF_SELECT_READ, tty, NULL, argv[1]);
+
+ if (buff[0] == 'w') {
+ res = enif_make_atom(env, "winch");
+ } else if (buff[0] == 'c') {
+ res = enif_make_atom(env, "cont");
+ } else {
+ res = enif_make_string_len(env, buff, 1, ERL_NIF_LATIN1);
+ }
+ return enif_make_tuple2(env, atom_ok, res);
+#else
+ return make_enotsup(env);
+#endif
+}
+
+#ifdef THREADED_READED
+struct tty_reader_init {
+ ErlNifEnv *env;
+ ERL_NIF_TERM tty;
+};
+
+#define TTY_READER_BUF_SIZE 1024
+
+static void *tty_reader_thread(void *args) {
+ struct tty_reader_init *tty_reader_init = (struct tty_reader_init*)args;
+ TTYResource *tty;
+ ErlNifBinary binary;
+ ErlNifEnv *env = NULL;
+ ERL_NIF_TERM data[10];
+ int cnt = 0;
+
+ enif_alloc_binary(TTY_READER_BUF_SIZE, &binary);
+
+ enif_get_resource(tty_reader_init->env, tty_reader_init->tty, tty_rt, (void **)&tty);
+
+ SET_BLOCKING(tty->ifd);
+
+ while(true) {
+ ssize_t i = read(tty->ifd, binary.data, TTY_READER_BUF_SIZE);
+ /* fprintf(stderr,"Read: %ld bytes from %d\r\n", i, tty->ifd); */
+ if (i < 0) {
+ int saved_errno = errno;
+ if (env) {
+ ERL_NIF_TERM msg = enif_make_list_from_array(env, data, cnt);
+ enif_send(env, &tty->self, NULL, enif_make_tuple2(env, atom_input, msg));
+ cnt = 0;
+ env = NULL;
+ }
+ if (saved_errno != EAGAIN) {
+ env = enif_alloc_env();
+ errno = saved_errno;
+ enif_send(env, &tty->self, NULL, make_errno_error(env, "read"));
+ break;
+ }
+ } else {
+ if (!env) {
+ env = enif_alloc_env();
+ }
+ enif_realloc_binary(&binary, i);
+ data[cnt++] = enif_make_binary(env, &binary);
+ if (cnt == 10 || i != TTY_READER_BUF_SIZE) {
+ ERL_NIF_TERM msg = enif_make_list_from_array(env, data, cnt);
+ enif_send(env, &tty->self, NULL, enif_make_tuple2(env, atom_input, msg));
+ cnt = 0;
+ env = NULL;
+ }
+ enif_alloc_binary(TTY_READER_BUF_SIZE, &binary);
+ }
+ }
+
+ enif_free_env(tty_reader_init->env);
+ enif_free(tty_reader_init);
+ return (void*)0;
+}
+
+#endif
+
+static ERL_NIF_TERM tty_select_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
+ TTYResource *tty;
+#ifdef THREADED_READER
+ struct tty_reader_init *tty_reader_init;
+#endif
+#ifndef __WIN32__
+ extern int using_oldshell; /* set this to let the rest of erts know */
+#endif
+ if (!enif_get_resource(env, argv[0], tty_rt, (void **)&tty))
+ return enif_make_badarg(env);
+
+#ifndef __WIN32__
+ if (pipe(tty->signal) == -1) {
+ return make_errno_error(env, "pipe");
+ }
+ SET_NONBLOCKING(tty->signal[0]);
+ enif_select(env, tty->signal[0], ERL_NIF_SELECT_READ, tty, NULL, argv[1]);
+ tty_signal_fd = tty->signal[1];
+
+ sys_signal(SIGCONT, tty_cont);
+ sys_signal(SIGWINCH, tty_winch);
+
+ using_oldshell = 0;
+#endif
+
+ enif_select(env, tty->ifd, ERL_NIF_SELECT_READ, tty, NULL, argv[2]);
+
+ enif_self(env, &tty->reader);
+ enif_monitor_process(env, tty, &tty->reader, NULL);
+
+#ifdef THREADED_READER
+
+ tty_reader_init = enif_alloc(sizeof(struct tty_reader_init));
+ tty_reader_init->env = enif_alloc_env();
+ tty_reader_init->tty = enif_make_copy(tty_reader_init->env, argv[0]);
+
+ if (enif_thread_create(
+ "stdin_reader",
+ &tty->reader_tid,
+ tty_reader_thread, tty_reader_init, NULL)) {
+ enif_free(tty_reader_init);
+ return make_errno_error(env, "enif_thread_create");
+ }
+#endif
+ return atom_ok;
+}
+
+static void tty_monitor_down(ErlNifEnv* caller_env, void* obj, ErlNifPid* pid, ErlNifMonitor* mon) {
+ TTYResource *tty = obj;
+#ifdef HAVE_TERMCAP
+ if (enif_compare_pids(pid, &tty->self) == 0) {
+ tcsetattr(tty->ifd, TCSANOW, &tty->tty_rmode);
+ }
+#endif
+ if (enif_compare_pids(pid, &tty->reader) == 0) {
+ enif_select(caller_env, tty->ifd, ERL_NIF_SELECT_STOP, tty, NULL, atom_undefined);
+#ifndef __WIN32__
+ enif_select(caller_env, tty->signal[0], ERL_NIF_SELECT_STOP, tty, NULL, atom_undefined);
+ close(tty->signal[1]);
+ sys_signal(SIGCONT, SIG_DFL);
+ sys_signal(SIGWINCH, SIG_DFL);
+#endif
+ }
+}
+
+static void tty_select_stop(ErlNifEnv* caller_env, void* obj, ErlNifEvent event, int is_direct_call) {
+/* Only used to close the signal pipe on unix */
+#ifndef __WIN32__
+ if (event != 0)
+ close(event);
+#endif
+}
+
+static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
+{
+
+ ErlNifResourceTypeInit rt = {
+ NULL /* dtor */,
+ tty_select_stop,
+ tty_monitor_down};
+
+#define ATOM_DECL(A) atom_##A = enif_make_atom(env, #A)
+ATOMS
+#undef ATOM_DECL
+
+ *priv_data = NULL;
+
+ tty_rt = enif_open_resource_type_x(env, "tty", &rt, ERL_NIF_RT_CREATE, NULL);
+
+ return 0;
+}
+
+static void unload(ErlNifEnv* env, void* priv_data)
+{
+
+}
+
+static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data,
+ ERL_NIF_TERM load_info)
+{
+ if (*old_priv_data != NULL) {
+ return -1; /* Don't know how to do that */
+ }
+ if (*priv_data != NULL) {
+ return -1; /* Don't know how to do that */
+ }
+ if (load(env, priv_data, load_info)) {
+ return -1;
+ }
+ return 0;
+}
diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c
index c3505eddc54d..5f269ea938b1 100644
--- a/erts/emulator/sys/win32/sys.c
+++ b/erts/emulator/sys/win32/sys.c
@@ -32,7 +32,6 @@
#include "erl_sys_driver.h"
#include "global.h"
#include "erl_threads.h"
-#include "../../drivers/win32/win_con.h"
#include "erl_cpu_topology.h"
#include
@@ -125,8 +124,6 @@ BOOL WINAPI ctrl_handler(DWORD dwCtrlType);
static int max_files = 1024;
static BOOL use_named_pipes;
-static BOOL win_console = FALSE;
-
static OSVERSIONINFO int_os_version; /* Version information for Win32. */
@@ -205,10 +202,6 @@ erts_sys_misc_mem_sz(void)
*/
void sys_tty_reset(int exit_code)
{
- if (exit_code == ERTS_ERROR_EXIT)
- ConWaitForExit();
- else
- ConNormalExit();
}
void erl_sys_args(int* argc, char** argv)
@@ -304,25 +297,16 @@ int erts_set_signal(Eterm signal, Eterm type) {
return 0;
}
+static DWORD dwOriginalOutMode = 0;
+static DWORD dwOriginalInMode = 0;
+
static void
init_console(void)
{
- char* mode = erts_read_env("ERL_CONSOLE_MODE");
-
- if (!mode || strcmp(mode, "window") == 0) {
- win_console = TRUE;
- ConInit();
- /*nohup = 0;*/
- } else if (strncmp(mode, "tty:", 4) == 0) {
- if (mode[5] == 'c') {
- setvbuf(stdout, NULL, _IONBF, 0);
- }
- if (mode[6] == 'c') {
- setvbuf(stderr, NULL, _IONBF, 0);
- }
- }
-
- erts_free_read_env(mode);
+ GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &dwOriginalOutMode);
+ GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &dwOriginalInMode);
+ setvbuf(stdout, NULL, _IONBF, 0);
+ setvbuf(stderr, NULL, _IONBF, 0);
}
int sys_max_files(void)
@@ -2194,7 +2178,6 @@ fd_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts)
return ERL_DRV_ERROR_GENERAL;
}
- fd_driver_input = &(dp->in);
dp->in.flags = DF_XLAT_CR;
if (is_std_error) {
dp->out.flags |= DF_DROP_IF_INVH; /* Just drop messages if stderror
@@ -2202,6 +2185,7 @@ fd_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts)
}
if ( in == 0 && out == 1) {
+ fd_driver_input = &(dp->in);
save_01_port = dp;
} else if (in == 2 && out == 2) {
save_22_port = dp;
@@ -2945,10 +2929,6 @@ sys_get_key(int fd)
{
ASSERT(fd == 0);
- if (win_console) {
- return ConGetKey();
- }
-
/*
* Black magic follows. (Code stolen from get_overlapped_result())
*/
@@ -2974,6 +2954,32 @@ sys_get_key(int fd)
}
}
}
+ else {
+ char c[64];
+ DWORD dwBytesRead, dwCurrentOutMode = 0, dwCurrentInMode = 0;
+
+ /* Get current console information */
+ GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &dwCurrentOutMode);
+ GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &dwCurrentInMode);
+
+ /* Set the a "oldstyle" terminal with line input that we can use ReadFile on */
+ SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), dwOriginalOutMode);
+ SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE),
+ ENABLE_PROCESSED_INPUT |
+ ENABLE_LINE_INPUT |
+ ENABLE_ECHO_INPUT |
+ ENABLE_INSERT_MODE |
+ ENABLE_QUICK_EDIT_MODE |
+ ENABLE_AUTO_POSITION
+ );
+
+ if (ReadFile(GetStdHandle(STD_INPUT_HANDLE), &c, sizeof(c), &dwBytesRead, NULL) && dwBytesRead > 0) {
+ /* Restore original console information */
+ SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), dwCurrentOutMode);
+ SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), dwCurrentInMode);
+ return c[0];
+ }
+ }
return '*'; /* Error! */
}
diff --git a/erts/emulator/sys/win32/sys_interrupt.c b/erts/emulator/sys/win32/sys_interrupt.c
index cee269eed4b1..b8821e47aa61 100644
--- a/erts/emulator/sys/win32/sys_interrupt.c
+++ b/erts/emulator/sys/win32/sys_interrupt.c
@@ -23,11 +23,13 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
+
+#define ERTS_WANT_BREAK_HANDLING
+
#include "sys.h"
#include "erl_alloc.h"
#include "erl_thr_progress.h"
#include "erl_driver.h"
-#include "../../drivers/win32/win_con.h"
#if defined(__GNUC__)
# define WIN_SYS_INLINE __inline__
@@ -82,7 +84,6 @@ BOOL WINAPI ctrl_handler_ignore_break(DWORD dwCtrlType)
}
void erts_set_ignore_break(void) {
- ConSetCtrlHandler(ctrl_handler_ignore_break);
SetConsoleCtrlHandler(ctrl_handler_ignore_break, TRUE);
}
@@ -92,6 +93,9 @@ BOOL WINAPI ctrl_handler_replace_intr(DWORD dwCtrlType)
case CTRL_C_EVENT:
return FALSE;
case CTRL_BREAK_EVENT:
+ if (ERTS_BREAK_REQUESTED) {
+ erts_exit(ERTS_INTR_EXIT, "");
+ }
SetEvent(erts_sys_break_event);
break;
case CTRL_LOGOFF_EVENT:
@@ -110,7 +114,11 @@ BOOL WINAPI ctrl_handler_replace_intr(DWORD dwCtrlType)
/* Don't use ctrl-c for break handler but let it be
used by the shell instead (see user_drv.erl) */
void erts_replace_intr(void) {
- ConSetCtrlHandler(ctrl_handler_replace_intr);
+ HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
+ DWORD dwOriginalInMode = 0;
+ if (GetConsoleMode(hIn, &dwOriginalInMode)) {
+ SetConsoleMode(hIn, dwOriginalInMode & ~ENABLE_PROCESSED_INPUT);
+ }
SetConsoleCtrlHandler(ctrl_handler_replace_intr, TRUE);
}
@@ -119,6 +127,9 @@ BOOL WINAPI ctrl_handler(DWORD dwCtrlType)
switch (dwCtrlType) {
case CTRL_C_EVENT:
case CTRL_BREAK_EVENT:
+ if (ERTS_BREAK_REQUESTED) {
+ erts_exit(ERTS_INTR_EXIT, "");
+ }
SetEvent(erts_sys_break_event);
break;
case CTRL_LOGOFF_EVENT:
@@ -135,7 +146,6 @@ BOOL WINAPI ctrl_handler(DWORD dwCtrlType)
void init_break_handler()
{
- ConSetCtrlHandler(ctrl_handler);
SetConsoleCtrlHandler(ctrl_handler, TRUE);
}
diff --git a/erts/emulator/test/statistics_SUITE.erl b/erts/emulator/test/statistics_SUITE.erl
index c230aa9f194c..014c0a114e3d 100644
--- a/erts/emulator/test/statistics_SUITE.erl
+++ b/erts/emulator/test/statistics_SUITE.erl
@@ -382,7 +382,7 @@ run_scheduler_wall_time_test(Type) ->
Pid
end,
StartDirtyHog = fun(Func) ->
- F = fun () ->
+ F = fun() ->
erts_debug:Func(alive_waitexiting,
MeMySelfAndI)
end,
@@ -470,7 +470,7 @@ online_statistics(Stats) ->
DirtyCPUSchedulersOnline = erlang:system_info(dirty_cpu_schedulers_online),
DirtyIOSchedulersOnline = erlang:system_info(dirty_io_schedulers),
SortedStats = lists:sort(Stats),
- ct:pal("Stats: ~p~n", [SortedStats]),
+ ct:log("Stats: ~p~n", [SortedStats]),
SchedulersStats =
lists:sublist(SortedStats, 1, SchedulersOnline),
DirtyCPUSchedulersStats =
diff --git a/erts/etc/common/Makefile.in b/erts/etc/common/Makefile.in
index 42d4395eb246..37f77d294b5f 100644
--- a/erts/etc/common/Makefile.in
+++ b/erts/etc/common/Makefile.in
@@ -168,7 +168,6 @@ INSTALL_PROGS = \
$(BINDIR)/erlsrv.exe \
$(BINDIR)/erl.exe \
$(BINDIR)/erl_log.exe\
- $(BINDIR)/werl.exe \
$(BINDIR)/$(ERLEXEC) \
$(INSTALL_EMBEDDED_PROGS)
@@ -267,13 +266,12 @@ endif
rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/vxcall.o
rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/erl.o
rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/erl_log.o
- rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/werl.o
rm -f $(TEXTFILES)
rm -f *~ core
#------------------------------------------------------------------------
# Windows specific targets
-# The windows platform is quite different from the others. erl/werl are small C programs
+# The windows platform is quite different from the others. erl are small C programs
# loading a DLL. INI files are used instead of environment variables and the Install
# script is actually a program, also Install has an INI file which tells of emulator
# versions etc.
@@ -287,9 +285,6 @@ $(BINDIR)/$(ERLEXEC): $(OBJDIR)/erlexec.o $(OBJDIR)/win_erlexec.o $(OBJDIR)/init
$(BINDIR)/erl@EXEEXT@: $(OBJDIR)/erl.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ)
$(V_LD) $(LDFLAGS) -o $@ $(OBJDIR)/erl.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ)
-$(BINDIR)/werl@EXEEXT@: $(OBJDIR)/werl.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ)
- $(V_LD) $(LDFLAGS) -o $@ $(OBJDIR)/werl.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ)
-
$(BINDIR)/erl_log@EXEEXT@: $(OBJDIR)/erl_log.o
$(V_LD) $(LDFLAGS) -o $@ $(OBJDIR)/erl_log.o
@@ -367,10 +362,6 @@ $(OBJDIR)/erlsrv_util.o: $(WINETC)/erlsrv/erlsrv_util.c $(ERLSRV_HEADERS) \
$(OBJDIR)/erlsrv_logmess.h $(RC_GENERATED)
$(V_CC) $(CFLAGS) -I$(OBJDIR) $(MT_FLAG) -o $@ -c $<
-$(OBJDIR)/werl.o: $(WINETC)/erl.c $(WINETC)/init_file.h $(RC_GENERATED)
- $(V_CC) $(CFLAGS) -DBUILD_TYPE=\"-$(TYPE)\" -DERL_RUN_SHARED_LIB=1 \
- -DWIN32_WERL -o $@ -c $(WINETC)/erl.c
-
$(OBJDIR)/erl_log.o: $(WINETC)/erl_log.c $(RC_GENERATED)
$(V_CC) $(CFLAGS) -DBUILD_TYPE=\"-$(TYPE)\" -DERL_RUN_SHARED_LIB=1 \
-o $@ -c $(WINETC)/erl_log.c
diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c
index 5d4432789be2..b666b4adec17 100644
--- a/erts/etc/common/erlexec.c
+++ b/erts/etc/common/erlexec.c
@@ -39,14 +39,12 @@
#define DIRSEP "\\"
#define PATHSEP ";"
#define NULL_DEVICE "nul"
-#define BINARY_EXT ""
#define DLL_EXT ".dll"
#define EMULATOR_EXECUTABLE "beam.dll"
#else
#define PATHSEP ":"
#define DIRSEP "/"
#define NULL_DEVICE "/dev/null"
-#define BINARY_EXT ""
#define EMULATOR_EXECUTABLE "beam"
#endif
@@ -218,7 +216,6 @@ static char* possibly_quote(char* arg);
/*
* Functions from win_erlexec.c
*/
-int start_win_emulator(char* emu, char *startprog,char** argv, int start_detached);
int start_emulator(char* emu, char*start_prog, char** argv, int start_detached);
#endif
@@ -246,7 +243,7 @@ static const char* emu_flavor = DEFAULT_SUFFIX; /* Flavor of emulator (smp, jit
#ifdef __WIN32__
static char *start_emulator_program = NULL; /* For detached mode -
- erl.exe/werl.exe */
+ erl.exe */
static char* key_val_name = ERLANG_VERSION; /* Used by the registry
* access functions.
*/
@@ -256,7 +253,6 @@ static int config_script_cnt = 0;
static int got_start_erl = 0;
static HANDLE this_module_handle;
-static int run_werl;
static WCHAR *utf8_to_utf16(unsigned char *bytes);
static char *utf16_to_utf8(WCHAR *wstr);
static WCHAR *latin1_to_utf16(char *str);
@@ -414,7 +410,7 @@ static void add_boot_config(void)
#define NEXT_ARG_CHECK() NEXT_ARG_CHECK_NAMED(argv[i])
#ifdef __WIN32__
-__declspec(dllexport) int win_erlexec(int argc, char **argv, HANDLE module, int windowed)
+__declspec(dllexport) int win_erlexec(int argc, char **argv, HANDLE module)
#else
int main(int argc, char **argv)
#endif
@@ -435,7 +431,6 @@ int main(int argc, char **argv)
#ifdef __WIN32__
this_module_handle = module;
- run_werl = windowed;
/* if we started this erl just to get a detached emulator,
* the arguments are already prepared for beam, so we skip
* directly to start_emulator */
@@ -534,7 +529,7 @@ int main(int argc, char **argv)
emu = add_extra_suffixes(emu);
emu_name = strsave(emu);
- erts_snprintf(tmpStr, sizeof(tmpStr), "%s" DIRSEP "%s" BINARY_EXT, bindir, emu);
+ erts_snprintf(tmpStr, sizeof(tmpStr), "%s" DIRSEP "%s", bindir, emu);
emu = strsave(tmpStr);
s = get_env("ESCRIPT_NAME");
@@ -1127,24 +1122,7 @@ int main(int argc, char **argv)
skip_arg_massage:
/*DebugBreak();*/
- if (run_werl) {
- if (start_detached) {
- char *p;
- /* transform werl to erl */
- p = start_emulator_program+strlen(start_emulator_program);
- while (--p >= start_emulator_program && *p != '/' && *p != '\\' &&
- *p != 'W' && *p != 'w')
- ;
- if (p >= start_emulator_program && (*p == 'W' || *p == 'w') &&
- (p[1] == 'E' || p[1] == 'e') && (p[2] == 'R' || p[2] == 'r') &&
- (p[3] == 'L' || p[3] == 'l')) {
- memmove(p,p+1,strlen(p));
- }
- }
- return start_win_emulator(emu, start_emulator_program, Eargsp, start_detached);
- } else {
- return start_emulator(emu, start_emulator_program, Eargsp, start_detached);
- }
+ return start_emulator(emu, start_emulator_program, Eargsp, start_detached);
#else
@@ -1610,6 +1588,14 @@ static void get_parameters(int argc, char** argv)
emu = EMULATOR_EXECUTABLE;
start_emulator_program = strsave(argv[0]);
+ /* in wsl argv[0] is given as "erl.exe", but start_emulator_program should be
+ an absolute path, so we prepend BINDIR to it */
+ if (strcmp(start_emulator_program, "erl.exe") == 0) {
+ erts_snprintf(tmpStr, sizeof(tmpStr), "%s" DIRSEP "%s", bindir,
+ start_emulator_program);
+ start_emulator_program = strsave(tmpStr);
+ }
+
free(ini_filename);
}
diff --git a/erts/etc/common/etc_common.h b/erts/etc/common/etc_common.h
index 289a33b42a82..865cb6a6c6ff 100644
--- a/erts/etc/common/etc_common.h
+++ b/erts/etc/common/etc_common.h
@@ -35,6 +35,7 @@
# include
# include
# include
+# include // _getcwd
#endif
#include
diff --git a/erts/etc/win32/Install.c b/erts/etc/win32/Install.c
index 1b8f894dc946..497dd537fd2a 100644
--- a/erts/etc/win32/Install.c
+++ b/erts/etc/win32/Install.c
@@ -24,6 +24,7 @@
*/
#include
+#include
#include
#include
#include "init_file.h"
@@ -47,11 +48,12 @@ int wmain(int argc, wchar_t **argv)
InitFile *ini_file;
InitSection *ini_section;
HANDLE module = GetModuleHandle(NULL);
- wchar_t *binaries[] = { L"erl.exe", L"werl.exe", L"erlc.exe", L"erl_call.exe",
+ wchar_t *binaries[] = { L"erl.exe", L"erlc.exe", L"erl_call.exe",
L"dialyzer.exe",
L"typer.exe",
L"escript.exe", L"ct_run.exe", NULL };
wchar_t *scripts[] = { L"start_clean.boot", L"start_sasl.boot", L"no_dot_erlang.boot", NULL };
+ wchar_t *links[][2] = { { L"erl.exe", L"werl.exe" }, NULL };
wchar_t fromname[MAX_PATH];
wchar_t toname[MAX_PATH];
size_t converted;
@@ -175,7 +177,32 @@ int wmain(int argc, wchar_t **argv)
fprintf(stderr,"Continuing installation anyway...\n");
}
}
-
+
+ for (i = 0; links[i][0] != NULL; ++i) {
+ swprintf(toname,MAX_PATH,L"%s\\%s",bin_dir,links[i][1]);
+ if (!CreateSymbolicLinkW(toname,links[i][0],0)) {
+ DWORD err = GetLastError();
+ if (err == ERROR_PRIVILEGE_NOT_HELD) {
+ fprintf(stderr,"Must be administrator to create link, copying %S instead.\n",
+ links[i][0]);
+ swprintf(fromname,MAX_PATH,L"%s\\%s",bin_dir,links[i][0]);
+ if (!CopyFileW(fromname,toname,FALSE)) {
+ fprintf(stderr,"Could not copy file %S to %S\n",
+ fromname,toname);
+ fprintf(stderr,"Continuing installation anyway...\n");
+ }
+ } else {
+ wchar_t buf[256];
+ FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ buf, (sizeof(buf) / sizeof(wchar_t)), NULL);
+ fprintf(stderr,"Could not create links from %S to %S %d: %S\n",
+ fromname, toname, err, buf);
+ fprintf(stderr,"Continuing installation anyway...\n");
+ }
+ }
+ }
+
for (i = 0; scripts[i] != NULL; ++i) {
swprintf(fromname,MAX_PATH,L"%s\\%s",release_dir,scripts[i]);
swprintf(toname,MAX_PATH,L"%s\\%s",bin_dir,scripts[i]);
diff --git a/erts/etc/win32/Makefile b/erts/etc/win32/Makefile
index c6376ebe7405..f553f83e9220 100644
--- a/erts/etc/win32/Makefile
+++ b/erts/etc/win32/Makefile
@@ -39,7 +39,6 @@ ROOTDIR = $(ERL_TOP)/erts
INSTALL_PROGS = \
$(BINDIR)/inet_gethost.exe \
$(BINDIR)/erl.exe \
- $(BINDIR)/werl.exe \
$(BINDIR)/heart.exe \
$(BINDIR)/erlc.exe \
$(BINDIR)/erlsrv.exe \
diff --git a/erts/etc/win32/erl.c b/erts/etc/win32/erl.c
index 99a41b99e5f9..31650de83198 100644
--- a/erts/etc/win32/erl.c
+++ b/erts/etc/win32/erl.c
@@ -23,7 +23,7 @@
#include
#include "init_file.h"
-typedef int ErlexecFunction(int, char **, HANDLE, int);
+typedef int ErlexecFunction(int, char **, HANDLE);
#define INI_FILENAME L"erl.ini"
#define INI_SECTION "erlang"
@@ -35,18 +35,8 @@ static void error(char* format, ...);
static wchar_t *erlexec_name;
static wchar_t *erlexec_dir;
-#ifdef WIN32_WERL
-#define WERL 1
-int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
- PWSTR szCmdLine, int iCmdShow)
-{
- int argc = __argc;
- wchar_t **argv = __wargv;
-#else
-#define WERL 0
int wmain(int argc, wchar_t **argv)
{
-#endif
HANDLE erlexec_handle; /* Instance */
ErlexecFunction *win_erlexec;
wchar_t *path = malloc(100*sizeof(wchar_t));
@@ -120,7 +110,7 @@ int wmain(int argc, wchar_t **argv)
}
#endif
- return (*win_erlexec)(argc,utf8argv,erlexec_handle,WERL);
+ return (*win_erlexec)(argc,utf8argv,erlexec_handle);
}
@@ -316,7 +306,6 @@ static void get_parameters(void)
free(ini_filename);
}
-
static void error(char* format, ...)
{
char sbuf[2048];
@@ -326,11 +315,6 @@ static void error(char* format, ...)
vsprintf(sbuf, format, ap);
va_end(ap);
-#ifndef WIN32_WERL
- fprintf(stderr, "%s\n", sbuf);
-#else
- MessageBox(NULL, sbuf, "Werl", MB_OK|MB_ICONERROR);
-#endif
+ fprintf(stderr, "%s\n", sbuf);
exit(1);
}
-
diff --git a/erts/etc/win32/nsis/erlang20.nsi b/erts/etc/win32/nsis/erlang20.nsi
index ae933f59af97..2e317fd3629a 100644
--- a/erts/etc/win32/nsis/erlang20.nsi
+++ b/erts/etc/win32/nsis/erlang20.nsi
@@ -190,7 +190,7 @@ cp_files:
CreateDirectory "$SMPROGRAMS\$STARTMENU_FOLDER"
continue_create:
CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\Erlang.lnk" \
- "$INSTDIR\bin\werl.exe"
+ "$INSTDIR\bin\erl.exe"
!insertmacro MUI_STARTMENU_WRITE_END
; And once again, the verbosity...
diff --git a/erts/etc/win32/win_erlexec.c b/erts/etc/win32/win_erlexec.c
index 7b21ed37850c..53b1ac92b0a8 100644
--- a/erts/etc/win32/win_erlexec.c
+++ b/erts/etc/win32/win_erlexec.c
@@ -48,7 +48,6 @@ static char* win32_errorstr(int error);
static int has_console(void);
static char** fnuttify_argv(char **argv);
static void free_fnuttified(char **v);
-static int windowed = 0;
#ifdef LOAD_BEAM_DYNAMICALLY
typedef int SysGetKeyFunction(int);
@@ -133,103 +132,6 @@ free_env_val(char *value)
free(value);
}
-
-int
-start_win_emulator(char* utf8emu, char *utf8start_prog, char** utf8argv, int start_detached)
-{
- int len;
- int argc = 0;
-
- windowed = 1;
- while (utf8argv[argc] != NULL) {
- ++argc;
- }
-
- if (start_detached) {
- wchar_t *start_prog=NULL;
- int result;
- int i;
- wchar_t **argv;
- close(0);
- close(1);
- close(2);
-
- set_env("ERL_CONSOLE_MODE", "detached");
- set_env(DLL_ENV, utf8emu);
-
- utf8argv[0] = utf8start_prog;
- utf8argv = fnuttify_argv(utf8argv);
-
- len = MultiByteToWideChar(CP_UTF8, 0, utf8start_prog, -1, NULL, 0);
- start_prog = malloc(len*sizeof(wchar_t));
- MultiByteToWideChar(CP_UTF8, 0, utf8start_prog, -1, start_prog, len);
-
- /* Convert utf8argv to multibyte argv */
- argv = malloc((argc+1) * sizeof(wchar_t*));
- for (i=0; i>,
+ down = <<"\n">>,
+ left = <<"\b">>,
+ right = <<"\e[C">>,
+ %% Tab to next 8 column windows is "\e[1I", for unix "ta" termcap
+ tab = <<"\e[1I">>,
+ insert = false,
+ delete = false,
+ position = <<"\e[6n">>, %% "u7" on my Linux
+ position_reply = <<"\e\\[([0-9]+);([0-9]+)R">>,
+ %% Copied from https://github.com/chalk/ansi-regex/blob/main/index.js
+ ansi_regexp = <<"^[\e",194,155,"][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?",7,")|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))">>,
+ %% The SGR (Select Graphic Rendition) parameters https://en.wikipedia.org/wiki/ANSI_escape_code#SGR
+ ansi_sgr = <<"^[\e",194,155,"]\\[[0-9;:]*m">>
+ }).
+
+-type options() :: #{ tty => boolean(),
+ canon => boolean(),
+ echo => boolean(),
+ sig => boolean()
+ }.
+-type request() ::
+ {putc, unicode:unicode_binary()} |
+ {insert, unicode:unicode_binary()} |
+ {delete, integer()} |
+ {move, integer()} |
+ beep.
+-opaque state() :: #state{}.
+-export_type([state/0]).
+
+-spec on_load() -> ok.
+on_load() ->
+ on_load(#{}).
+
+-spec on_load(Extra) -> ok when
+ Extra :: map().
+on_load(Extra) ->
+ case erlang:load_nif(atom_to_list(?MODULE), Extra) of
+ ok -> ok;
+ {error,{reload,_}} ->
+ ok
+ end.
+
+window_size(State = #state{ tty = TTY }) ->
+ case tty_window_size(TTY) of
+ {error, enotsup} when map_get(tty, State#state.options) ->
+ %% When the TTY is enabled, we should return a "dummy" row and column
+ %% when we cannot find the proper size.
+ {ok, {State#state.cols, State#state.rows}};
+ WinSz ->
+ WinSz
+ end.
+
+-spec init(options()) -> state().
+init(UserOptions) when is_map(UserOptions) ->
+
+ Options = options(UserOptions),
+ {ok, TTY} = tty_create(),
+
+ %% Initialize the locale to see if we support utf-8 or not
+ UnicodeMode =
+ case setlocale() of
+ primitive ->
+ lists:any(
+ fun(Key) ->
+ string:find(os:getenv(Key,""),"UTF-8") =/= nomatch
+ end, ["LC_ALL", "LC_CTYPE", "LANG"]);
+ UnicodeLocale when is_boolean(UnicodeLocale) ->
+ UnicodeLocale
+ end,
+
+ init_term(#state{ tty = TTY, unicode = UnicodeMode, options = Options }).
+init_term(State = #state{ tty = TTY, options = Options }) ->
+ TTYState =
+ case maps:get(tty, Options) of
+ true ->
+ ok = tty_init(TTY, stdout, Options),
+ ok = tty_set(TTY),
+ init(State, os:type());
+ false ->
+ State
+ end,
+
+ {ok, Writer} = proc_lib:start_link(?MODULE, writer, [State#state.tty]),
+ {ok, Reader} = proc_lib:start_link(?MODULE, reader, [[State#state.tty, self()]]),
+
+ update_geometry(TTYState#state{ reader = Reader, writer = Writer }).
+
+-spec reinit(state(), options()) -> state().
+reinit(State, UserOptions) ->
+ init_term(State#state{ options = options(UserOptions) }).
+
+options(UserOptions) ->
+ maps:merge(
+ #{ input => true,
+ tty => true,
+ canon => false,
+ echo => false }, UserOptions).
+
+init(State, {unix,_}) ->
+ ok = tgetent(os:getenv("TERM")),
+
+ %% See https://www.gnu.org/software/termutils/manual/termcap-1.3/html_mono/termcap.html#SEC23
+ %% for a list of all possible termcap capabilities
+ Cols = case tgetnum("co") of
+ {ok, Cs} -> Cs;
+ _ -> (#state{})#state.cols
+ end,
+ Up = case tgetstr("up") of
+ {ok, U} -> U;
+ false -> error(enotsup)
+ end,
+ Down = case tgetstr("do") of
+ false -> (#state{})#state.down;
+ {ok, D} -> D
+ end,
+ Left = case {tgetflag("bs"),tgetstr("bc")} of
+ {true,_} -> (#state{})#state.left;
+ {_,false} -> (#state{})#state.left;
+ {_,{ok, L}} -> L
+ end,
+
+ Right = case tgetstr("nd") of
+ {ok, R} -> R;
+ false -> error(enotsup)
+ end,
+ Insert =
+ case tgetstr("IC") of
+ {ok, IC} -> IC;
+ false -> (#state{})#state.insert
+ end,
+
+ Tab = case tgetstr("ta") of
+ {ok, TA} -> TA;
+ false -> (#state{})#state.tab
+ end,
+
+ Delete = case tgetstr("DC") of
+ {ok, DC} -> DC;
+ false -> (#state{})#state.delete
+ end,
+
+ Position = case tgetstr("u7") of
+ {ok, <<"\e[6n">> = U7} ->
+ %% User 7 should contain the codes for getting
+ %% cursor position.
+ % User 6 should contain how to parse the reply
+ {ok, <<"\e[%i%d;%dR">>} = tgetstr("u6"),
+ <<"\e[6n">> = U7;
+ false -> (#state{})#state.position
+ end,
+
+ State#state{
+ cols = Cols,
+ xn = tgetflag("xn"),
+ up = Up,
+ down = Down,
+ left = Left,
+ right = Right,
+ insert = Insert,
+ delete = Delete,
+ tab = Tab,
+ position = Position
+ };
+init(State, {win32, _}) ->
+ State#state{
+ %% position = false,
+ xn = true }.
+
+-spec handles(state()) -> #{ read := undefined | reference(),
+ write := reference() }.
+handles(#state{ reader = undefined,
+ writer = {_WriterPid, WriterRef}}) ->
+ #{ read => undefined, write => WriterRef };
+handles(#state{ reader = {_ReaderPid, ReaderRef},
+ writer = {_WriterPid, WriterRef}}) ->
+ #{ read => ReaderRef, write => WriterRef }.
+
+-spec unicode(state()) -> boolean().
+unicode(State) ->
+ State#state.unicode.
+
+-spec unicode(state(), boolean()) -> state().
+unicode(#state{ reader = {ReaderPid, _} } = State, Bool) ->
+ MonRef = erlang:monitor(process, ReaderPid),
+ ReaderPid ! {self(), set_unicode_state, Bool},
+ receive
+ {ReaderPid, set_unicode_state, _} -> ok;
+ {'DOWN',MonRef,_,_,_} -> ok
+ end,
+ State#state{ unicode = Bool }.
+
+-spec handle_signal(state(), winch | cont) -> state().
+handle_signal(State, winch) ->
+ update_geometry(State);
+handle_signal(State, cont) ->
+ tty_set(State#state.tty),
+ State.
+
+reader([TTY, Parent]) ->
+ register(user_drv_reader, self()),
+ ReaderRef = make_ref(),
+ SignalRef = make_ref(),
+ ok = tty_select(TTY, SignalRef, ReaderRef),
+ proc_lib:init_ack({ok, {self(), ReaderRef}}),
+ FromEnc = case os:type() of
+ {unix, _} -> utf8;
+ {win32, _} -> {utf16, little}
+ end,
+ reader_loop(TTY, Parent, SignalRef, ReaderRef, FromEnc, <<>>).
+
+reader_loop(TTY, Parent, SignalRef, ReaderRef, FromEnc, Acc) ->
+ receive
+ {select, TTY, SignalRef, ready_input} ->
+ {ok, Signal} = tty_read_signal(TTY, SignalRef),
+ Parent ! {ReaderRef,{signal,Signal}},
+ reader_loop(TTY, Parent, SignalRef, ReaderRef, FromEnc, Acc);
+ {Parent, set_unicode_state, _} when FromEnc =:= {utf16, little} ->
+ %% Ignore requests on windows
+ Parent ! {self(), set_unicode_state, true},
+ reader_loop(TTY, Parent, SignalRef, ReaderRef, FromEnc, Acc);
+ {Parent, set_unicode_state, Bool} ->
+ Parent ! {self(), set_unicode_state, FromEnc =/= latin1},
+ NewFromEnc = if Bool -> utf8; not Bool -> latin1 end,
+ reader_loop(TTY, Parent, SignalRef, ReaderRef, NewFromEnc, Acc);
+ {select, TTY, ReaderRef, ready_input} ->
+ case read_nif(TTY, ReaderRef) of
+ {error, closed} ->
+ Parent ! {ReaderRef, eof},
+ ok;
+ {ok, <<>>} ->
+ %% EAGAIN or EINTR
+ reader_loop(TTY, Parent, SignalRef, ReaderRef, FromEnc, Acc);
+ {ok, UtfXBytes} ->
+
+ {Bytes, NewAcc, NewFromEnc} =
+ case unicode:characters_to_binary([Acc, UtfXBytes], FromEnc, utf8) of
+ {error, B, Error} ->
+ %% We should only be able to get incorrect encoded data when
+ %% using utf8 (i.e. we are on unix)
+ FromEnc = utf8,
+ Parent ! {self(), set_unicode_state, false},
+ receive
+ {Parent, set_unicode_state, false} ->
+ Parent ! {self(), set_unicode_state, true}
+ end,
+ receive
+ {Parent, set_unicode_state, true} -> ok
+ end,
+ Latin1Chars = unicode:characters_to_binary(Error, latin1, utf8),
+ {<>, <<>>, latin1};
+ {incomplete, B, Inc} ->
+ {B, Inc, FromEnc};
+ B when is_binary(B) ->
+ {B, <<>>, FromEnc}
+ end,
+ Parent ! {ReaderRef, {data, Bytes}},
+ reader_loop(TTY, Parent, SignalRef, ReaderRef, NewFromEnc, NewAcc)
+ end
+ end.
+
+writer(TTY) ->
+ register(user_drv_writer, self()),
+ WriterRef = make_ref(),
+ proc_lib:init_ack({ok, {self(), WriterRef}}),
+ writer_loop(TTY, WriterRef).
+
+-spec write(state(), unicode:chardata()) -> ok.
+write(#state{ writer = {WriterPid, _}}, Chars) ->
+ WriterPid ! {write, erlang:iolist_to_iovec(Chars)}, ok.
+-spec write(state(), unicode:chardata(), From :: pid()) -> ok.
+write(#state{ writer = {WriterPid, _}}, Chars, From) ->
+ WriterPid ! {write, From, erlang:iolist_to_iovec(Chars)}, ok.
+
+writer_loop(TTY, WriterRef) ->
+ receive
+ {write, []} ->
+ writer_loop(TTY, WriterRef);
+ {write, Chars} ->
+ ok = write_nif(TTY, Chars),
+ writer_loop(TTY, WriterRef);
+ {write, From, []} ->
+ From ! {WriterRef, ok},
+ writer_loop(TTY, WriterRef);
+ {write, From, Chars} ->
+ case write_nif(TTY, Chars) of
+ ok ->
+ From ! {WriterRef, ok},
+ writer_loop(TTY, WriterRef);
+ Else ->
+ From ! {WriterRef, Else},
+ writer_loop(TTY, WriterRef)
+ end
+ end.
+
+-spec handle_request(state(), request()) -> {erlang:iovec(), state()}.
+handle_request(State = #state{ options = #{ tty := false } }, Request) ->
+ case Request of
+ {putc, Binary} ->
+ {encode(Binary, State#state.unicode), State};
+ beep ->
+ {<<7>>, State};
+ _Ignore ->
+ {<<>>, State}
+ end;
+%% putc prints Binary and overwrites any existing characters
+handle_request(State = #state{ unicode = U }, {putc, Binary}) ->
+ %% Todo should handle invalid unicode?
+ {PutBuffer, NewState} = insert_buf(State, Binary),
+ if NewState#state.buffer_after =:= [] ->
+ {encode(PutBuffer, U), NewState};
+ true ->
+ %% Delete any overwritten characters after current the cursor
+ OldLength = logical(State#state.buffer_before),
+ NewLength = logical(NewState#state.buffer_before),
+ {_, _, _, NewBA} = split(NewLength - OldLength, NewState#state.buffer_after, U),
+ {encode(PutBuffer, U), NewState#state{ buffer_after = NewBA }}
+ end;
+handle_request(State = #state{ unicode = U }, {delete, N}) when N > 0 ->
+ {_DelNum, DelCols, _, NewBA} = split(N, State#state.buffer_after, U),
+ BBCols = cols(State#state.buffer_before, U),
+ NewBACols = cols(NewBA, U),
+ {[encode(NewBA, U),
+ lists:duplicate(DelCols, $\s),
+ xnfix(State, BBCols + NewBACols + DelCols),
+ move_cursor(State,
+ BBCols + NewBACols + DelCols,
+ BBCols)],
+ State#state{ buffer_after = NewBA }};
+handle_request(State = #state{ unicode = U }, {delete, N}) when N < 0 ->
+ {_DelNum, DelCols, _, NewBB} = split(-N, State#state.buffer_before, U),
+ NewBBCols = cols(NewBB, U),
+ BACols = cols(State#state.buffer_after, U),
+ {[move_cursor(State, NewBBCols + DelCols, NewBBCols),
+ encode(State#state.buffer_after,U),
+ lists:duplicate(DelCols, $\s),
+ xnfix(State, NewBBCols + BACols + DelCols),
+ move_cursor(State, NewBBCols + BACols + DelCols, NewBBCols)],
+ State#state{ buffer_before = NewBB } };
+handle_request(State, {delete, 0}) ->
+ {"",State};
+handle_request(State = #state{ unicode = U }, {move, N}) when N < 0 ->
+ {_DelNum, DelCols, NewBA, NewBB} = split(-N, State#state.buffer_before, U),
+ NewBBCols = cols(NewBB, U),
+ Moves = move_cursor(State, NewBBCols + DelCols, NewBBCols),
+ {Moves, State#state{ buffer_before = NewBB,
+ buffer_after = NewBA ++ State#state.buffer_after} };
+handle_request(State = #state{ unicode = U }, {move, N}) when N > 0 ->
+ {_DelNum, DelCols, NewBB, NewBA} = split(N, State#state.buffer_after, U),
+ BBCols = cols(State#state.buffer_before, U),
+ {move_cursor(State, BBCols, BBCols + DelCols),
+ State#state{ buffer_after = NewBA,
+ buffer_before = NewBB ++ State#state.buffer_before} };
+handle_request(State, {move, 0}) ->
+ {"",State};
+handle_request(State = #state{ xn = OrigXn, unicode = U }, {insert, Chars}) ->
+ {InsertBuffer, NewState0} = insert_buf(State#state{ xn = false }, Chars),
+ NewState = NewState0#state{ xn = OrigXn },
+ BBCols = cols(NewState#state.buffer_before, U),
+ BACols = cols(NewState#state.buffer_after, U),
+ {[ encode(InsertBuffer, U),
+ encode(NewState#state.buffer_after, U),
+ xnfix(State, BBCols + BACols),
+ move_cursor(State, BBCols + BACols, BBCols) ],
+ NewState};
+handle_request(State, beep) ->
+ {<<7>>, State};
+handle_request(State, Req) ->
+ erlang:display({unhandled_request, Req}),
+ {"", State}.
+
+%% Split the buffer after N logical characters returning
+%% the number of real characters deleted and the column length
+%% of those characters
+split(N, Buff, Unicode) ->
+ ?dbg({?FUNCTION_NAME, N, Buff, Unicode}),
+ split(N, Buff, [], 0, 0, Unicode).
+split(0, Buff, Acc, Chars, Cols, _Unicode) ->
+ ?dbg({?FUNCTION_NAME, {Chars, Cols, Acc, Buff}}),
+ {Chars, Cols, Acc, Buff};
+split(N, _Buff, _Acc, _Chars, _Cols, _Unicode) when N < 0 ->
+ ok = N;
+split(_N, [], Acc, Chars, Cols, _Unicode) ->
+ {Chars, Cols, Acc, []};
+split(N, [Char | T], Acc, Cnt, Cols, Unicode) when is_integer(Char) ->
+ split(N - 1, T, [Char | Acc], Cnt + 1, Cols + npwcwidth(Char, Unicode), Unicode);
+split(N, [Chars | T], Acc, Cnt, Cols, Unicode) when is_list(Chars) ->
+ split(N - length(Chars), T, [Chars | Acc],
+ Cnt + length(Chars), Cols + cols(Chars, Unicode), Unicode);
+split(N, [SkipChars | T], Acc, Cnt, Cols, Unicode) when is_binary(SkipChars) ->
+ split(N, T, [SkipChars | Acc], Cnt, Cols, Unicode).
+
+logical([]) ->
+ 0;
+logical([Char | T]) when is_integer(Char) ->
+ 1 + logical(T);
+logical([Chars | T]) when is_list(Chars) ->
+ length(Chars) + logical(T);
+logical([SkipChars | T]) when is_binary(SkipChars) ->
+ logical(T).
+
+move_cursor(#state{ cols = W } = State, FromCol, ToCol) ->
+ ?dbg({?FUNCTION_NAME, FromCol, ToCol}),
+ [case (ToCol div W) - (FromCol div W) of
+ 0 -> "";
+ N when N < 0 ->
+ ?dbg({move, up, -N}),
+ move(up, State, -N);
+ N ->
+ ?dbg({move, down, N}),
+ move(down, State, N)
+ end,
+ case (ToCol rem W) - (FromCol rem W) of
+ 0 -> "";
+ N when N < 0 ->
+ ?dbg({down, left, -N}),
+ move(left, State, -N);
+ N ->
+ ?dbg({down, right, N}),
+ move(right, State, N)
+ end].
+
+move(up, #state{ up = Up }, N) ->
+ lists:duplicate(N, Up);
+move(down, #state{ down = Down }, N) ->
+ lists:duplicate(N, Down);
+move(left, #state{ left = Left }, N) ->
+ lists:duplicate(N, Left);
+move(right, #state{ right = Right }, N) ->
+ lists:duplicate(N, Right).
+
+cols([],_Unicode) ->
+ 0;
+cols([Char | T], Unicode) when is_integer(Char) ->
+ npwcwidth(Char, Unicode) + cols(T, Unicode);
+cols([Chars | T], Unicode) when is_list(Chars) ->
+ cols(Chars, Unicode) + cols(T, Unicode);
+cols([SkipSeq | T], Unicode) when is_binary(SkipSeq) ->
+ %% Any binary should be an ANSI escape sequence
+ %% so we skip that
+ cols(T, Unicode).
+
+update_geometry(State) ->
+ case tty_window_size(State#state.tty) of
+ {ok, {Cols, Rows}} when Cols > 0 ->
+ ?dbg({?FUNCTION_NAME, Cols}),
+ State#state{ cols = Cols, rows = Rows };
+ _Error ->
+ ?dbg({?FUNCTION_NAME, _Error}),
+ State
+ end.
+
+npwcwidth(Char) ->
+ npwcwidth(Char, true).
+npwcwidth(Char, true) ->
+ case wcwidth(Char) of
+ {error, not_printable} -> 0;
+ {error, enotsup} ->
+ case unicode_util:is_wide(Char) of
+ true -> 2;
+ false -> 1
+ end;
+ C -> C
+ end;
+npwcwidth(Char, false) ->
+ byte_size(char_to_latin1(Char)).
+
+
+%% Return the xn fix for the current cursor position.
+%% We use get_position to figure out if we need to calculate the current columns
+%% or not.
+%%
+%% We need to know the actual column because get_position will return the last
+%% column number when the cursor is:
+%% * in the last column
+%% * off screen
+%%
+%% and it is when the cursor is off screen that we should do the xnfix.
+xnfix(#state{ position = _, unicode = U } = State) ->
+ xnfix(State, cols(State#state.buffer_before, U)).
+%% Return the xn fix for CurrCols location.
+xnfix(#state{ xn = true, cols = Cols } = State, CurrCols)
+ when CurrCols =/= 0, CurrCols rem Cols == 0 ->
+ [<<"\s">>,move(left, State, 1)];
+xnfix(_, _CurrCols) ->
+ ?dbg({xnfix, _CurrCols}),
+ [].
+
+characters_to_output(Chars) ->
+ try unicode:characters_to_binary(Chars) of
+ Binary ->
+ Binary
+ catch error:badarg ->
+ unicode:characters_to_binary(
+ lists:map(
+ fun({ansi, Ansi}) ->
+ Ansi;
+ (Char) ->
+ Char
+ end, Chars)
+ )
+ end.
+characters_to_buffer(Chars) ->
+ lists:flatmap(
+ fun({ansi, _Ansi}) ->
+ "";
+ (Char) ->
+ [Char]
+ end, Chars).
+
+insert_buf(State, Binary) when is_binary(Binary) ->
+ insert_buf(State, Binary, [], []).
+insert_buf(State, Bin, LineAcc, Acc) ->
+ case string:next_grapheme(Bin) of
+ [] ->
+ NewBB = characters_to_buffer(LineAcc) ++ State#state.buffer_before,
+ NewState = State#state{ buffer_before = NewBB },
+ {[Acc, characters_to_output(lists:reverse(LineAcc)), xnfix(NewState)],
+ NewState};
+ [$\t | Rest] ->
+ insert_buf(State, Rest, [State#state.tab | LineAcc], Acc);
+ [$\e | Rest] ->
+ case re:run(Bin, State#state.ansi_regexp, [unicode]) of
+ {match, [{0, N}]} ->
+ <> = Bin,
+ case re:run(Bin, State#state.ansi_sgr, [unicode]) of
+ {match, [{0, N}]} ->
+ %% We include the graphics ansi sequences in the
+ %% buffer that we step over
+ insert_buf(State, AnsiRest, [Ansi | LineAcc], Acc);
+ _ ->
+ %% Any other ansi sequences are just printed and
+ %% then dropped as they "should" not effect rendering
+ insert_buf(State, AnsiRest, [{ansi, Ansi} | LineAcc], Acc)
+ end;
+ _ ->
+ insert_buf(State, Rest, [$\e | LineAcc], Acc)
+ end;
+ [NLCR | Rest] when NLCR =:= $\n; NLCR =:= $\r ->
+ Tail =
+ if NLCR =:= $\n ->
+ <<$\r,$\n>>;
+ true ->
+ <<$\r>>
+ end,
+ insert_buf(State#state{ buffer_before = [], buffer_after = [] }, Rest, [],
+ [Acc, [characters_to_output(lists:reverse(LineAcc)), Tail]]);
+ [Cluster | Rest] when is_list(Cluster) ->
+ insert_buf(State, Rest, [Cluster | LineAcc], Acc);
+ %% We have gotten a code point that may be part of the previous grapheme cluster.
+ [Char | Rest] when Char >= 128, LineAcc =:= [], State#state.buffer_before =/= [] ->
+ [PrevChar | BB] = State#state.buffer_before,
+ case string:next_grapheme([PrevChar | Bin]) of
+ [PrevChar | _] ->
+ %% It was not part of the previous cluster, so just insert
+ %% it as a normal character
+ insert_buf(State, Rest, [Char | LineAcc], Acc);
+ [Cluster | ClusterRest] ->
+ %% It was part of the previous grapheme cluster, so we output
+ %% it and insert it into the before_buffer
+ %% TODO: If an xnfix was done on PrevChar,
+ %% then we should rewrite the entire grapheme cluster.
+ {_, ToWrite} = lists:split(length(lists:flatten([PrevChar])), Cluster),
+ insert_buf(State#state{ buffer_before = [Cluster | BB] },
+ ClusterRest, LineAcc,
+ [Acc, unicode:characters_to_binary(ToWrite)])
+ end;
+ [Char | Rest] when Char >= 128 ->
+ insert_buf(State, Rest, [Char | LineAcc], Acc);
+ [Char | Rest] ->
+ case {isprint(Char), Char} of
+ {true,_} ->
+ insert_buf(State, Rest, [Char | LineAcc], Acc);
+ {false, 8#177} -> %% DEL
+ insert_buf(State, Rest, ["^?" | LineAcc], Acc);
+ {false, _} ->
+ insert_buf(State, Rest, ["^" ++ [Char bor 8#40] | LineAcc], Acc)
+ end
+ end.
+
+-spec to_latin1(erlang:binary()) -> erlang:iovec().
+to_latin1(Bin) ->
+ case is_usascii(Bin) of
+ true -> [Bin];
+ false -> lists:flatten([binary_to_latin1(Bin)])
+ end.
+
+is_usascii(<>) when Char < 128 ->
+ is_usascii(T);
+is_usascii(<<>>) ->
+ true;
+is_usascii(_) ->
+ false.
+
+binary_to_latin1(Buffer) ->
+ [char_to_latin1(CP) || CP <- unicode:characters_to_list(Buffer)].
+char_to_latin1(UnicodeChar) when UnicodeChar >= 512 ->
+ <<"\\x{",(integer_to_binary(UnicodeChar, 16))/binary,"}">>;
+char_to_latin1(UnicodeChar) when UnicodeChar >= 128 ->
+ <<"\\",(integer_to_binary(UnicodeChar, 8))/binary>>;
+char_to_latin1(UnicodeChar) ->
+ <>.
+
+encode(UnicodeChars, true) ->
+ unicode:characters_to_binary(UnicodeChars);
+encode(UnicodeChars, false) ->
+ to_latin1(unicode:characters_to_binary(UnicodeChars)).
+
+%% Using get_position adds about 10ms of latency
+%% get_position(#state{ position = false }) ->
+%% unknown;
+%% get_position(State) ->
+%% [] = write(State, State#state.position),
+%% get_position(State, <<>>).
+%% get_position(State, Acc) ->
+%% receive
+%% {select,TTY,Ref,ready_input}
+%% when TTY =:= State#state.tty,
+%% Ref =:= State#state.read ->
+%% {Bytes, <<>>} = read_input(State#state{ acc = Acc }),
+%% case re:run(Bytes, State#state.position_reply, [unicode]) of
+%% {match,[{Start,Length},Row,Col]} ->
+%% <> = Bytes,
+%% %% This should be put in State in order to not screw up the
+%% %% message order...
+%% [State#state.parent ! {{self(), State#state.tty}, {data, <>}}
+%% || Before =/= <<>>, After =/= <<>>],
+%% {binary_to_integer(binary:part(Bytes,Row)),
+%% binary_to_integer(binary:part(Bytes,Col))};
+%% nomatch ->
+%% get_position(State, Bytes)
+%% end
+%% after 1000 ->
+%% unknown
+%% end.
+
+-ifdef(debug).
+dbg(_) ->
+ ok.
+-endif
+
+%% Nif functions
+-spec isatty(stdin | stdout | stderr) -> boolean() | ebadf.
+isatty(_Fd) ->
+ erlang:nif_error(undef).
+tty_create() ->
+ erlang:nif_error(undef).
+tty_init(_TTY, _Fd, _Options) ->
+ erlang:nif_error(undef).
+tty_set(_TTY) ->
+ erlang:nif_error(undef).
+setlocale() ->
+ erlang:nif_error(undef).
+tty_select(_TTY, _SignalRef, _ReadRef) ->
+ erlang:nif_error(undef).
+write_nif(_TTY, _IOVec) ->
+ erlang:nif_error(undef).
+read_nif(_TTY, _Ref) ->
+ erlang:nif_error(undef).
+tty_window_size(_TTY) ->
+ erlang:nif_error(undef).
+isprint(_Char) ->
+ erlang:nif_error(undef).
+wcwidth(_Char) ->
+ erlang:nif_error(undef).
+sizeof_wchar() ->
+ erlang:nif_error(undef).
+wcswidth(_Char) ->
+ erlang:nif_error(undef).
+tgetent(Char) ->
+ tgetent_nif([Char,0]).
+tgetnum(Char) ->
+ tgetnum_nif([Char,0]).
+tgetflag(Char) ->
+ tgetflag_nif([Char,0]).
+tgetstr(Char) ->
+ tgetstr_nif([Char,0]).
+tgoto(Char, Arg) ->
+ tgoto_nif([Char,0], Arg).
+tgoto(Char, Arg1, Arg2) ->
+ tgoto_nif([Char,0], Arg1, Arg2).
+tgetent_nif(_Char) ->
+ erlang:nif_error(undef).
+tgetnum_nif(_Char) ->
+ erlang:nif_error(undef).
+tgetflag_nif(_Char) ->
+ erlang:nif_error(undef).
+tgetstr_nif(_Char) ->
+ erlang:nif_error(undef).
+tgoto_nif(_Ent, _Arg) ->
+ erlang:nif_error(undef).
+tgoto_nif(_Ent, _Arg1, _Arg2) ->
+ erlang:nif_error(undef).
+tty_read_signal(_TTY, _Ref) ->
+ erlang:nif_error(undef).
+
diff --git a/lib/kernel/src/user_drv.erl b/lib/kernel/src/user_drv.erl
index 4ae0498a8c9f..6c6a4b7024fc 100644
--- a/lib/kernel/src/user_drv.erl
+++ b/lib/kernel/src/user_drv.erl
@@ -19,13 +19,14 @@
%%
-module(user_drv).
-%% Basic interface to a port.
+%% Basic interface to stdin/stdout.
%%
%% This is responsible for a couple of things:
-%% - Dispatching I/O messages when erl is running as a terminal.
+%% - Dispatching I/O messages when erl is running
+%% * as a terminal.
%% The messages are listed in the type message/0.
%% - Any data received from the terminal is sent to the current group like this:
-%% `{DrvPid :: pid(), {data, UnicodeBinary :: binary()}}`
+%% `{DrvPid :: pid(), {data, UnicodeCharacters :: list()}}`
%% - It serves as the job control manager (i.e. what happens when you type ^G)
%% - Starts potential -remsh sessions to other nodes
%%
@@ -76,28 +77,24 @@
-include_lib("kernel/include/logger.hrl").
--define(OP_PUTC,0).
--define(OP_MOVE,1).
--define(OP_INSC,2).
--define(OP_DELC,3).
--define(OP_BEEP,4).
--define(OP_PUTC_SYNC,5).
-% Control op
--define(ERTS_TTYSL_DRV_CONTROL_MAGIC_NUMBER, 16#018b0900).
--define(CTRL_OP_GET_WINSIZE, (100 + ?ERTS_TTYSL_DRV_CONTROL_MAGIC_NUMBER)).
--define(CTRL_OP_GET_UNICODE_STATE, (101 + ?ERTS_TTYSL_DRV_CONTROL_MAGIC_NUMBER)).
--define(CTRL_OP_SET_UNICODE_STATE, (102 + ?ERTS_TTYSL_DRV_CONTROL_MAGIC_NUMBER)).
+-record(state, { tty, write, read, shell_started = true, user, current_group, groups, queue }).
--record(state, { port, user, current_group, groups, queue }).
-
-%% start()
+-type shell() :: {module(), atom(), arity()} | {node(), module(), atom(), arity()}.
+-type arguments() :: #{ initial_shell => shell() | {remote, unicode:charlist()} }.
+%% Default line editing shell
-spec start() -> pid().
-
-start() -> %Default line editing shell
- start(#{}).
+start() ->
+ case init:get_argument(remsh) of
+ {ok,[[Node]]} ->
+ start(#{ initial_shell => {remote, Node} });
+ E when E =:= error ; E =:= {ok,[[]]} ->
+ start(#{ })
+ end.
%% Backwards compatibility with pre OTP-26 for Elixir/LFE etc
+-spec start(['tty_sl -c -e'| shell()]) -> pid();
+ (arguments()) -> pid().
start(['tty_sl -c -e', Shell]) ->
start(#{ initial_shell => Shell });
start(Args) when is_map(Args) ->
@@ -109,93 +106,143 @@ start(Args) when is_map(Args) ->
callback_mode() -> state_functions.
+-spec init(arguments()) -> gen_statem:init_result(init).
init(Args) ->
process_flag(trap_exit, true),
- case catch open_port({spawn,"tty_sl -c -e"}, [eof]) of
- {'EXIT', _Reason} ->
- {stop, normal};
- Port ->
- {ok, init, {Args, #state{ user = start_user() } },
- {next_event, internal, Port}}
+ prim_tty:on_load(),
+ IsTTY = prim_tty:isatty(stdin) =:= true andalso prim_tty:isatty(stdout) =:= true,
+ if IsTTY ->
+ try prim_tty:init(#{}) of
+ TTYState ->
+ init_standard_error(TTYState, true),
+ {ok, init, {Args, #state{ user = start_user() } },
+ {next_event, internal, TTYState}}
+ catch error:enotsup ->
+ %% This is thrown by prim_tty:init when
+ %% it could not start the terminal,
+ %% probably because TERM=dumb was set.
+ {stop, normal}
+ end;
+ not IsTTY ->
+ {stop, normal}
end.
-init(internal, Port, {Args, State = #state{ user = User }}) ->
+%% Initialize standard_error
+init_standard_error(TTY, NewlineCarriageReturn) ->
+ Encoding = case prim_tty:unicode(TTY) of
+ true -> unicode;
+ false -> latin1
+ end,
+ ok = io:setopts(standard_error, [{encoding, Encoding},
+ {onlcr, NewlineCarriageReturn}]).
+
+init(internal, TTYState, {Args, State = #state{ user = User }}) ->
%% Cleanup ancestors so that observer looks nice
put('$ancestors',[User|get('$ancestors')]),
- %% Initialize standard_error
- Encoding =
- case get_unicode_state(Port) of
- true -> unicode;
- false -> latin1
- end,
- ok = io:setopts(standard_error, [{encoding, Encoding}, {onlcr,true}]),
-
- %% Initialize the starting shell
- {Curr,Shell} =
- case init:get_argument(remsh) of
- {ok,[[Node]]} ->
- ANode =
- if
- node() =:= nonode@nohost ->
- %% We try to connect to the node if the current node is not
- %% a distributed node yet. If this succeeds it means that we
- %% are running using "-sname undefined".
- _ = net_kernel:start([undefined, shortnames]),
- NodeName = append_hostname(Node, net_kernel:nodename()),
- case net_kernel:connect_node(NodeName) of
- true ->
- NodeName;
- _Else ->
- ?LOG_ERROR("Could not connect to ~p",[Node])
- end;
- true ->
- append_hostname(Node, node())
- end,
+ #{ read := ReadHandle, write := WriteHandle } = prim_tty:handles(TTYState),
+
+ NewState = State#state{ tty = TTYState,
+ read = ReadHandle, write = WriteHandle,
+ user = User, queue = {false, queue:new()},
+ groups = gr_add_cur(gr_new(), User, {})
+ },
- RShell = {ANode,shell,start,[]},
- {group:start(self(), RShell, rem_sh_opts(ANode)), RShell};
- E when E =:= error ; E =:= {ok,[[]]} ->
- LShell = maps:get(initial_shell, Args, {shell,start,[init]}),
- {group:start(self(), LShell), LShell}
- end,
+ case Args of
+ #{ initial_shell := {remote, Node} } ->
+ init_remote_shell(NewState, Node);
+ #{ initial_shell := InitialShell } ->
+ init_local_shell(NewState, InitialShell);
+ _ ->
+ init_local_shell(NewState, {shell,start,[init]})
+ end.
- Gr1 = gr_add_cur(gr_new(), User, {}),
- Gr = gr_add_cur(Gr1, Curr, Shell),
+init_remote_shell(State, Node) ->
- NewState = State#state{ port = Port, current_group = Curr, user = User,
- groups = Gr, queue = {false, queue:new()}
- },
+ StartedDist =
+ case net_kernel:get_state() of
+ #{ started := no } ->
+ {ok, _} = net_kernel:start([undefined, shortnames]),
+ true;
+ _ ->
+ false
+ end,
- %% Print some information.
- Slogan = case application:get_env(stdlib, shell_slogan,
- fun() -> erlang:system_info(system_version) end) of
- Fun when is_function(Fun, 0) ->
- Fun();
- SloganEnv ->
- SloganEnv
- end,
+ LocalNode =
+ case net_kernel:get_state() of
+ #{ name_type := dynamic } ->
+ net_kernel:nodename();
+ #{ name_type := static } ->
+ node()
+ end,
- {next_state, server, NewState,
- {next_event, info,
- {Curr, {put_chars, unicode, lists:flatten(io_lib:format("~ts\n", [Slogan]))}}}}.
+ RemoteNode =
+ case string:find(Node,"@") of
+ nomatch ->
+ list_to_atom(Node ++ string:find(atom_to_list(LocalNode),"@"));
+ _ ->
+ list_to_atom(Node)
+ end,
-append_hostname(Node, LocalNode) ->
- case string:find(Node,"@") of
- nomatch ->
- list_to_atom(Node ++ string:find(atom_to_list(LocalNode),"@"));
- _ ->
- list_to_atom(Node)
+ case net_kernel:connect_node(RemoteNode) of
+ true ->
+ %% We fetch the shell slogan from the remote node
+ Slogan =
+ case erpc:call(RemoteNode, application, get_env,
+ [stdlib, shell_slogan,
+ erpc:call(RemoteNode, erlang, system_info, [system_version])]) of
+ Fun when is_function(Fun, 0) ->
+ erpc:call(RemoteNode, Fun);
+ SloganEnv ->
+ SloganEnv
+ end,
+
+ RShellOpts = [{expand_fun,fun(B)-> rpc:call(RemoteNode,edlin_expand,expand,[B]) end}],
+
+ RShell = {RemoteNode,shell,start,[]},
+ Gr = gr_add_cur(State#state.groups,
+ group:start(self(), RShell, RShellOpts),
+ RShell),
+
+ init_shell(State#state{ groups = Gr }, [Slogan,$\n]);
+ false ->
+ ?LOG_ERROR("Could not connect to ~p, starting local shell",[RemoteNode]),
+ _ = [net_kernel:stop() || StartedDist],
+ init_local_shell(State, {shell, start, []})
end.
-rem_sh_opts(Node) ->
- [{expand_fun,fun(B)-> rpc:call(Node,edlin_expand,expand,[B]) end}].
+init_local_shell(State, InitialShell) ->
+
+ Slogan =
+ case application:get_env(
+ stdlib, shell_slogan,
+ fun() -> erlang:system_info(system_version) end) of
+ Fun when is_function(Fun, 0) ->
+ Fun();
+ SloganEnv ->
+ SloganEnv
+ end,
+
+ Gr = gr_add_cur(State#state.groups,
+ group:start(self(), InitialShell),
+ InitialShell),
+
+ init_shell(State#state{ groups = Gr }, [Slogan,$\n]).
+
+init_shell(State, Slogan) ->
+
+ init_standard_error(State#state.tty, State#state.shell_started),
+
+ {next_state, server, State#state{ current_group = gr_cur_pid(State#state.groups) },
+ {next_event, info,
+ {gr_cur_pid(State#state.groups),
+ {put_chars, unicode,
+ unicode:characters_to_binary(io_lib:format("~ts", [Slogan]))}}}}.
%% start_user()
%% Start a group leader process and register it as 'user', unless,
%% of course, a 'user' already exists.
-
start_user() ->
case whereis(user) of
undefined ->
@@ -206,8 +253,12 @@ start_user() ->
User
end.
-server(info, {Port,{data,Bs}}, State = #state{ port = Port }) ->
- UTF8Binary = list_to_binary(Bs),
+server(info, {ReadHandle,{data,UTF8Binary}}, State = #state{ read = ReadHandle })
+ when State#state.current_group =:= State#state.user ->
+ State#state.current_group !
+ {self(), {data, unicode:characters_to_list(UTF8Binary, utf8)}},
+ keep_state_and_data;
+server(info, {ReadHandle,{data,UTF8Binary}}, State = #state{ read = ReadHandle }) ->
case contains_ctrl_g_or_ctrl_c(UTF8Binary) of
ctrl_g -> {next_state, switch_loop, State, {next_event, internal, init}};
ctrl_c ->
@@ -221,30 +272,51 @@ server(info, {Port,{data,Bs}}, State = #state{ port = Port }) ->
{self(), {data, unicode:characters_to_list(UTF8Binary, utf8)}},
keep_state_and_data
end;
-server(info, {Port,eof}, State = #state{ port = Port }) ->
- State#state.current_group ! {self(),eof},
+server(info, {ReadHandle,eof}, State = #state{ read = ReadHandle }) ->
+ State#state.current_group ! {self(), eof},
keep_state_and_data;
-server(info, {Requester,tty_geometry}, #state{ port = Port }) ->
- Requester ! {self(),tty_geometry,get_tty_geometry(Port)},
- keep_state_and_data;
-server(info, {Requester,get_unicode_state}, #state{ port = Port }) ->
- Requester ! {self(),get_unicode_state,get_unicode_state(Port)},
+server(info,{ReadHandle,{signal,Signal}}, State = #state{ tty = TTYState, read = ReadHandle }) ->
+ {keep_state, State#state{ tty = prim_tty:handle_signal(TTYState, Signal) }};
+
+server(info, {Requester, tty_geometry}, #state{ tty = TTYState }) ->
+ case prim_tty:window_size(TTYState) of
+ {ok, Geometry} ->
+ Requester ! {self(), tty_geometry, Geometry},
+ ok;
+ Error ->
+ Requester ! {self(), tty_geometry, Error},
+ ok
+ end,
keep_state_and_data;
-server(info, {Requester,set_unicode_state,Bool}, #state{ port = Port }) ->
- Requester ! {self(),set_unicode_state,set_unicode_state(Port, Bool)},
+server(info, {Requester, get_unicode_state}, #state{ tty = TTYState }) ->
+ Requester ! {self(), get_unicode_state, prim_tty:unicode(TTYState) },
keep_state_and_data;
+server(info, {Requester, set_unicode_state, Bool}, #state{ tty = TTYState } = State) ->
+ OldUnicode = prim_tty:unicode(TTYState),
+ NewTTYState = prim_tty:unicode(TTYState, Bool),
+ ok = io:setopts(standard_error,[{encoding, if Bool -> unicode; true -> latin1 end}]),
+ Requester ! {self(), set_unicode_state, OldUnicode},
+ {keep_state, State#state{ tty = NewTTYState }};
server(info, Req, State = #state{ user = User, current_group = Curr })
when element(1,Req) =:= User orelse element(1,Req) =:= Curr,
tuple_size(Req) =:= 2 orelse tuple_size(Req) =:= 3 ->
%% We match {User|Curr,_}|{User|Curr,_,_}
- {keep_state, State#state{ queue = handle_req(Req, State#state.port, State#state.queue) }};
-server(info, {Port, ok}, State = #state{ port = Port, queue = {{Origin, Reply}, IOQ} }) ->
+ {NewTTYState, NewQueue} = handle_req(Req, State#state.tty, State#state.queue),
+ {keep_state, State#state{ tty = NewTTYState, queue = NewQueue }};
+server(info, {WriteRef, ok}, State = #state{ write = WriteRef,
+ queue = {{Origin, Reply}, IOQ} }) ->
%% We get this ok from the port, in io_request we store
%% info about where to send reply at head of queue
- Origin ! {reply,Reply},
- {keep_state, State#state{ queue = handle_req(next, Port, {false, IOQ}) }};
-server(info,{'EXIT',Port, _Reason}, #state{ port = Port }) ->
+ Origin ! {reply, Reply},
+ {NewTTYState, NewQueue} = handle_req(next, State#state.tty, {false, IOQ}),
+ {keep_state, State#state{ tty = NewTTYState, queue = NewQueue }};
+server(info,{Requester, {put_chars_sync, _, _, Reply}}, _State) ->
+ %% This is a sync request from an unknown or inactive group.
+ %% We need to ack the Req otherwise originating process will hang forever.
+ %% We discard the output to non visible shells
+ Requester ! {reply, Reply},
keep_state_and_data;
+
server(info,{'EXIT',User, shutdown}, #state{ user = User }) ->
keep_state_and_data;
server(info,{'EXIT',User, _Reason}, State = #state{ user = User }) ->
@@ -253,66 +325,39 @@ server(info,{'EXIT',User, _Reason}, State = #state{ user = User }) ->
groups = gr_set_num(State#state.groups, 1, NewUser, {})}};
server(info,{'EXIT', Group, Reason}, State) -> % shell and group leader exit
case gr_cur_pid(State#state.groups) of
- Group when Reason =/= die ,
- Reason =/= terminated -> % current shell exited
- if Reason =/= normal ->
- io_requests([{put_chars,unicode,"*** ERROR: "}], State#state.port);
- true -> % exit not caused by error
- io_requests([{put_chars,unicode,"*** "}], State#state.port)
- end,
- io_requests([{put_chars,unicode,"Shell process terminated! "}], State#state.port),
+ Group when Reason =/= die, Reason =/= terminated -> % current shell exited
+ Reqs = [if
+ Reason =/= normal ->
+ {put_chars,unicode,<<"*** ERROR: ">>};
+ true -> % exit not caused by error
+ {put_chars,unicode,<<"*** ">>}
+ end,
+ {put_chars,unicode,<<"Shell process terminated! ">>}],
Gr1 = gr_del_pid(State#state.groups, Group),
case gr_get_info(State#state.groups, Group) of
{Ix,{shell,start,Params}} -> % 3-tuple == local shell
- io_requests([{put_chars,unicode,"***\n"}], State#state.port),
+ NewTTyState = io_requests(Reqs ++ [{put_chars,unicode,<<"***\n">>}],
+ State#state.tty),
%% restart group leader and shell, same index
NewGroup = group:start(self(), {shell,start,Params}),
{ok,Gr2} = gr_set_cur(gr_set_num(Gr1, Ix, NewGroup,
{shell,start,Params}), Ix),
- {keep_state, State#state{ current_group = NewGroup, groups = Gr2 }};
+ {keep_state, State#state{ tty = NewTTyState,
+ current_group = NewGroup,
+ groups = Gr2 }};
_ -> % remote shell
- io_requests([{put_chars,unicode,"(^G to start new job) ***\n"}],
- State#state.port),
- {keep_state, State#state{ groups = Gr1 }}
+ NewTTYState = io_requests(
+ Reqs ++ [{put_chars,unicode,<<"(^G to start new job) ***\n">>}],
+ State#state.tty),
+ {keep_state, State#state{ tty = NewTTYState, groups = Gr1 }}
end;
_ -> % not current, just remove it
{keep_state, State#state{ groups = gr_del_pid(State#state.groups, Group) }}
end;
-server(info,{Requester, {put_chars_sync, _, _, Reply}}, _State) ->
- %% This is a sync request from an unknown or inactive group.
- %% We need to ack the Req otherwise originating process will hang forever.
- %% We discard the output to non visible shells
- Requester ! {reply, Reply},
- keep_state_and_data;
server(_, _, _) ->
%% Ignore unknown messages.
keep_state_and_data.
-handle_req(next,Port,{false,IOQ}=IOQueue) ->
- case queue:out(IOQ) of
- {empty,_} ->
- IOQueue;
- {{value,{Origin,Req}},ExecQ} ->
- case io_request(Req,Port) of
- ok ->
- handle_req(next,Port,{false,ExecQ});
- Reply ->
- {{Origin,Reply},ExecQ}
- end
- end;
-handle_req(Msg,Port,{false,IOQ}=IOQueue) ->
- empty = queue:peek(IOQ),
- {Origin,Req} = Msg,
- case io_request(Req, Port) of
- ok ->
- IOQueue;
- Reply ->
- {{Origin,Reply}, IOQ}
- end;
-handle_req(Msg,_Port,{Resp, IOQ}) ->
- %% All requests are queued when we have outstanding sync put_chars
- {Resp, queue:in(Msg,IOQ)}.
-
contains_ctrl_g_or_ctrl_c(<<$\^G,_/binary>>) ->
ctrl_g;
contains_ctrl_g_or_ctrl_c(<<$\^C,_/binary>>) ->
@@ -339,96 +384,95 @@ switch_loop(internal, init, State) ->
end
end,
NewGroup = group:start(self(), {shell,start,[]}),
- io_request({put_chars,unicode,"\n"}, State#state.port),
+ NewTTYState = io_requests([{put_chars,unicode,<<"\n">>}], State#state.tty),
{next_state, server,
- State#state{ groups = gr_add_cur(Gr1, NewGroup, {shell,start,[]})}};
+ State#state{ tty = NewTTYState,
+ groups = gr_add_cur(Gr1, NewGroup, {shell,start,[]})}};
jcl ->
- io_request({put_chars,unicode,"\nUser switch command\n"}, State#state.port),
+ NewTTYState =
+ io_requests([{put_chars,unicode,<<"\nUser switch command\n">>}],
+ State#state.tty),
%% init edlin used by switch command and have it copy the
%% text buffer from current group process
edlin:init(gr_cur_pid(State#state.groups)),
- {keep_state_and_data,
+ {keep_state, State#state{ tty = NewTTYState },
{next_event, internal, line}}
end;
switch_loop(internal, line, State) ->
{more_chars, Cont, Rs} = edlin:start(" --> "),
- io_requests(Rs, State#state.port),
- {keep_state, {Cont, State}};
+ {keep_state, {Cont, State#state{ tty = io_requests(Rs, State#state.tty) }}};
switch_loop(internal, {line, Line}, State) ->
case erl_scan:string(Line) of
{ok, Tokens, _} ->
- case switch_cmd(Tokens, State#state.port, State#state.groups) of
+ case switch_cmd(Tokens, State#state.groups) of
{ok, Groups} ->
{next_state, server,
State#state{ current_group = gr_cur_pid(Groups), groups = Groups } };
- retry ->
- {keep_state_and_data,
+ {retry, Requests} ->
+ {keep_state, State#state{ tty = io_requests(Requests, State#state.tty) },
{next_event, internal, line}};
- {retry, Groups} ->
- {keep_state, State#state{ current_group = gr_cur_pid(Groups),
- groups = Groups },
+ {retry, Requests, Groups} ->
+ {keep_state, State#state{
+ tty = io_requests(Requests, State#state.tty),
+ current_group = gr_cur_pid(Groups),
+ groups = Groups },
{next_event, internal, line}}
end;
{error, _, _} ->
- io_request({put_chars,unicode,"Illegal input\n"}, State#state.port),
- {keep_state_and_data,
+ NewTTYState =
+ io_requests([{put_chars,unicode,<<"Illegal input\n">>}], State#state.tty),
+ {keep_state, State#state{ tty = NewTTYState },
{next_event, internal, line}}
end;
-switch_loop(info,{Port,{data,Cs}}, {Cont, State}) ->
- case edlin:edit_line(Cs, Cont) of
+switch_loop(info,{ReadHandle,{data,Cs}}, {Cont, #state{ read = ReadHandle } = State}) ->
+ case edlin:edit_line(unicode:characters_to_list(Cs), Cont) of
{done,Line,_Rest, Rs} ->
- io_requests(Rs, State#state.port),
- {keep_state, State, {next_event, internal, {line, Line}}};
+ {keep_state, State#state{ tty = io_requests(Rs, State#state.tty) },
+ {next_event, internal, {line, Line}}};
{undefined,_Char,MoreCs,NewCont,Rs} ->
- io_requests(Rs, State#state.port),
- io_request(beep, State#state.port),
- {keep_state, {NewCont, State},
- {next_event, info, {Port,{data,MoreCs}}}};
+ {keep_state,
+ {NewCont, State#state{ tty = io_requests(Rs ++ [beep], State#state.tty)}},
+ {next_event, info, {ReadHandle,{data,MoreCs}}}};
{more_chars,NewCont,Rs} ->
- io_requests(Rs, State#state.port),
- {keep_state, {NewCont, State}};
+ {keep_state,
+ {NewCont, State#state{ tty = io_requests(Rs, State#state.tty)}}};
{blink,NewCont,Rs} ->
- io_requests(Rs, State#state.port),
- {keep_state, {NewCont, State}, 1000}
+ {keep_state,
+ {NewCont, State#state{ tty = io_requests(Rs, State#state.tty)}},
+ 1000}
end;
-switch_loop(timeout, _, State) ->
+switch_loop(timeout, _, {_Cont, State}) ->
{keep_state_and_data,
- {next_state, info,{State#state.port,{data,[]}}}};
+ {next_event, info, {State#state.read,{data,[]}}}};
switch_loop(info, _Unknown, _State) ->
{keep_state_and_data, postpone}.
-switch_cmd([{atom,_,Key},{Type,_,Value}], Port, Gr)
+switch_cmd([{atom,_,Key},{Type,_,Value}], Gr)
when Type =:= atom; Type =:= integer ->
- switch_cmd({Key, Value}, Port, Gr);
-switch_cmd([{atom,_,Key},{atom,_,V1},{atom,_,V2}], Port, Gr) ->
- switch_cmd({Key, V1, V2}, Port, Gr);
-switch_cmd([{atom,_,Key}], Port, Gr) ->
- switch_cmd(Key, Port, Gr);
-switch_cmd([{'?',_}], Port, Gr) ->
- switch_cmd(h, Port, Gr);
-
-switch_cmd(Cmd, Port, Gr) when Cmd =:= c; Cmd =:= i; Cmd =:= k ->
- Pid = gr_cur_pid(Gr),
- CurrIndex =
- case gr_get_info(Gr, Pid) of
- undefined -> undefined;
- {Ix, _} -> Ix
- end,
- switch_cmd({Cmd, CurrIndex}, Port, Gr);
-switch_cmd({c, I}, Port, Gr0) ->
+ switch_cmd({Key, Value}, Gr);
+switch_cmd([{atom,_,Key},{atom,_,V1},{atom,_,V2}], Gr) ->
+ switch_cmd({Key, V1, V2}, Gr);
+switch_cmd([{atom,_,Key}], Gr) ->
+ switch_cmd(Key, Gr);
+switch_cmd([{'?',_}], Gr) ->
+ switch_cmd(h, Gr);
+
+switch_cmd(Cmd, Gr) when Cmd =:= c; Cmd =:= i; Cmd =:= k ->
+ switch_cmd({Cmd, gr_cur_index(Gr)}, Gr);
+switch_cmd({c, I}, Gr0) ->
case gr_set_cur(Gr0, I) of
{ok,Gr} -> {ok, Gr};
- undefined -> unknown_group(Port)
+ undefined -> unknown_group()
end;
-switch_cmd({i, I}, Port, Gr) ->
+switch_cmd({i, I}, Gr) ->
case gr_get_num(Gr, I) of
{pid,Pid} ->
exit(Pid, interrupt),
- retry;
+ {retry, []};
undefined ->
- unknown_group(Port)
+ unknown_group()
end;
-switch_cmd({k, I}, Port, Gr) ->
+switch_cmd({k, I}, Gr) ->
case gr_get_num(Gr, I) of
{pid,Pid} ->
exit(Pid, die),
@@ -437,163 +481,132 @@ switch_cmd({k, I}, Port, Gr) ->
retry;
_ ->
receive {'EXIT',Pid,_} ->
- {retry,gr_del_pid(Gr, Pid)}
+ {retry,[],gr_del_pid(Gr, Pid)}
after 1000 ->
- {retry,Gr}
+ {retry,[],Gr}
end
end;
undefined ->
- unknown_group(Port)
+ unknown_group()
end;
-switch_cmd(j, Port, Gr) ->
- io_requests(gr_list(Gr), Port),
- retry;
-switch_cmd({s, Shell}, _Port, Gr0) when is_atom(Shell) ->
+switch_cmd(j, Gr) ->
+ {retry, gr_list(Gr)};
+switch_cmd({s, Shell}, Gr0) when is_atom(Shell) ->
Pid = group:start(self(), {Shell,start,[]}),
Gr = gr_add_cur(Gr0, Pid, {Shell,start,[]}),
- {retry, Gr};
-switch_cmd(s, Port, Gr) ->
- switch_cmd({s, shell}, Port, Gr);
-switch_cmd(r, Port, Gr0) ->
+ {retry, [], Gr};
+switch_cmd(s, Gr) ->
+ switch_cmd({s, shell}, Gr);
+switch_cmd(r, Gr0) ->
case is_alive() of
true ->
Node = pool:get_node(),
Pid = group:start(self(), {Node,shell,start,[]}),
Gr = gr_add_cur(Gr0, Pid, {Node,shell,start,[]}),
- {retry, Gr};
+ {retry, [], Gr};
false ->
- io_request({put_chars,unicode,"Node is not alive\n"}, Port),
- retry
+ {retry, [{put_chars,unicode,<<"Node is not alive\n">>}]}
end;
-switch_cmd({r, Node}, Port, Gr) when is_atom(Node)->
- switch_cmd({r, Node, shell}, Port, Gr);
-switch_cmd({r,Node,Shell}, Port, Gr0) when is_atom(Node),
- is_atom(Shell) ->
+switch_cmd({r, Node}, Gr) when is_atom(Node)->
+ switch_cmd({r, Node, shell}, Gr);
+switch_cmd({r,Node,Shell}, Gr0) when is_atom(Node), is_atom(Shell) ->
case is_alive() of
true ->
Pid = group:start(self(), {Node,Shell,start,[]}),
Gr = gr_add_cur(Gr0, Pid, {Node,Shell,start,[]}),
- {retry, Gr};
+ {retry, [], Gr};
false ->
- io_request({put_chars,unicode,"Node is not alive\n"}, Port),
- retry
+ {retry, [{put_chars,unicode,"Node is not alive\n"}]}
end;
-switch_cmd(q, Port, _Gr) ->
+switch_cmd(q, _Gr) ->
case erlang:system_info(break_ignored) of
true -> % noop
- io_request({put_chars,unicode,"Unknown command\n"}, Port),
- retry;
+ {retry, [{put_chars,unicode,<<"Unknown command\n">>}]};
false ->
halt()
end;
-switch_cmd(h, Port, _Gr) ->
- list_commands(Port),
- retry;
-switch_cmd([], _Port, _Gr) ->
- retry;
-switch_cmd(_Ts, Port, _Gr) ->
- io_request({put_chars,unicode,"Unknown command\n"}, Port),
- retry.
+switch_cmd(h, _Gr) ->
+ {retry, list_commands()};
+switch_cmd([], _Gr) ->
+ {retry,[]};
+switch_cmd(_Ts, _Gr) ->
+ {retry, [{put_chars,unicode,<<"Unknown command\n">>}]}.
-unknown_group(Port) ->
- io_request({put_chars,unicode,"Unknown job\n"}, Port),
- retry.
+unknown_group() ->
+ {retry,[{put_chars,unicode,<<"Unknown job\n">>}]}.
-
-list_commands(Port) ->
+list_commands() ->
QuitReq = case erlang:system_info(break_ignored) of
- true ->
+ true ->
[];
false ->
- [{put_chars, unicode," q - quit erlang\n"}]
+ [{put_chars, unicode,<<" q - quit erlang\n">>}]
end,
- io_requests([{put_chars, unicode," c [nn] - connect to job\n"},
- {put_chars, unicode," i [nn] - interrupt job\n"},
- {put_chars, unicode," k [nn] - kill job\n"},
- {put_chars, unicode," j - list all jobs\n"},
- {put_chars, unicode," s [shell] - start local shell\n"},
- {put_chars, unicode," r [node [shell]] - start remote shell\n"}] ++
- QuitReq ++
- [{put_chars, unicode," ? | h - this message\n"}],
- Port).
-
-% Let driver report window geometry,
-% definitely outside of the common interface
-get_tty_geometry(Port) ->
- case (catch port_control(Port,?CTRL_OP_GET_WINSIZE,[])) of
- List when length(List) =:= 8 ->
- <> = list_to_binary(List),
- {W,H};
- _ ->
- error
- end.
-get_unicode_state(Port) ->
- case (catch port_control(Port,?CTRL_OP_GET_UNICODE_STATE,[])) of
- [Int] when Int > 0 ->
- true;
- [Int] when Int =:= 0 ->
- false;
- _ ->
- error
- end.
-
-set_unicode_state(Port, Bool) ->
- Data = case Bool of
- true -> [1];
- false -> [0]
- end,
- case (catch port_control(Port,?CTRL_OP_SET_UNICODE_STATE,Data)) of
- [Int] when Int > 0 ->
- true;
- [Int] when Int =:= 0 ->
- false;
- _ ->
- error
- end.
-
-%% io_request(Request, InPort, OutPort)
-%% io_requests(Requests, InPort, OutPort)
-%% Note: InPort is unused.
-io_request({requests,Rs}, Port) ->
- io_requests(Rs, Port);
-io_request(Request, Port) ->
- case io_command(Request) of
- {Data, Reply} ->
- true = port_command(Port, Data),
- Reply;
- unhandled ->
- ok
- end.
-
-io_requests([R|Rs], Port) ->
- io_request(R, Port),
- io_requests(Rs, Port);
-io_requests([], _Port) ->
- ok.
-
-put_int16(N, Tail) ->
- [(N bsr 8)band 255,N band 255|Tail].
-
-%% When a put_chars_sync command is used, user_drv guarantees that
-%% the bytes have been put in the buffer of the port before an acknowledgement
-%% is sent back to the process sending the request. This command was added in
-%% OTP 18 to make sure that data sent from io:format is actually printed
-%% to the console before the vm stops when calling erlang:halt(integer()).
--dialyzer({no_improper_lists, io_command/1}).
-io_command({put_chars_sync, unicode, Cs, Reply}) ->
- {[?OP_PUTC_SYNC|unicode:characters_to_binary(Cs, utf8)], Reply};
-io_command({put_chars, unicode, Cs}) ->
- {[?OP_PUTC|unicode:characters_to_binary(Cs, utf8)], ok};
-io_command({move_rel, N}) ->
- {[?OP_MOVE|put_int16(N, [])], ok};
-io_command({insert_chars, unicode, Cs}) ->
- {[?OP_INSC|unicode:characters_to_binary(Cs, utf8)], ok};
-io_command({delete_chars, N}) ->
- {[?OP_DELC|put_int16(N, [])], ok};
-io_command(beep) ->
- {[?OP_BEEP], ok};
-io_command(_) ->
- unhandled.
+ [{put_chars, unicode,<<" c [nn] - connect to job\n">>},
+ {put_chars, unicode,<<" i [nn] - interrupt job\n">>},
+ {put_chars, unicode,<<" k [nn] - kill job\n">>},
+ {put_chars, unicode,<<" j - list all jobs\n">>},
+ {put_chars, unicode,<<" s [shell] - start local shell\n">>},
+ {put_chars, unicode,<<" r [node [shell]] - start remote shell\n">>}] ++
+ QuitReq ++
+ [{put_chars, unicode,<<" ? | h - this message\n">>}].
+
+-spec io_request(request(), prim_tty:state()) -> {noreply | term(), prim_tty:state()}.
+io_request({requests,Rs}, TTY) ->
+ {noreply, io_requests(Rs, TTY)};
+io_request({put_chars, unicode, Chars}, TTY) ->
+ write(prim_tty:handle_request(TTY, {putc, unicode:characters_to_binary(Chars)}));
+io_request({put_chars_sync, unicode, Chars, Reply}, TTY) ->
+ {Output, NewTTY} = prim_tty:handle_request(TTY, {putc, unicode:characters_to_binary(Chars)}),
+ ok = prim_tty:write(NewTTY, Output, self()),
+ {Reply, NewTTY};
+io_request({move_rel, N}, TTY) ->
+ write(prim_tty:handle_request(TTY, {move, N}));
+io_request({insert_chars, unicode, Chars}, TTY) ->
+ write(prim_tty:handle_request(TTY, {insert, unicode:characters_to_binary(Chars)}));
+io_request({delete_chars, N}, TTY) ->
+ write(prim_tty:handle_request(TTY, {delete, N}));
+io_request(beep, TTY) ->
+ write(prim_tty:handle_request(TTY, beep)).
+
+write({Output, TTY}) ->
+ ok = prim_tty:write(TTY, Output),
+ {noreply, TTY}.
+
+io_requests([{insert_chars, unicode, C1},{insert_chars, unicode, C2}|Rs], TTY) ->
+ io_requests([{insert_chars, unicode, [C1,C2]}|Rs], TTY);
+io_requests([{put_chars, unicode, C1},{put_chars, unicode, C2}|Rs], TTY) ->
+ io_requests([{put_chars, unicode, [C1,C2]}|Rs], TTY);
+io_requests([R|Rs], TTY) ->
+ {noreply, NewTTY} = io_request(R, TTY),
+ io_requests(Rs, NewTTY);
+io_requests([], TTY) ->
+ TTY.
+
+handle_req(next,TTYState,{false,IOQ}=IOQueue) ->
+ case queue:out(IOQ) of
+ {empty,_} ->
+ {TTYState, IOQueue};
+ {{value,{Origin,Req}},ExecQ} ->
+ case io_request(Req,TTYState) of
+ {noreply, NewTTYState} ->
+ handle_req(next,NewTTYState,{false,ExecQ});
+ {Reply, NewTTYState} ->
+ {NewTTYState, {{Origin,Reply},ExecQ}}
+ end
+ end;
+handle_req(Msg,TTYState,{false,IOQ}=IOQueue) ->
+ empty = queue:peek(IOQ),
+ {Origin, Req} = Msg,
+ case io_request(Req, TTYState) of
+ {noreply, NewTTYState} ->
+ {NewTTYState, IOQueue};
+ {Reply, NewTTYState} ->
+ {NewTTYState, {{Origin,Reply}, IOQ}}
+ end;
+handle_req(Msg,TTYState,{Resp, IOQ}) ->
+ %% All requests are queued when we have outstanding sync put_chars
+ {TTYState, {Resp, queue:in(Msg,IOQ)}}.
%% gr_new()
%% gr_get_num(Group, Index)
@@ -663,5 +676,6 @@ gr_list(#gr{ current = Current, groups = Groups}) ->
(#group{ index = I, shell = S }) ->
Marker = ["*" || Current =:= I],
[{put_chars, unicode,
- lists:flatten(io_lib:format("~4w~.1ts ~w\n", [I,Marker,S]))}]
+ unicode:characters_to_binary(
+ io_lib:format("~4w~.1ts ~w\n", [I,Marker,S]))}]
end, Groups).
diff --git a/lib/sasl/test/systools_SUITE.erl b/lib/sasl/test/systools_SUITE.erl
index 801660ddacf4..6065c099fa9e 100644
--- a/lib/sasl/test/systools_SUITE.erl
+++ b/lib/sasl/test/systools_SUITE.erl
@@ -1109,9 +1109,9 @@ erts_tar(Config) ->
{win32, _} ->
{["beam.smp.pdb","erl.exe",
"erl.pdb","erl_log.exe","erlexec.dll","erlsrv.exe","heart.exe",
- "start_erl.exe","werl.exe","beam.smp.dll",
+ "start_erl.exe","beam.smp.dll",
"epmd.exe","erl.ini","erl_call.exe",
- "erlexec.pdb","escript.exe","inet_gethost.exe","werl.pdb"],
+ "erlexec.pdb","escript.exe","inet_gethost.exe"],
["dialyzer.exe","erlc.exe","yielding_c_fun.exe","ct_run.exe","typer.exe"]}
end,
diff --git a/lib/stdlib/src/stdlib.app.src b/lib/stdlib/src/stdlib.app.src
index 90c19b2cade4..653e92812784 100644
--- a/lib/stdlib/src/stdlib.app.src
+++ b/lib/stdlib/src/stdlib.app.src
@@ -112,6 +112,6 @@
dets]},
{applications, [kernel]},
{env, []},
- {runtime_dependencies, ["sasl-3.0","kernel-8.4","erts-@OTP-17934@","crypto-4.5",
+ {runtime_dependencies, ["sasl-3.0","kernel-@OTP-17932@","erts-@OTP-17934@","crypto-4.5",
"compiler-5.0"]}
]}.