scummvm/engines/glk/alan2/readline.cpp

561 lines
12 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "glk/alan2/types.h"
#ifdef GLK
#include "glk/alan2/alan2.h"
#include "glk/alan2/readline.h"
#include "glk/alan2/main.h"
#include "glk/alan2/glkio.h"
namespace Glk {
namespace Alan2 {
/*======================================================================
readline()
Read a line from the user, with history and editing
*/
/* 4f - length of user buffer should be used */
Boolean readline(char usrbuf[])
{
event_t event;
g_vm->glk_request_line_event(glkMainWin, usrbuf, 255, 0);
/* FIXME: buffer size should be infallible: all existing calls use 256 or
80 character buffers, except parse which uses LISTLEN (currently 100)
*/
do
{
g_vm->glk_select(&event);
if (evtype_Arrange == event.type)
statusline();
} while (event.type != evtype_LineInput);
usrbuf[event.val1] = 0;
return TRUE;
}
} // End of namespace Alan2
} // End of namespace Glk
#else
#include "glk/alan2/sysdep.h"
#ifdef HAVE_TERMIO
#include <termios.h>
#endif
#ifdef __PACIFIC__
#include <unixio.h>
#else
//#include <unistd.h>
#endif
#include "glk/alan2/readline.h"
#include "glk/alan2/main.h"
namespace Glk {
namespace Alan2 {
#ifdef HAVE_TERMIO
/*----------------------------------------------------------------------*\
termio handling
\*----------------------------------------------------------------------*/
static struct termios term;
static void newtermio()
{
struct termios newterm;
tcgetattr(0, &term);
newterm=term;
newterm.c_lflag&=~(ECHO|ICANON);
newterm.c_cc[VMIN]=1;
newterm.c_cc[VTIME]=0;
tcsetattr(0, TCSANOW, &newterm);
}
static void restoretermio()
{
tcsetattr(0, TCSANOW, &term);
}
#endif
/*----------------------------------------------------------------------*\
Global character buffers etc.
\*----------------------------------------------------------------------*/
static unsigned char buffer[LINELENGTH+1];
static int bufidx;
static unsigned char *history[HISTORYLENGTH];
static int histidx; /* Index where to store next history */
static int histp; /* Points to the history recalled last */
static unsigned char ch;
static int endOfInput = 0;
static Boolean change;
static Boolean insert = TRUE;
/*----------------------------------------------------------------------*\
Character map types and maps
\*----------------------------------------------------------------------*/
typedef struct {unsigned char min, max; void (*hook)(char ch);} KeyMap;
/* Forward declaration of hooks */
static void escHook(char ch);
static void insertCh(char ch);
static void arrowHook(char ch);
static void upArrow(char ch);
static void downArrow(char ch);
static void rightArrow(char ch);
static void leftArrow(char ch);
static void insertToggle(char ch);
static void newLine(char ch);
static void delFwd(char ch);
static void delBwd(char ch);
#ifdef __XXunix__
static KeyMap keymap[] = {
{0x00, 0x07, NULL},
{0x08, 0x08, delBwd},
{0x09, 0x09, NULL},
{0x0a, 0x0a, newLine},
{0x1b, 0x1b, escHook},
{0x1c, 0x7e, insertCh},
{0x7f, 0x7f, delFwd},
{0x80, 0xff, insertCh},
{0x00, 0x00, NULL}
};
/* I can't figure out what really coverns the esc-map characters... */
#ifdef __solarisX__
static KeyMap escmap[] = {
{0x00, 0x4e, NULL},
{0x4f, 0x4f, arrowHook},
{0x50, 0xff, NULL},
{0x00, 0x00, NULL}
};
#else
static KeyMap escmap[] = {
{0x00, 0x5a, NULL},
{0x5b, 0x5b, arrowHook},
{0x5c, 0xff, NULL},
{0x00, 0x00, NULL}
};
#endif
static KeyMap arrowmap[] = {
{0x00, 0x31, NULL},
{0x32, 0x32, insertToggle},
{0x33, 0x40, NULL},
{0x41, 0x41, upArrow},
{0x42, 0x42, downArrow},
{0x43, 0x43, rightArrow},
{0x44, 0x44, leftArrow},
{0x45, 0xff, NULL},
{0x00, 0x00, NULL}
};
#endif
#ifdef __win__
static KeyMap keymap[] = {
{0x00, 0x01, NULL},
{0x02, 0x02, leftArrow},
{0x03, 0x05, NULL},
{0x06, 0x06, rightArrow},
{0x07, 0x07, NULL},
{0x08, 0x08, delBwd},
{0x09, 0x09, NULL},
{0x0a, 0x0a, newLine},
{0x1b, 0x1b, escHook},
{0x1c, 0x7e, insertCh},
{0x7f, 0x7f, delFwd},
{0x80, 0xff, insertCh},
{0x00, 0x00, NULL}
};
static KeyMap escmap[] = {
{0x00, 0x5a, NULL},
{0x5b, 0x5b, arrowHook},
{0x5c, 0xff, NULL},
{0x00, 0x00, NULL}
};
static KeyMap arrowmap[] = {
{0x00, 0x31, NULL},
{0x32, 0x32, insertToggle},
{0x33, 0x40, NULL},
{0x41, 0x41, upArrow},
{0x42, 0x42, downArrow},
{0x43, 0x43, rightArrow},
{0x44, 0x44, leftArrow},
{0x45, 0xff, NULL},
{0x00, 0x00, NULL}
};
#endif
#ifdef __dos__
static KeyMap keymap[] = {
{0x00, 0x01, NULL},
{0x02, 0x02, leftArrow},
{0x03, 0x05, NULL},
{0x06, 0x06, rightArrow},
{0x07, 0x07, NULL},
{0x08, 0x08, delBwd},
{0x09, 0x09, NULL},
{0x0a, 0x0a, newLine},
{0x1b, 0x1b, escHook},
{0x1c, 0x7e, insertCh},
{0x7f, 0x7f, delFwd},
{0x80, 0xff, insertCh},
{0x00, 0x00, NULL}
};
static KeyMap escmap[] = {
{0x00, 0x5a, NULL},
{0x5b, 0x5b, arrowHook},
{0x5c, 0xff, NULL},
{0x00, 0x00, NULL}
};
static KeyMap arrowmap[] = {
{0x00, 0x31, NULL},
{0x32, 0x32, insertToggle},
{0x33, 0x40, NULL},
{0x41, 0x41, upArrow},
{0x42, 0x42, downArrow},
{0x43, 0x43, rightArrow},
{0x44, 0x44, leftArrow},
{0x45, 0xff, NULL},
{0x00, 0x00, NULL}
};
#endif
static void doBeep(void)
{
write(1, "\7", 1);
}
static void backspace(void)
{
write(1, "\b", 1);
}
static void erase()
{
int i;
for (i = 0; i < bufidx; i++) backspace(); /* Backup to beginning of text */
for (i = 0; i < strlen((char *)buffer); i++) write(1, " ", 1); /* Erase all text */
for (i = 0; i < strlen((char *)buffer); i++) backspace(); /* Backup to beginning of text */
}
/*----------------------------------------------------------------------*\
Character handling hook functions
\*----------------------------------------------------------------------*/
static void execute(KeyMap map[], unsigned char ch)
{
int i = 0;
for (i = 0; i <= 256; i++) {
if (i > 0 && map[i].min == 0x00) break; /* End marker is a 0,0,NULL */
if (map[i].min <= ch && ch <= map[i].max) {
if (map[i].hook != NULL) {
map[i].hook(ch);
return;
} else
doBeep();
}
}
doBeep();
}
static void upArrow(char ch)
{
/* Is there more history ? */
if (history[(histp+HISTORYLENGTH-1)%HISTORYLENGTH] == NULL ||
(histp+HISTORYLENGTH-1)%HISTORYLENGTH == histidx) {
write(1, "\7", 1);
return;
}
erase();
/* Backup history pointer */
histp = (histp+HISTORYLENGTH-1)%HISTORYLENGTH;
/* Copy the history and write it */
strcpy((char *)buffer, (char *)history[histp]);
bufidx = strlen((char *)buffer);
write(1, (void *)buffer, strlen((char *)buffer));
}
static void downArrow(char ch)
{
/* Is there more history ? */
if (histp == histidx) {
write(1, "\7", 1);
return;
}
erase();
/* Advance history pointer */
histp = (histp+1)%HISTORYLENGTH;
/* If we are not at the most recent history entry, copy the history and write it */
if (histp != histidx) {
strcpy((char *)buffer, (char *)history[histp]);
bufidx = strlen((char *)buffer);
write(1, (void *)buffer, strlen((char *)buffer));
} else {
bufidx = 0;
buffer[0] = '\0';
}
}
static void rightArrow(char ch)
{
if (bufidx > LINELENGTH || buffer[bufidx] == '\0')
doBeep();
else {
write(1, (void *)&buffer[bufidx], 1);
bufidx++;
}
}
static void leftArrow(char ch)
{
if (bufidx == 0)
doBeep();
else {
bufidx--;
backspace();
}
}
static void insertToggle(char ch)
{
read(0, &ch, 1);
if (ch != 'z')
doBeep();
else
insert = !insert;
}
static void delBwd(char ch)
{
if (bufidx == 0)
doBeep();
else {
int i;
change = TRUE;
backspace();
bufidx--;
for (i = 0; i <= strlen((char *)&buffer[bufidx+1]); i++)
buffer[bufidx+i] = buffer[bufidx+1+i];
write(1, (void *)&buffer[bufidx], strlen((char *)&buffer[bufidx]));
write(1, " ", 1);
for (i = 0; i <= strlen((char *)&buffer[bufidx]); i++) backspace();
}
}
static void delFwd(char ch)
{
if (bufidx > LINELENGTH || buffer[bufidx] == '\0')
doBeep();
else {
int i;
change = TRUE;
strcpy((char *)&buffer[bufidx], (char *)&buffer[bufidx+1]);
write(1, (void *)&buffer[bufidx], strlen((char *)&buffer[bufidx]));
write(1, " ", 1);
for (i = 0; i <= strlen((char *)&buffer[bufidx]); i++) backspace();
}
}
static void escHook(char ch) {
read(0, &ch, 1);
execute(escmap, ch);
}
static void arrowHook(char ch) {
read(0, &ch, 1);
execute(arrowmap, ch);
}
static void newLine(char ch)
{
endOfInput = 1;
write(1, "\n", 1);
/* If the input is not the same as the previous, save it in the history */
if (change && strlen((char *)buffer) > 0) {
if (history[histidx] == NULL)
history[histidx] = (unsigned char *)allocate(LINELENGTH+1);
strcpy((char *)history[histidx], (char *)buffer);
histidx = (histidx+1)%HISTORYLENGTH;
}
}
static void insertCh(char ch) {
if (bufidx > LINELENGTH)
doBeep();
else {
/* If at end advance the NULL */
if (buffer[bufidx] == '\0')
buffer[bufidx+1] = '\0';
else if (insert) {
int i;
/* If insert mode is on, move the characters ahead */
for (i = strlen((char *)buffer); i >= bufidx; i--)
buffer[i+1] = buffer[i];
write(1, (void *)&buffer[bufidx], strlen((char *)&buffer[bufidx]));
for (i = strlen((char *)&buffer[bufidx]); i > 0; i--) backspace();
}
change = TRUE;
buffer[bufidx] = ch;
write(1, &ch, 1);
bufidx++;
}
}
/*----------------------------------------------------------------------
echoOff()
*/
static void echoOff()
{
#ifdef HAVE_TERMIO
newtermio();
#else
#ifdef __win__
#include <windows.h>
#include <winbase.h>
#include <wincon.h>
DWORD handle = GetStdHandle(STD_INPUT_HANDLE);
(void) SetConsoleMode(handle, 0);
#endif
#endif
}
/*----------------------------------------------------------------------
echoOn()
*/
static void echoOn()
{
#ifdef HAVE_TERMIO
restoretermio();
#else
#ifdef __win__
#include <windows.h>
#include <winbase.h>
#include <wincon.h>
DWORD handle = GetStdHandle(STD_INPUT_HANDLE);
(void) SetConsoleMode(handle, ENABLE_ECHO_INPUT);
#endif
#endif
}
/*======================================================================
readline()
Read a line from the user, with history and editing
*/
/* 4f - length of user buffer should be used */
Boolean readline(char usrbuf[])
{
fflush(stdout);
bufidx = 0;
histp = histidx;
buffer[0] = '\0';
change = TRUE;
echoOff();
endOfInput = 0;
while (!endOfInput) {
if (read(0, (void *)&ch, 1) != 1) {
echoOn();
return FALSE;
}
execute(keymap, ch);
}
echoOn();
strcpy(usrbuf, (char *)buffer);
return TRUE;
}
} // End of namespace Alan2
} // End of namespace Glk
#endif