The R Project SVN R

Rev

Rev 6192 | Rev 6357 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 6192 Rev 6283
Line 30... Line 30...
30
#include "Graphics.h"		/* KillAllDevices() [nothing else?] */
30
#include "Graphics.h"		/* KillAllDevices() [nothing else?] */
31
#include "graphapp/ga.h"
31
#include "graphapp/ga.h"
32
#include "console.h"
32
#include "console.h"
33
#include "rui.h"
33
#include "rui.h"
34
#include "getline/getline.h"
34
#include "getline/getline.h"
35
/*#include "devga.h"*/
35
#include <windows.h>  /* for CreateEvent,.. */
36
/*#include <windows.h>*/
36
#include <process.h> /* for _begithread,... */
37
#include "run.h"
37
#include "run.h"
38
#include "Startup.h"
38
#include "Startup.h"
39
 
39
 
40
int SaveAction = SA_DEFAULT;
40
int SaveAction = SA_DEFAULT;
41
int RestoreAction = SA_RESTORE;
41
int RestoreAction = SA_RESTORE;
Line 52... Line 52...
52
/* used to avoid some flashing during cleaning up */
52
/* used to avoid some flashing during cleaning up */
53
int   AllDevicesKilled = 0;
53
int   AllDevicesKilled = 0;
54
int   setupui(void);
54
int   setupui(void);
55
void  delui(void);
55
void  delui(void);
56
 
56
 
57
DWORD mainThreadId;
57
static DWORD mainThreadId;
58
 
58
 
59
 
59
 
60
int   UserBreak = 0;
60
int   UserBreak = 0;
61
 
61
 
62
/* callbacks */
62
/* callbacks */
Line 71... Line 71...
71
void ProcessEvents(void)
71
void ProcessEvents(void)
72
{
72
{
73
    while (peekevent()) doevent();
73
    while (peekevent()) doevent();
74
    if (UserBreak) {
74
    if (UserBreak) {
75
	UserBreak = 0;
75
	UserBreak = 0;
76
	/* error("user break"); */
-
 
77
	raise(SIGINT);
76
	raise(SIGINT);
78
    }
77
    }
79
    R_CallBackHook();
78
    R_CallBackHook();
80
}
79
}
81
 
80
 
Line 96... Line 95...
96
/*
95
/*
97
 *  2. CONSOLE I/O
96
 *  2. CONSOLE I/O
98
 */
97
 */
99
 
98
 
100
/*
99
/*
101
 * I realized that we are supporting 4 different type of input.
100
 * We support 4 different type of input.
102
 * 1) from the gui console;
101
 * 1) from the gui console;
103
 * 2) from a character mode console (interactive);
102
 * 2) from a character mode console (interactive);
104
 * 3) from a pipe under --ess, i.e, interactive.
103
 * 3) from a pipe under --ess, i.e, interactive.
105
 * 4) from a file or from a pipe (not interactive)
104
 * 4) from a file or from a pipe (not interactive)
106
 *
105
 *
107
 * Hence, it is better to have a different function for every
106
 * Hence, it is better to have a different function for every
108
 * situation.
107
 * situation.
109
 * Same, it is true for output (but in this case, 3==4)
108
 * Same, it is true for output (but in this case, 2=3=4)
110
 *
109
 *
111
 * BTW, 3 and 4 are different on input  since fgets,ReadFile...
110
 * BTW, 3 and 4 are different on input  since fgets,ReadFile...
112
 * "blocks" =>
-
 
113
 * (e.g.) you cannot give focus to the graphics device if
111
 * "blocks" =>  (e.g.) you cannot give focus to the graphics device if
114
 * you are wating for input. For this reason, under 3,
112
 * you are wating for input. For this reason, input is got in a different 
-
 
113
 * thread 
-
 
114
 *
-
 
115
 * All works in this way:
115
 * fgets is runned in a different thread (Windows is wonderful,
116
 * R_ReadConsole calls TrueReadConsole which points to:
-
 
117
 * case 1: GuiReadConsole
-
 
118
 * case 2 and 3: ThreadedReadConsole
-
 
119
 * case 4: FileReadConsole
116
 * I never used 'threads', hence, after made this running
120
 * ThreadedReadConsole wake up our 'reader thread' and wait until
117
 * I was very, very happy "Wuah, fgets in a thread!!!")
121
 * a new line of input is available. The 'reader thread' uses 
-
 
122
 * InThreadReadConsole to get it. InThreadReadConsole points to:
-
 
123
 * case 2: CharReadConsole
-
 
124
 * case 3: FileReadConsole
118
 */
