improve SWI emulation in WIN32.
This commit is contained in:
459
console/LGPL/pl-ntconsole.c
Executable file
459
console/LGPL/pl-ntconsole.c
Executable file
@@ -0,0 +1,459 @@
|
||||
/* $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;
|
||||
}
|
Reference in New Issue
Block a user