mirror of
https://github.com/cookiengineer/audacity
synced 2025-06-19 17:40:15 +02:00
399 lines
12 KiB
C
399 lines
12 KiB
C
/* winstuff.c - windows interface routines for xlisp */
|
|
/* Written by Chris Tchou. */
|
|
/* This file contains the stuff that the other xlisp files call directly. */
|
|
|
|
/* Changes by Roger Dannenberg, Jan 2006:
|
|
Previously, the input thread would block on input, so if a command line
|
|
instantiation of Nyquist called (EXIT), the process would still block
|
|
in getchar() until the user typed a newline. Now, I only start the
|
|
input thread if ostgetc is called (input is really needed). This will
|
|
still read ahead and block, but only in cases where you are already
|
|
interactive.
|
|
|
|
/* Changes by Roger Dannenberg, April 2004:
|
|
To support interrupts to Lisp processing, XLISP call oscheck frequently to
|
|
test for an abort or break condition. This condition can be activated by
|
|
special handlers, e.g. if a software interrupt is generated by Ctrl-C.
|
|
Alternatively, the application can read ahead and look for break characters
|
|
in the input stream. A third approach, implemented by Ning Hu for her
|
|
Delphi-based IDE, is to send a Windows message to the process. Unfortunately,
|
|
the Java IDE does not support sending a Windows message, nor can console
|
|
IO be used to read ahead (console IO does not work because, when started by
|
|
Java, Nyquist standard IO is redirected through pipes). The two solutions
|
|
to enable break character prcessing seem to be:
|
|
1) extend Java with C code to find the process and send Windows messages
|
|
2) add a thread to perform read ahead and break character processing
|
|
Option 2 contains the ugliness to Nyquist IO, which is already big and ugly,
|
|
and leaves Java alone, which is something I don't know much about anyway,
|
|
so I have chosen option 2: create a thread and read ahead. This uses only
|
|
about 50 lines of code.
|
|
|
|
A shortcoming of this approach is that, except for Ctrl-C, break characters
|
|
like ^P, ^B, and ^U require the user to type RETURN to end the input line
|
|
and allow the character to be processed.
|
|
|
|
The thread will set a signal whenever a line of input is delivered so that
|
|
Nyquist can block waiting for input. The signal will also be set when a
|
|
^C or ^G is detected.
|
|
|
|
For flexibility, compatibility with the Delphi IDE (NyqIDE) is retained by
|
|
continuing to check for Windows process messages.
|
|
*/
|
|
|
|
#include <windows.h> /* Added by Ning Hu Apr.2001 */
|
|
#include <process.h> /* Added by Dannenberg Apr 2004 */
|
|
#include <signal.h> /* Added by Dannneberg, Apr 2004 */
|
|
#include "exitpa.h" /* Added by Dannneberg, Apr 2004 */
|
|
|
|
|
|
const char os_pathchar = '\\';
|
|
const char os_sepchar = ',';
|
|
|
|
|
|
#undef ERROR
|
|
#include <stdio.h>
|
|
//#include <QuickDraw.h> /* for Random */
|
|
#include <memory.h> /* for DisposPtr */
|
|
#include <string.h>
|
|
//#include <SegLoad.h> /* for ExitToShell */
|
|
#include "xlisp.h"
|
|
#include "cext.h"
|
|
#include "userio.h"
|
|
#include "sliders.h"
|
|
#include "sound.h" /* define nosc_enabled */
|
|
|
|
/* externals */
|
|
extern FILE *tfp; /* transcript file pointer */
|
|
extern int cursorPos;
|
|
extern char *macgets (void);
|
|
//Added by Ning Hu Apr.2001
|
|
extern int _isatty(int);
|
|
extern int redirect_flag;
|
|
//Add end
|
|
|
|
/* local variables */
|
|
int lposition;
|
|
static char *linebuf = NULL, *lineptr;
|
|
static int numChars;
|
|
|
|
/* input thread */
|
|
unsigned long input_thread_handle = -1;
|
|
#define NEED_INPUT if (input_thread_handle == -1) start_input_thread();
|
|
#define input_buffer_max 1024
|
|
#define input_buffer_mask (input_buffer_max - 1)
|
|
char input_buffer[1024];
|
|
volatile int input_buffer_head = 0;
|
|
volatile int input_buffer_tail = 0;
|
|
volatile int buffer_eof = 0;
|
|
HANDLE input_buffer_ready = NULL;
|
|
|
|
BOOL WINAPI ctrl_c_handler(DWORD dwCtrlType)
|
|
{
|
|
if (dwCtrlType == CTRL_C_EVENT) {
|
|
abort_flag = ABORT_LEVEL;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
#ifdef DEBUG_INPUT
|
|
extern FILE *to_input_buffer;
|
|
#endif
|
|
|
|
void input_thread_run(void *args)
|
|
{
|
|
int c;
|
|
/* this gets called, possible later, in io_init() in userio.c, but
|
|
* that doesn't seem to prevent this thread from being killed by
|
|
* CTRL-C, so call it here to be safe.
|
|
*/
|
|
SetConsoleCtrlHandler(ctrl_c_handler, TRUE);
|
|
/* printf("input_thread_run\n"); */
|
|
|
|
while (!buffer_eof) {
|
|
int head;
|
|
c = getchar();
|
|
if (c == EOF && abort_flag) {
|
|
// when user types ^C, an EOF is generated for some reason.
|
|
// Ignore it...
|
|
if (abort_flag == ABORT_LEVEL) c = ABORT_CHAR;
|
|
else c = BREAK_CHAR;
|
|
} else if (c == ABORT_CHAR) {
|
|
abort_flag = ABORT_LEVEL;
|
|
} else if (!abort_flag && c == BREAK_CHAR) {
|
|
// notice that a break will be ignored until XLISP
|
|
// handles the ABORT_LEVEL
|
|
abort_flag = BREAK_LEVEL;
|
|
} else if (c == BREAK_CHAR) {
|
|
; // ignore this because abort_flag is set to ABORT_LEVEL
|
|
} else if (c == '\005' || c == '\006') { // control-e or control-f
|
|
; // ignore these. IDE will send control-f to turn off echo, but
|
|
// under Windows, echo is already turned off. We filter control-f
|
|
// here to avoid generating an error message. Maybe the IDE should
|
|
// not send control-f in the first place, but the IDE is cross-platform
|
|
// and does not know it's running under Windows, whereas this file
|
|
// is platform dependent.
|
|
} else if (c == '\016') { // begin hidden message
|
|
#define MSGBUF_MAX 64
|
|
char msgbuf[MSGBUF_MAX];
|
|
int msgbufx = 0;
|
|
char type_char = getchar(); // read message type character
|
|
printf("begin hidden message: %c\n", type_char);
|
|
if (type_char == EOF) buffer_eof = TRUE;
|
|
else {
|
|
// message is terminated by '\021'
|
|
while ((c = getchar()) != '\021' &&
|
|
c != EOF &&
|
|
msgbufx < MSGBUF_MAX - 1) {
|
|
msgbuf[msgbufx++] = c;
|
|
}
|
|
msgbuf[msgbufx++] = 0;
|
|
printf("message: %s\n", msgbuf);
|
|
if (c == EOF) buffer_eof = TRUE;
|
|
else if (msgbufx < MSGBUF_MAX) {
|
|
if (type_char == 'S') { // slider change message
|
|
// message format is index<space>value
|
|
int index;
|
|
float value;
|
|
if (sscanf(msgbuf, "%d %g", &index, &value) == 2) {
|
|
set_slider(index, value);
|
|
printf("set_slider %d %g\n", index, value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (c == EOF) {
|
|
buffer_eof = TRUE;
|
|
} else {
|
|
// insert character into the FIFO
|
|
head = (input_buffer_head + 1) & input_buffer_mask;
|
|
while (head == input_buffer_tail) Sleep(100);
|
|
input_buffer[input_buffer_head] = c;
|
|
#ifdef DEBUG_INPUT
|
|
if (to_input_buffer) putc(c, to_input_buffer);
|
|
#endif
|
|
input_buffer_head = head;
|
|
}
|
|
if (c == '\n' || abort_flag || buffer_eof) {
|
|
SetEvent(input_buffer_ready);
|
|
// wake up Nyquist if it is waiting for input
|
|
}
|
|
}
|
|
// printf("Input thread exiting\n");
|
|
}
|
|
|
|
//int isascii (char c) { return 1; } /* every char is an ascii char, isn't it? */
|
|
|
|
void start_input_thread()
|
|
{
|
|
// create thread to process input
|
|
input_thread_handle = _beginthread(input_thread_run, 0, NULL);
|
|
if (input_thread_handle == -1) {
|
|
printf("Unable to create input thread, errno = %d\n", errno);
|
|
EXIT(1);
|
|
}
|
|
}
|
|
|
|
void osinit (char *banner)
|
|
{
|
|
printf(banner);
|
|
if (_isatty( _fileno( stdin ) ) ){
|
|
redirect_flag = 0;
|
|
#ifdef DEBUG
|
|
printf( "stdout has not been redirected to a file\n" ); //for debugging use
|
|
#endif
|
|
} else {
|
|
redirect_flag = 1;
|
|
#ifdef DEBUG
|
|
printf( "stdout has been redirected to a file\n"); //for debugging use
|
|
#endif
|
|
}
|
|
// signal when input is ready
|
|
input_buffer_ready = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (input_buffer_ready == NULL) {
|
|
printf("Unable to create Event object\n");
|
|
EXIT(1);
|
|
}
|
|
}
|
|
|
|
FILE *osaopen (char *name, char *mode) {
|
|
return fopen (name, mode);
|
|
}
|
|
|
|
FILE *osbopen (char *name, char *mode) {
|
|
char nmode[4];
|
|
strcpy (nmode, mode); strcat (nmode, "b");
|
|
return (fopen (name, nmode));
|
|
}
|
|
|
|
int osclose (FILE *fp) { return (fclose (fp)); }
|
|
int osaputc (int ch, FILE *fp) { return (putc (ch, fp)); }
|
|
int osbputc (int ch, FILE *fp) { return (putc (ch, fp)); }
|
|
void osoutflush(FILE *fp) { fflush(fp); }
|
|
|
|
/* osagetc - get a character from an ascii file */
|
|
int osagetc(fp)
|
|
FILE *fp;
|
|
{
|
|
return (getc(fp));
|
|
}
|
|
|
|
extern int abort_flag;
|
|
extern int redirect_flag; //Added by Ning Hu Apr.2001
|
|
int ostgetc (void)
|
|
{
|
|
int c;
|
|
NEED_INPUT;
|
|
while (!buffer_eof && (input_buffer_tail == input_buffer_head)) {
|
|
oscheck();
|
|
WaitForSingleObject(input_buffer_ready, INFINITE);
|
|
}
|
|
if (buffer_eof) c = EOF;
|
|
else {
|
|
c = input_buffer[input_buffer_tail];
|
|
input_buffer_tail = (input_buffer_tail + 1) & input_buffer_mask;
|
|
}
|
|
if (c == '\025') { // control-u
|
|
xlcleanup();
|
|
} else if (c == '\020') { // control-p
|
|
xlcontinue();
|
|
} else if (c == '\024') { // control-t
|
|
xinfo();
|
|
}
|
|
return c;
|
|
}
|
|
|
|
|
|
void ostputc (int ch) {
|
|
// macputc (ch);
|
|
putchar(ch); // console
|
|
|
|
if (tfp) osaputc (ch, tfp);
|
|
}
|
|
|
|
void ostoutflush()
|
|
{
|
|
if (tfp) fflush(tfp);
|
|
fflush(stdout);
|
|
}
|
|
|
|
|
|
void osflush (void) {
|
|
lineptr = linebuf;
|
|
numChars = 0;
|
|
lposition = 0;
|
|
}
|
|
|
|
|
|
void oscheck (void) {
|
|
MSG lpMsg;
|
|
|
|
#if OSC
|
|
if (nosc_enabled) nosc_poll();
|
|
#endif
|
|
|
|
// check_aborted(); -- call to userio.c superceded by code here in winstuff.c
|
|
// printf("Current Thread: %d\n", GetCurrentThreadId()); //for debugging use
|
|
// look for Windows messages from NyqIDE (a Delphi program)
|
|
if ((redirect_flag) && (PeekMessage(&lpMsg, NULL, 0, 0, PM_REMOVE)!=0)) {
|
|
if (lpMsg.message == WM_CHAR) {
|
|
switch (lpMsg.wParam) {
|
|
case ABORT_CHAR: abort_flag = ABORT_LEVEL;
|
|
break;
|
|
case BREAK_CHAR: // for nyquist, defined to be 2
|
|
case 7: // NyqIDE sends 7 (BEL) as break character
|
|
abort_flag = BREAK_LEVEL;
|
|
break;
|
|
}
|
|
// printf("Get message: %d %d %d\n", lpMsg.wParam, BREAK_CHAR, abort_flag); //for debugging use
|
|
}
|
|
}
|
|
if (abort_flag == ABORT_LEVEL) {
|
|
abort_flag = 0;
|
|
osflush();
|
|
xltoplevel();
|
|
} else if (abort_flag == BREAK_LEVEL) {
|
|
abort_flag = 0;
|
|
osflush();
|
|
xlbreak("BREAK", s_unbound);
|
|
}
|
|
}
|
|
//Update end
|
|
|
|
void oserror (char *msg) {
|
|
char line[100], *p;
|
|
sprintf (line,"error: %s\n",msg);
|
|
for (p = line; *p != '\0'; ++p) ostputc (*p);
|
|
}
|
|
|
|
void osfinish(void) {
|
|
portaudio_exit();
|
|
/* dispose of everything... */
|
|
// if (linebuf) DisposPtr (linebuf);
|
|
// MacWrapUp ();
|
|
// ExitToShell ();
|
|
}
|
|
|
|
int renamebackup (char *filename) { return 0; }
|
|
|
|
|
|
|
|
|
|
static WIN32_FIND_DATA FindFileData;
|
|
static HANDLE hFind = INVALID_HANDLE_VALUE;
|
|
#define OSDIR_LIST_READY 0
|
|
#define OSDIR_LIST_STARTED 1
|
|
#define OSDIR_LIST_DONE 2
|
|
static int osdir_list_status = OSDIR_LIST_READY;
|
|
#define OSDIR_MAX_PATH 256
|
|
static char osdir_path[OSDIR_MAX_PATH];
|
|
|
|
// osdir_list_start -- prepare to list a directory
|
|
int osdir_list_start(char *path)
|
|
{
|
|
if (strlen(path) >= OSDIR_MAX_PATH - 2) {
|
|
xlcerror("LISTDIR path too big", "return nil", NULL);
|
|
return FALSE;
|
|
}
|
|
strcpy(osdir_path, path);
|
|
strcat(osdir_path, "/*"); // make a pattern to match all files
|
|
if (osdir_list_status != OSDIR_LIST_READY) {
|
|
osdir_list_finish(); // close previously interrupted listing
|
|
}
|
|
hFind = FindFirstFile(osdir_path, &FindFileData); // get the "."
|
|
if (hFind == INVALID_HANDLE_VALUE) return FALSE;
|
|
if (FindNextFile(hFind, &FindFileData) == 0) return FALSE; // get the ".."
|
|
osdir_list_status = OSDIR_LIST_STARTED;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
char *osdir_list_next()
|
|
{
|
|
if (FindNextFile(hFind, &FindFileData) == 0) {
|
|
osdir_list_status = OSDIR_LIST_DONE;
|
|
return NULL;
|
|
}
|
|
return FindFileData.cFileName;
|
|
}
|
|
|
|
void osdir_list_finish()
|
|
{
|
|
if (osdir_list_status != OSDIR_LIST_READY) {
|
|
FindClose(hFind);
|
|
}
|
|
osdir_list_status = OSDIR_LIST_READY;
|
|
}
|
|
|
|
|
|
/* xechoenabled -- set/clear echo_enabled flag (unix only) */
|
|
LVAL xechoenabled()
|
|
{
|
|
int flag = (xlgetarg() != NULL);
|
|
xllastarg();
|
|
// echo_enabled = flag; -- do nothing in Windows
|
|
return NULL;
|
|
}
|
|
|
|
|