This repository has been archived on 2023-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
yap-6.3/console/LGPL/pl-ntconsole.c

460 lines
9.7 KiB
C

/* $Id$
Part of SWI-Prolog
Author: Jan Wielemaker and Matt Lilley
E-mail: J.Wielemaker@cs.vu.nl
WWW: http://www.swi-prolog.org
Copyright (C): 2010, VU University, Amsterdam
Copyright (C): 2009, SCIENTIFIC SOFTWARE AND SYSTEMS LIMITED
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define WINDOWS_LEAN_AND_MEAN 1
#if (_MSC_VER >= 1300)
#include <winsock2.h> /* Needed on VC8 */
#include <windows.h>
#else
#include <windows.h> /* Needed for MSVC 5&6 */
#include <winsock2.h>
#endif
#include "pl-incl.h"
#define ANSI_MAGIC (0x734ab9de)
#define ANSI_BUFFER_SIZE (256)
#define ANSI_MAX_ARGC (10)
typedef enum
{ CMD_INITIAL = 0,
CMD_ESC,
CMD_ANSI
} astate;
typedef struct
{ int magic;
HANDLE hConsole;
IOSTREAM *pStream;
void *saved_handle;
wchar_t buffer[ANSI_BUFFER_SIZE];
size_t buffered;
int argv[ANSI_MAX_ARGC];
int argc;
int argstat;
astate cmdstat; /* State for sequence processing */
WORD def_attr; /* Default attributes */
} ansi_stream;
static IOFUNCTIONS con_functions;
static IOFUNCTIONS *saved_functions;
static void
Message(const char *fm, ...)
{ char buf[1024];
va_list(args);
return;
va_start(args, fm);
vsprintf(buf, fm, args);
MessageBox(NULL, buf, "SWI-Prolog", MB_OK|MB_TASKMODAL);
va_end(args);
}
static int
flush_ansi(ansi_stream *as)
{ size_t written = 0;
while ( written < as->buffered )
{ BOOL rc;
DWORD done;
rc = WriteConsoleW(as->hConsole,
&as->buffer[written],
(DWORD)(as->buffered-written),
&done,
NULL);
if ( rc )
{ written += done;
} else
{ as->buffered = 0;
return -1;
}
}
as->buffered = 0;
return 0;
}
static int
send_ansi(ansi_stream *as, int chr)
{ as->buffer[as->buffered++] = chr;
if ( as->buffered == ANSI_BUFFER_SIZE ||
(as->pStream->flags & SIO_NBUF) ||
(chr == '\n' && (as->pStream->flags & SIO_LBUF)) )
return flush_ansi(as);
return 0;
}
#define FG_MASK (FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN)
#define BG_MASK (BACKGROUND_RED|BACKGROUND_BLUE|BACKGROUND_GREEN)
static void
set_ansi_attributes(ansi_stream *as)
{ CONSOLE_SCREEN_BUFFER_INFO info;
if ( GetConsoleScreenBufferInfo(as->hConsole, &info) )
{ int i;
WORD attr = info.wAttributes;
for(i=0; i < as->argc; i++)
{ switch( as->argv[i] )
{ case 0:
attr = as->def_attr;
break;
case 1:
attr |= FOREGROUND_INTENSITY;
break;
case 22:
attr &= ~FOREGROUND_INTENSITY;
break;
default:
if ( as->argv[i] >= 30 && as->argv[i] <= 39 )
{ int fg = as->argv[i] - 30;
attr &= ~FG_MASK;
if ( fg == 9 ) /* default */
{ attr |= (as->def_attr & FG_MASK);
} else
{ if ( fg % 2 == 1 )
attr |= FOREGROUND_RED;
if ( fg >= 4 )
attr |= FOREGROUND_BLUE;
if ( (fg == 2) || (fg == 3) || (fg == 6) || (fg == 7) )
attr |= FOREGROUND_GREEN;
}
} else if ( as->argv[i] >= 40 && as->argv[i] <= 49 )
{ int bg = as->argv[i] - 40;
attr &= ~BG_MASK;
if ( bg == 9 ) /* default */
{ attr |= (as->def_attr & BG_MASK);
} else
{ if ( bg % 2 == 1 )
attr |= BACKGROUND_RED;
if ( bg >= 4 )
attr |= BACKGROUND_BLUE;
if ( (bg == 2) || (bg == 3) || (bg == 6) || (bg == 7) )
attr |= BACKGROUND_GREEN;
}
}
}
}
if ( attr != info.wAttributes )
{ flush_ansi(as);
SetConsoleTextAttribute(as->hConsole, attr);
}
}
}
static void
rlc_need_arg(ansi_stream *as, int arg, int def)
{ if ( as->argc < arg )
{ as->argv[arg-1] = def;
as->argc = arg;
}
}
static int
put_ansi(ansi_stream *as, int chr)
{ switch(as->cmdstat)
{ case CMD_INITIAL:
switch(chr)
{
#if 0
case '\b':
CMD(rlc_caret_backward(b, 1));
break;
case Control('G'):
MessageBeep(MB_ICONEXCLAMATION);
break;
case '\r':
CMD(rlc_cariage_return(b));
break;
case '\n':
CMD(rlc_caret_down(b, 1));
break;
case '\t':
CMD(rlc_tab(b));
break;
#endif
case 27: /* ESC */
as->cmdstat = CMD_ESC;
break;
default:
return send_ansi(as, chr);
}
break;
case CMD_ESC:
switch(chr)
{ case '[':
as->cmdstat = CMD_ANSI;
as->argc = 0;
as->argstat = 0; /* no arg */
break;
default:
as->cmdstat = CMD_INITIAL;
break;
}
break;
case CMD_ANSI: /* ESC [ */
if ( chr >= '0' && chr <= '9' )
{ if ( !as->argstat )
{ as->argv[as->argc] = (chr - '0');
as->argstat = 1; /* positive */
} else
{ as->argv[as->argc] = as->argv[as->argc] * 10 + (chr - '0');
}
break;
}
if ( !as->argstat && chr == '-' )
{ as->argstat = -1; /* negative */
break;
}
if ( as->argstat )
{ as->argv[as->argc] *= as->argstat;
if ( as->argc < (ANSI_MAX_ARGC-1) )
as->argc++; /* silently discard more of them */
as->argstat = 0;
}
switch(chr)
{ case ';':
return 0; /* wait for more args */
#if 0
case 'H':
case 'f':
rlc_need_arg(as, 1, 0);
rlc_need_arg(as, 2, 0);
CMD(rlc_set_caret(as, as->argv[0], as->argv[1]));
break;
case 'A':
rlc_need_arg(as, 1, 1);
CMD(rlc_caret_up(as, as->argv[0]));
break;
case 'B':
rlc_need_arg(as, 1, 1);
CMD(rlc_caret_down(as, as->argv[0]));
break;
case 'C':
rlc_need_arg(as, 1, 1);
CMD(rlc_caret_forward(as, as->argv[0]));
break;
case 'D':
rlc_need_arg(as, 1, 1);
CMD(rlc_caret_backward(as, as->argv[0]));
break;
case 's':
CMD(rlc_save_caret_position(as));
break;
case 'u':
CMD(rlc_restore_caret_position(as));
break;
case 'J':
if ( as->argv[0] == 2 )
CMD(rlc_erase_display(as));
break;
case 'K':
CMD(rlc_erase_line(as));
break;
#endif
case 'm':
rlc_need_arg(as, 1, 0);
set_ansi_attributes(as);
}
as->cmdstat = CMD_INITIAL;
}
return 0;
}
static ssize_t
write_ansi(void *handle, char *buffer, size_t size)
{ ansi_stream *as = handle;
size_t n = size/sizeof(wchar_t);
const wchar_t *s = (const wchar_t*)buffer;
const wchar_t *e = &s[n];
Message("Writing %d characters", n);
for( ; s<e; s++)
{ if ( put_ansi(as, *s) != 0 )
return -1; /* error */
}
Message("Wrote %d characters", n);
return n * sizeof(wchar_t);
}
static int
close_ansi(void *handle)
{ ansi_stream *as = handle;
if ( as->magic == ANSI_MAGIC )
{ as->pStream->functions = saved_functions;
as->pStream->handle = as->saved_handle;
PL_free(as);
return 0;
}
return -1;
}
static int
control_ansi(void *handle, int op, void *data)
{ ansi_stream *as = handle;
switch( op )
{ case SIO_FLUSHOUTPUT:
return flush_ansi(as);
case SIO_SETENCODING:
return -1; /* We cannot change the encoding! */
case SIO_LASTERROR:
return 0; /* TBD */
default:
return -1;
}
}
/*******************************
* USER WIN32 CONSOLE *
*******************************/
static ssize_t
Sread_win32_console(void *handle, char *buffer, size_t size)
{ GET_LD
ansi_stream *as = handle;
BOOL rc;
DWORD done;
DWORD mode;
int isRaw = FALSE;
if ( Suser_input &&
Suser_input->handle == handle &&
PL_ttymode(Suser_input) == PL_RAWTTY )
{ if ( GetConsoleMode(as->hConsole, &mode) &&
SetConsoleMode(as->hConsole,
mode & ~(ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT)) )
isRaw = TRUE;
}
if ( !PL_wait_for_console_input(as->hConsole) )
goto error;
rc = ReadConsoleW(as->hConsole,
buffer,
(DWORD)(size / sizeof(wchar_t)),
&done,
NULL);
if ( rc )
{ if ( isRaw )
SetConsoleMode(as->hConsole, mode);
return done * sizeof(wchar_t);
}
error:
if ( isRaw )
SetConsoleMode(as->hConsole, mode);
return -1;
}
static int
wrap_console(HANDLE h, IOSTREAM *s, IOFUNCTIONS *funcs)
{ ansi_stream *as;
as = PL_malloc(sizeof(*as));
memset(as, 0, sizeof(*as));
as->hConsole = h;
as->pStream = s;
as->saved_handle = s->handle;
s->handle = as;
s->encoding = ENC_WCHAR;
s->functions = funcs;
return TRUE;
}
static void
init_output(void *handle, CONSOLE_SCREEN_BUFFER_INFO *info)
{ ansi_stream *as = handle;
as->def_attr = info->wAttributes;
}
int
PL_w32_wrap_ansi_console(void)
{ HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
HANDLE hError = GetStdHandle(STD_ERROR_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO info;
if ( hIn == INVALID_HANDLE_VALUE ||
hOut == INVALID_HANDLE_VALUE ||
hError == INVALID_HANDLE_VALUE ||
!GetConsoleScreenBufferInfo(hOut, &info) )
return FALSE;
saved_functions = Sinput->functions;
con_functions = *Sinput->functions;
con_functions.read = Sread_win32_console;
con_functions.write = write_ansi;
con_functions.close = close_ansi;
con_functions.control = control_ansi;
con_functions.seek = NULL;
wrap_console(hIn, Sinput, &con_functions);
wrap_console(hOut, Soutput, &con_functions);
wrap_console(hError, Serror, &con_functions);
init_output(Soutput->handle, &info);
init_output(Serror->handle, &info);
PL_set_prolog_flag("tty_control", PL_BOOL, TRUE);
return TRUE;
}