125
*/
-
 
126
 
-
 
127
/* Global variables */
-
 
128
static int (*TrueReadConsole) (char *, char *, int, int);
-
 
129
static int (*InThreadReadConsole) (char *, char *, int, int);
-
 
130
static void (*TrueWriteConsole) (char *, int);
-
 
131
HANDLE EhiWakeUp;
-
 
132
static char *tprompt, *tbuf;
-
 
133
static  int tlen, thist, lineavailable;
-
 
134
 
-
 
135
 /* Fill a text buffer with user typed console input. */
-
 
136
int
-
 
137
R_ReadConsole(char *prompt, unsigned char *buf, int len, int addtohistory)
-
 
138
{
-
 
139
    ProcessEvents();
-
 
140
    return TrueReadConsole(prompt, (char *) buf, len, addtohistory);
-
 
141
}
-
 
142
 
-
 
143
	/* Write a text buffer to the console. */
-
 
144
	/* All system output is filtered through this routine. */
-
 
145
 
-
 
146
void R_WriteConsole(char *buf, int len)
-
 
147
{
-
 
148
    ProcessEvents();
-
 
149
    TrueWriteConsole(buf, len);
-
 
150
}
-
 
151
 
119
 
152
 
120
 
153
 
121
/*1: from GUI console */
154
/*1: from GUI console */
122
static int R_is_running = 0;
155
static int R_is_running = 0;
123
 
156
 
Line 125... Line 158...
125
{
158
{
126
    if(R_is_running && setWidthOnResize)
159
    if(R_is_running && setWidthOnResize)
127
	R_SetOptionWidth(cols);
160
	R_SetOptionWidth(cols);
128
}
161
}
129
 
162
 
-
 
163
static int 
130
static int GuiReadConsole(char *prompt, char *buf, int len, int addtohistory)
164
GuiReadConsole(char *prompt, char *buf, int len, int addtohistory)
131
{
165
{
132
    char *p;
166
    char *p;
133
    char *NormalPrompt =
167
    char *NormalPrompt =
134
	(char *) CHAR(STRING(GetOption(install("prompt"), R_NilValue))[0]);
168
	(char *) CHAR(STRING(GetOption(install("prompt"), R_NilValue))[0]);
135
 
169
 
Line 144... Line 178...
144
	    *p = '\001';
178
	    *p = '\001';
145
    ConsoleAcceptCmd = 0;
179
    ConsoleAcceptCmd = 0;
146
    return 1;
180
    return 1;
147
}
181
}
148
 
182
 
149
static void GuiWriteConsole(char *buf,int len)
-
 
150
{
-
 
151
    char *p;
-
 
152
 
-
 
153
    for (p = buf; *p; p++)
-
 
154
	if (*p == '\001')
-
 
155
	    *p = EOF;
-
 
156
    consolewrites(RConsole, buf);
-
 
157
}
-
 
158
 
-
 
159
/*2: from character console with getline */
-
 
160
 
183
 
161
static char LastLine[512], *gl = NULL;
-
 
162
static int lineavailable;
184
/* 2 and 3: reading in a thread */
163
static DWORD id;
-
 
164
 
185
 
165
static void pipe_onintr()
-
 
166
{
-
 
167
    UserBreak = 1;
-
 
168
    PostThreadMessage(mainThreadId, 0, 0, 0);
-
 
169
}
-
 
170
 
186
 
171
static DWORD CALLBACK
187
/* 'Reader thread' main function */
172
threadedgetline(LPVOID unused)
188
static void __cdecl ReaderThread(void *unused)
173
{
189
{ 
-
 
190
  while(1) {
174
    gl = getline(LastLine);
191
    WaitForSingleObject(EhiWakeUp,INFINITE);
-
 
192
    tlen = InThreadReadConsole(tprompt,tbuf,tlen,thist);
175
    lineavailable = 1;
193
    lineavailable = 1;
176
    PostThreadMessage(mainThreadId, 0, 0, 0);
194
    PostThreadMessage(mainThreadId, 0, 0, 0);
177
    return 0;
-
 
178
}
-
 
179
 
-
 
180
int CharReadConsole(char *prompt, char *buf, int len, int addtohistory)
-
 
181
{
-
 
182
    int   i;
-
 
183
    HANDLE rH;
-
 
184
    if (!gl) {
-
 
185
      /*
-
 
186
         All the signal stuff must be rethinked. Anyway, in this way
-
 
187
         it works both under NT and 9X. But, first SIGINT can be lost
-
 
188
         (if is received AFTER signal(SIGINT,pipe_onintr) and
-
 
189
          BEFORE WaitMessage() below).
-
 
190
      */
-
 
191
        sighandler_t oldhandler = signal(SIGINT, pipe_onintr);
-
 
192
	strcpy(LastLine, prompt);
-
 
193
	lineavailable = 0;
-
 
194
	mainThreadId = GetCurrentThreadId();
-
 
195
	rH = CreateThread(NULL, 0, threadedgetline, NULL, 0, &id);
-
 
196
	while (1) {
-
 
197
	    WaitMessage();
-
 
198
	    if (lineavailable || UserBreak) break;
-
 
199
	    doevent();
-
 
200
	}
-
 
201
        signal(SIGINT, oldhandler);
-
 
202
	if (UserBreak) {
-
 
203
		UserBreak = 0;
-
 
204
		lineavailable = 0;
-
 
205
		TerminateThread(rH, 1);
-
 
206
		CloseHandle(rH);
-
 
207
		gl = NULL;
-
 
208
		/* raise(SIGINT);  just clean up ourselves */
-
 
209
		Rprintf("^C\n");
-
 
210
		strcpy(buf, "\n");
-
 
211
		return 1;
-
 
212
	}
-
 
213
	CloseHandle(rH);
-
 
214
	LastLine[0] = '\0';
-
 
215
	if (addtohistory)
-
 
216
	    gl_histadd(gl);
-
 
217
    }
195
  }
218
    for (i = 0; *gl && (*gl != '\n') && (i < len - 2); gl++, i++)
-
 
219
	buf[i] = *gl;
-
 
220
    buf[i] = '\n';
-
 
221
    buf[i + 1] = '\0';
-
 
222
    if (!*gl || (*gl == '\n'))
-
 
223
	gl = NULL;
-
 
224
    return 1;
-
 
225
}
196
}  
226
 
-
 
227
void CharWriteConsole(char *buf, int len)
-
 
228
{
-
 
229
    char *p = strrchr(buf, '\n');
-
 
230
 
197
 
-
 
198
static int 
-
 
199
ThreadedReadConsole(char *prompt, char *buf, int len, int addtohistory)
-
 
200
{ 
-
 
201
  sighandler_t oldint,oldbreak;
-
 
202
  /* 
-
 
203
   *   SIGINT/SIGBREAK when ESS is waiting for output are a real pain: 
-
 
204
   *   they get processed after user hit <return>. 
-
 
205
   *   The '^C\n' in raw Rterm is nice. But, do we really need it ? 
-
 
206
  */
-
 
207
  oldint = signal(SIGINT, SIG_IGN);
-
 
208
  oldbreak = signal(SIGBREAK, SIG_IGN);
-
 
209
  mainThreadId = GetCurrentThreadId();
-
 
210
  lineavailable = 0;
-
 
211
  tprompt = prompt;
-
 
212
  tbuf = buf;
-
 
213
  tlen = len;
-
 
214
  thist = addtohistory;
-
 
215
  PulseEvent(EhiWakeUp);
231
    if (p)
216
  while (1) {
232
	strcpy(LastLine, p + 1);
217
    WaitMessage();
-
 
218
    if (lineavailable) break;
233
    else
219
    doevent();
-
 
220
  }
-
 
221
  lineavailable = 0;
-
 
222
  /* restore handler  */
234
	strcat(LastLine, buf);
223
  signal(SIGINT,oldint);
-
 
224
  signal(SIGBREAK,oldbreak);
235
    printf("%s", buf);
225
  return tlen;
236
}
226
}
237
 
227
 
238
/*3: from a pipe under --ess*/
-
 
239
 
-
 
240
/*
-
 
241
 * Variables used to communicate between thread and main process
-
 
242
 */
-
 
243
static int lengthofbuffer;
-
 
244
static char *inputbuffer;
-
 
245
 
-
 
246
static DWORD CALLBACK
-
 
247
threadedfgets(LPVOID unused)
-
 
248
{
-
 
249
    signal(SIGINT, pipe_onintr);
-
 
250
    inputbuffer = fgets(inputbuffer, lengthofbuffer, stdin);
-
 
251
    lineavailable = 1;
-
 
252
    PostThreadMessage(mainThreadId, 0, 0, 0);
-
 
253
    return 0;
-
 
254
}
-
 
255
 
228
 
-
 
229
/*2: from character console with getline (only used as InThreadReadConsole)*/
256
static int
230
static int
257
PipeReadConsole(char *prompt, char *buf, int len, int addhistory)
231
CharReadConsole(char *prompt, char *buf, int len, int addtohistory)
258
{
232
{
259
    HANDLE rH;
-
 
260
    DWORD  id;
-
 
261
    sighandler_t oldhandler = signal(SIGINT, pipe_onintr);
-
 
262
    if (!R_Slave) {
-
 
263
	fputs(prompt, stdout);
233
   getline(prompt,buf,len);
264
	fflush(stdout);
-
 
265
    }
-
 
266
    lineavailable = 0;
-
 
267
    lengthofbuffer = len;
-
 
268
    inputbuffer = buf;
-
 
269
    mainThreadId = GetCurrentThreadId();
234
   if (addtohistory) gl_histadd(buf);
270
    rH = CreateThread(NULL, 0, threadedfgets, NULL, 0, &id);
-
 
271
    if (!rH) {
-
 
272
	/* failure! Use standard fgets. */
-
 
273
	inputbuffer = fgets(buf, len, stdin);
-
 
274
	lineavailable = 1;
-
 
275
    } else {
-
 
276
	while (1) {
-
 
277
	    WaitMessage();
-
 
278
	    if (lineavailable) break;
-
 
279
	    doevent();
-
 
280
	}
-
 
281
	if (rH)
-
 
282
	    CloseHandle(rH);
-
 
283
    }
-
 
284
    signal(SIGINT, oldhandler);
-
 
285
    if(!inputbuffer) {
-
 
286
	strcpy(buf, "\n");
-
 
287
	printf("^C\n");
-
 
288
    }
-
 
289
    return 1;
235
   return 1;
290
}
236
}
291
 
237
 
292
/*4: non-interactive
238
/*3: (as InThreadReadConsole) and 4: non-interactive */
293
*/
-
 
294
static int
239
static int
295
FileReadConsole(char *prompt, char *buf, int len, int addhistory)
240
FileReadConsole(char *prompt, char *buf, int len, int addhistory)
296
{
241
{
297
    if (!R_Slave) {
242
    if (!R_Slave) {
298
	fputs(prompt, stdout);
243
	fputs(prompt, stdout);
299
	fflush(stdout);
244
	fflush(stdout);
300
    }
245
    }
301
    if (!fgets(buf, len, stdin))
246
    if (!fgets(buf, len, stdin))
302
	return 0;
247
	return 0;
303
    if (!R_Slave)
248
    if (!R_Interactive && !R_Slave)
304
	fputs(buf, stdout);
249
	fputs(buf, stdout);
305
    return 1;
250
    return 1;
306
}
251
}
307
 
252
 
308
static void
-
 
309
FileWriteConsole(char *buf, int len)
-
 
310
{
-
 
311
    printf("%s", buf);
-
 
312
}
-
 
313
 
253
 
-
 
254
/* Rgui */
-
 
255
static void 
-
 
256
GuiWriteConsole(char *buf,int len)
-
 
257
{
-
 
258
    char *p;
314
 
259
 
315
static int (*TrueReadConsole) (char *, char *, int, int);
260
    for (p = buf; *p; p++)
-
 
261
	if (*p == '\001')
-
 
262
	    *p = EOF;
316
static void (*TrueWriteConsole) (char *, int);
263
    consolewrites(RConsole, buf);
-
 
264
}
317
 
265
 
318
 /* Fill a text buffer with user typed console input. */
266
/* Rterm write */
319
int
267
static void
320
R_ReadConsole(char *prompt, unsigned char *buf, int len, int addtohistory)
268
TermWriteConsole(char *buf, int len)
321
{
269
{
322
    ProcessEvents();
270
    printf("%s", buf);
323
    return TrueReadConsole(prompt, (char *) buf, len, addtohistory);
-
 
324
}
271
}
325
 
272
 
326
	/* Write a text buffer to the console. */
-
 
327
	/* All system output is filtered through this routine. */
-
 
328
 
273
 
329
void R_WriteConsole(char *buf, int len)
-
 
330
{
-
 
331
    ProcessEvents();
-
 
332
    TrueWriteConsole(buf, len);
-
 
333
}
274
 
334
 
275
 
335
 
276
 
336
	/* Indicate that input is coming from the console */
277
	/* Indicate that input is coming from the console */
337
 
278
 
338
void R_ResetConsole()
279
void R_ResetConsole()
Line 663... Line 604...
663
	if (!strcmp(av[i], "--no-environ") || !strcmp(av[i], "--vanilla"))
604
	if (!strcmp(av[i], "--no-environ") || !strcmp(av[i], "--vanilla"))
664
		Rp->NoRenviron = True;
605
		Rp->NoRenviron = True;
665
 
606
 
666
/* Here so that --ess and similar can change */
607
/* Here so that --ess and similar can change */
667
    Rp->CallBack = R_DoNothing;
608
    Rp->CallBack = R_DoNothing;
-
 
609
    InThreadReadConsole = NULL;
668
    if (CharacterMode == RTerm) {
610
    if (CharacterMode == RTerm) {
669
	if (isatty(0)) {
611
	if (isatty(0)) {
670
	    Rp->R_Interactive = True;
612
	    Rp->R_Interactive = True;
671
	    LastLine[0] = 0;
-
 
672
	    Rp->ReadConsole = CharReadConsole;
613
	    Rp->ReadConsole = ThreadedReadConsole;
673
	    Rp->WriteConsole = CharWriteConsole;
614
            InThreadReadConsole = CharReadConsole;
674
	} else {
615
	} else {
675
	    Rp->R_Interactive = False;
616
	    Rp->R_Interactive = False;
676
	    R_Consolefile = stdout; /* used for errors */
-
 
677
	    R_Outputfile = stdout;  /* used for sink-able output */
-
 
678
	    Rp->ReadConsole = FileReadConsole;
617
	    Rp->ReadConsole = FileReadConsole;
679
	    Rp->WriteConsole = FileWriteConsole;
-
 
680
	}
618
	}
-
 
619
	R_Consolefile = stdout; /* used for errors */
-
 
620
	R_Outputfile = stdout;  /* used for sink-able output */
-
 
621
        Rp->WriteConsole = TermWriteConsole;
681
	Rp->message = char_message;
622
	Rp->message = char_message;
682
	Rp->yesnocancel = char_yesnocancel;
623
	Rp->yesnocancel = char_yesnocancel;
683
	Rp->busy = CharBusy;
624
	Rp->busy = CharBusy;
684
    } else {
625
    } else {
685
	Rp->R_Interactive = True;
626
	Rp->R_Interactive = True;
Line 711... Line 652...
711
	    if (!strcmp(*av, "--no-environ")) {
652
	    if (!strcmp(*av, "--no-environ")) {
712
		Rp->NoRenviron = True;
653
		Rp->NoRenviron = True;
713
	    } else if (!strcmp(*av, "--ess")) {
654
	    } else if (!strcmp(*av, "--ess")) {
714
/* Assert that we are interactive even if input is from a file */
655
/* Assert that we are interactive even if input is from a file */
715
		Rp->R_Interactive = True;
656
		Rp->R_Interactive = True;
716
		Rp->ReadConsole = PipeReadConsole;
657
		Rp->ReadConsole = ThreadedReadConsole;
-
 
658
                InThreadReadConsole = FileReadConsole;
717
	    } else if (!strcmp(*av, "--mdi")) {
659
	    } else if (!strcmp(*av, "--mdi")) {
718
		MDIset = 1;
660
		MDIset = 1;
719
	    } else if (!strcmp(*av, "--sdi") || !strcmp(*av, "--no-mdi")) {
661
	    } else if (!strcmp(*av, "--sdi") || !strcmp(*av, "--no-mdi")) {
720
		MDIset = -1;
662
		MDIset = -1;
721
	    } else {
663
	    } else {
Line 752... Line 694...
752
 *  that they should be forced to specify in the non-interactive case.
694
 *  that they should be forced to specify in the non-interactive case.
753
 */
695
 */
754
    if (!R_Interactive && SaveAction != SA_SAVE && SaveAction != SA_NOSAVE)
696
    if (!R_Interactive && SaveAction != SA_SAVE && SaveAction != SA_NOSAVE)
755
	R_Suicide("you must specify `--save', `--no-save' or `--vanilla'");
697
	R_Suicide("you must specify `--save', `--no-save' or `--vanilla'");
756
 
698
 
-
 
699
    if (InThreadReadConsole && 
-
 
700
        (!(EhiWakeUp = CreateEvent(NULL,FALSE,FALSE,NULL)) ||
-
 
701
	 (_beginthread(ReaderThread,0,NULL)==-1)))
-
 
702
      R_Suicide("impossible to create 'reader thread'; you must free some system resources");
757
    return 0;
703
    return 0;
758
}
704
}
-
 
705
 
-
 
706
 
-
 
707