Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/programs/conhost/conhost.c
4389 views
1
/*
2
* Copyright 1998 Alexandre Julliard
3
* Copyright 2001 Eric Pouech
4
* Copyright 2012 Detlef Riekenberg
5
* Copyright 2020 Jacek Caban
6
*
7
* This library is free software; you can redistribute it and/or
8
* modify it under the terms of the GNU Lesser General Public
9
* License as published by the Free Software Foundation; either
10
* version 2.1 of the License, or (at your option) any later version.
11
*
12
* This library is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
* Lesser General Public License for more details.
16
*
17
* You should have received a copy of the GNU Lesser General Public
18
* License along with this library; if not, write to the Free Software
19
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20
*/
21
22
#include <assert.h>
23
#include <limits.h>
24
25
#include "conhost.h"
26
27
#include "wine/server.h"
28
#include "wine/debug.h"
29
30
WINE_DEFAULT_DEBUG_CHANNEL(console);
31
32
static const char_info_t empty_char_info = { ' ', 0x0007 }; /* white on black space */
33
34
static CRITICAL_SECTION console_section;
35
static CRITICAL_SECTION_DEBUG critsect_debug =
36
{
37
0, 0, &console_section,
38
{ &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
39
0, 0, { (DWORD_PTR)(__FILE__ ": console_section") }
40
};
41
static CRITICAL_SECTION console_section = { &critsect_debug, -1, 0, 0, 0, 0 };
42
43
static void *ioctl_buffer;
44
static size_t ioctl_buffer_size;
45
46
static void *alloc_ioctl_buffer( size_t size )
47
{
48
if (size > ioctl_buffer_size)
49
{
50
void *new_buffer;
51
if (!(new_buffer = realloc( ioctl_buffer, size ))) return NULL;
52
ioctl_buffer = new_buffer;
53
ioctl_buffer_size = size;
54
}
55
return ioctl_buffer;
56
}
57
58
static int screen_buffer_compare_id( const void *key, const struct wine_rb_entry *entry )
59
{
60
struct screen_buffer *screen_buffer = WINE_RB_ENTRY_VALUE( entry, struct screen_buffer, entry );
61
return PtrToLong(key) - screen_buffer->id;
62
}
63
64
static struct wine_rb_tree screen_buffer_map = { screen_buffer_compare_id };
65
66
static void destroy_screen_buffer( struct screen_buffer *screen_buffer )
67
{
68
if (screen_buffer->console->active == screen_buffer)
69
screen_buffer->console->active = NULL;
70
wine_rb_remove( &screen_buffer_map, &screen_buffer->entry );
71
free( screen_buffer->font.face_name );
72
free( screen_buffer->data );
73
free( screen_buffer );
74
}
75
76
static struct screen_buffer *create_screen_buffer( struct console *console, int id, int width, int height )
77
{
78
struct screen_buffer *screen_buffer;
79
unsigned int i;
80
81
if (!(screen_buffer = calloc( 1, sizeof(*screen_buffer) ))) return NULL;
82
screen_buffer->console = console;
83
screen_buffer->id = id;
84
screen_buffer->mode = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT;
85
screen_buffer->cursor_size = 25;
86
screen_buffer->cursor_visible = 1;
87
screen_buffer->width = width;
88
screen_buffer->height = height;
89
90
if (console->active)
91
{
92
screen_buffer->max_width = console->active->max_width;
93
screen_buffer->max_height = console->active->max_height;
94
screen_buffer->win.right = console->active->win.right - console->active->win.left;
95
screen_buffer->win.bottom = console->active->win.bottom - console->active->win.top;
96
screen_buffer->attr = console->active->attr;
97
screen_buffer->popup_attr = console->active->attr;
98
screen_buffer->font = console->active->font;
99
memcpy( screen_buffer->color_map, console->active->color_map, sizeof(console->active->color_map) );
100
101
if (screen_buffer->font.face_len)
102
{
103
screen_buffer->font.face_name = malloc( screen_buffer->font.face_len * sizeof(WCHAR) );
104
if (!screen_buffer->font.face_name)
105
{
106
free( screen_buffer );
107
return NULL;
108
}
109
110
memcpy( screen_buffer->font.face_name, console->active->font.face_name,
111
screen_buffer->font.face_len * sizeof(WCHAR) );
112
}
113
}
114
else
115
{
116
screen_buffer->max_width = width;
117
screen_buffer->max_height = height;
118
screen_buffer->win.right = width - 1;
119
screen_buffer->win.bottom = height - 1;
120
screen_buffer->attr = FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED;
121
screen_buffer->popup_attr = 0xf5;
122
screen_buffer->font.weight = FW_NORMAL;
123
screen_buffer->font.pitch_family = FIXED_PITCH | FF_DONTCARE;
124
}
125
126
if (wine_rb_put( &screen_buffer_map, LongToPtr(id), &screen_buffer->entry ))
127
{
128
free( screen_buffer );
129
ERR( "id %x already exists\n", id );
130
return NULL;
131
}
132
133
if (!(screen_buffer->data = malloc( screen_buffer->width * screen_buffer->height *
134
sizeof(*screen_buffer->data) )))
135
{
136
destroy_screen_buffer( screen_buffer );
137
return NULL;
138
}
139
140
/* clear the first row */
141
for (i = 0; i < screen_buffer->width; i++) screen_buffer->data[i] = empty_char_info;
142
/* and copy it to all other rows */
143
for (i = 1; i < screen_buffer->height; i++)
144
memcpy( &screen_buffer->data[i * screen_buffer->width], screen_buffer->data,
145
screen_buffer->width * sizeof(char_info_t) );
146
147
return screen_buffer;
148
}
149
150
static BOOL is_active( struct screen_buffer *screen_buffer )
151
{
152
return screen_buffer == screen_buffer->console->active;
153
}
154
155
static unsigned int get_tty_cp( struct console *console )
156
{
157
return console->is_unix ? CP_UNIXCP : CP_UTF8;
158
}
159
160
static void tty_flush( struct console *console )
161
{
162
if (!console->tty_output || !console->tty_buffer_count) return;
163
TRACE("%s\n", debugstr_an(console->tty_buffer, console->tty_buffer_count));
164
if (!WriteFile( console->tty_output, console->tty_buffer, console->tty_buffer_count,
165
NULL, NULL ))
166
WARN( "write failed: %lu\n", GetLastError() );
167
console->tty_buffer_count = 0;
168
}
169
170
static void tty_write( struct console *console, const char *buffer, size_t size )
171
{
172
if (!size || !console->tty_output) return;
173
if (console->tty_buffer_count + size > sizeof(console->tty_buffer))
174
tty_flush( console );
175
if (console->tty_buffer_count + size <= sizeof(console->tty_buffer))
176
{
177
memcpy( console->tty_buffer + console->tty_buffer_count, buffer, size );
178
console->tty_buffer_count += size;
179
}
180
else
181
{
182
assert( !console->tty_buffer_count );
183
if (!WriteFile( console->tty_output, buffer, size, NULL, NULL ))
184
WARN( "write failed: %lu\n", GetLastError() );
185
}
186
}
187
188
static void *tty_alloc_buffer( struct console *console, size_t size )
189
{
190
void *ret;
191
if (console->tty_buffer_count + size > sizeof(console->tty_buffer)) return NULL;
192
ret = console->tty_buffer + console->tty_buffer_count;
193
console->tty_buffer_count += size;
194
return ret;
195
}
196
197
static void hide_tty_cursor( struct console *console )
198
{
199
if (console->tty_cursor_visible)
200
{
201
tty_write( console, "\x1b[?25l", 6 );
202
console->tty_cursor_visible = FALSE;
203
}
204
}
205
206
static void set_tty_cursor( struct console *console, unsigned int x, unsigned int y )
207
{
208
char buf[64];
209
210
if (console->tty_cursor_x == x && console->tty_cursor_y == y) return;
211
212
if (!x && y == console->tty_cursor_y + 1) strcpy( buf, "\r\n" );
213
else if (!x && y == console->tty_cursor_y) strcpy( buf, "\r" );
214
else if (y == console->tty_cursor_y)
215
{
216
if (console->tty_cursor_x >= console->active->width)
217
{
218
if (console->is_unix)
219
{
220
/* Unix will usually have the cursor at width-1 in this case. instead of depending
221
* on the exact behaviour, move the cursor to the first column and move forward
222
* from there. */
223
tty_write( console, "\r", 1 );
224
console->tty_cursor_x = 0;
225
}
226
else if (console->active->mode & ENABLE_WRAP_AT_EOL_OUTPUT)
227
{
228
console->tty_cursor_x--;
229
}
230
if (console->tty_cursor_x == x) return;
231
}
232
if (x + 1 == console->tty_cursor_x) strcpy( buf, "\b" );
233
else if (x > console->tty_cursor_x) sprintf( buf, "\x1b[%uC", x - console->tty_cursor_x );
234
else sprintf( buf, "\x1b[%uD", console->tty_cursor_x - x );
235
}
236
else if (x || y)
237
{
238
hide_tty_cursor( console );
239
sprintf( buf, "\x1b[%u;%uH", y + 1, x + 1);
240
}
241
else strcpy( buf, "\x1b[H" );
242
console->tty_cursor_x = x;
243
console->tty_cursor_y = y;
244
tty_write( console, buf, strlen(buf) );
245
}
246
247
static void set_tty_cursor_relative( struct console *console, unsigned int x, unsigned int y )
248
{
249
if (y < console->tty_cursor_y)
250
{
251
char buf[64];
252
sprintf( buf, "\x1b[%uA", console->tty_cursor_y - y );
253
tty_write( console, buf, strlen(buf) );
254
console->tty_cursor_y = y;
255
}
256
else
257
{
258
while (console->tty_cursor_y < y)
259
{
260
console->tty_cursor_x = 0;
261
console->tty_cursor_y++;
262
tty_write( console, "\r\n", 2 );
263
}
264
}
265
set_tty_cursor( console, x, y );
266
}
267
268
static void set_tty_attr( struct console *console, unsigned int attr )
269
{
270
char buf[8];
271
272
if ((attr & 0x0f) != (console->tty_attr & 0x0f))
273
{
274
if ((attr & 0x0f) != 7)
275
{
276
unsigned int n = 30;
277
if (attr & FOREGROUND_BLUE) n += 4;
278
if (attr & FOREGROUND_GREEN) n += 2;
279
if (attr & FOREGROUND_RED) n += 1;
280
if (attr & FOREGROUND_INTENSITY) n += 60;
281
sprintf(buf, "\x1b[%um", n);
282
tty_write( console, buf, strlen(buf) );
283
}
284
else tty_write( console, "\x1b[m", 3 );
285
}
286
287
if ((attr & 0xf0) != (console->tty_attr & 0xf0) && attr != 7)
288
{
289
unsigned int n = 40;
290
if (attr & BACKGROUND_BLUE) n += 4;
291
if (attr & BACKGROUND_GREEN) n += 2;
292
if (attr & BACKGROUND_RED) n += 1;
293
if (attr & BACKGROUND_INTENSITY) n += 60;
294
sprintf(buf, "\x1b[%um", n);
295
tty_write( console, buf, strlen(buf) );
296
}
297
298
console->tty_attr = attr;
299
}
300
301
static void tty_sync( struct console *console )
302
{
303
if (!console->tty_output) return;
304
305
if (console->active->cursor_visible)
306
{
307
set_tty_cursor( console, get_bounded_cursor_x( console->active ), console->active->cursor_y );
308
if (!console->tty_cursor_visible)
309
{
310
tty_write( console, "\x1b[?25h", 6 ); /* show cursor */
311
console->tty_cursor_visible = TRUE;
312
}
313
}
314
else if (console->tty_cursor_visible)
315
hide_tty_cursor( console );
316
tty_flush( console );
317
}
318
319
static void init_tty_output( struct console *console )
320
{
321
if (!console->is_unix)
322
{
323
/* initialize tty output, but don't flush */
324
tty_write( console, "\x1b[2J", 4 ); /* clear screen */
325
set_tty_attr( console, console->active->attr );
326
tty_write( console, "\x1b[H", 3 ); /* move cursor to (0,0) */
327
}
328
else console->tty_attr = empty_char_info.attr;
329
console->tty_cursor_visible = TRUE;
330
}
331
332
/* no longer use relative cursor positioning (legacy API have been used) */
333
static void enter_absolute_mode( struct console *console )
334
{
335
console->use_relative_cursor = 0;
336
}
337
338
static void scroll_to_cursor( struct screen_buffer *screen_buffer )
339
{
340
unsigned int cursor_x = get_bounded_cursor_x( screen_buffer );
341
int w = screen_buffer->win.right - screen_buffer->win.left + 1;
342
int h = screen_buffer->win.bottom - screen_buffer->win.top + 1;
343
344
if (cursor_x < screen_buffer->win.left)
345
screen_buffer->win.left = min( cursor_x, screen_buffer->width - w );
346
else if (cursor_x > screen_buffer->win.right)
347
screen_buffer->win.left = max( cursor_x, w ) - w + 1;
348
screen_buffer->win.right = screen_buffer->win.left + w - 1;
349
350
if (screen_buffer->cursor_y < screen_buffer->win.top)
351
screen_buffer->win.top = min( screen_buffer->cursor_y, screen_buffer->height - h );
352
else if (screen_buffer->cursor_y > screen_buffer->win.bottom)
353
screen_buffer->win.top = max( screen_buffer->cursor_y, h ) - h + 1;
354
screen_buffer->win.bottom = screen_buffer->win.top + h - 1;
355
}
356
357
static void update_output( struct screen_buffer *screen_buffer, RECT *rect )
358
{
359
int x, y, size, trailing_spaces;
360
char_info_t *ch;
361
char buf[8];
362
WCHAR wch;
363
const unsigned int mask = (1u << '\0') | (1u << '\b') | (1u << '\t') | (1u << '\n') | (1u << '\a') | (1u << '\r');
364
365
if (!is_active( screen_buffer ) || rect->top > rect->bottom || rect->right < rect->left)
366
return;
367
368
TRACE( "%s\n", wine_dbgstr_rect( rect ));
369
370
if (screen_buffer->console->window)
371
{
372
update_window_region( screen_buffer->console, rect );
373
return;
374
}
375
if (!screen_buffer->console->tty_output) return;
376
377
hide_tty_cursor( screen_buffer->console );
378
379
for (y = rect->top; y <= rect->bottom; y++)
380
{
381
for (trailing_spaces = 0; trailing_spaces < screen_buffer->width; trailing_spaces++)
382
{
383
ch = &screen_buffer->data[(y + 1) * screen_buffer->width - trailing_spaces - 1];
384
if (ch->ch != ' ' || ch->attr != 7) break;
385
}
386
if (trailing_spaces < 4) trailing_spaces = 0;
387
388
for (x = rect->left; x <= rect->right; x++)
389
{
390
ch = &screen_buffer->data[y * screen_buffer->width + x];
391
set_tty_attr( screen_buffer->console, ch->attr );
392
set_tty_cursor( screen_buffer->console, x, y );
393
394
if (x + trailing_spaces >= screen_buffer->width)
395
{
396
tty_write( screen_buffer->console, "\x1b[K", 3 );
397
break;
398
}
399
wch = ch->ch;
400
if (screen_buffer->console->is_unix && wch < L' ' && mask & (1u << wch))
401
wch = L'?';
402
size = WideCharToMultiByte( get_tty_cp( screen_buffer->console ), 0,
403
&wch, 1, buf, sizeof(buf), NULL, NULL );
404
tty_write( screen_buffer->console, buf, size );
405
screen_buffer->console->tty_cursor_x++;
406
}
407
}
408
409
empty_update_rect( screen_buffer, rect );
410
}
411
412
static void new_line( struct screen_buffer *screen_buffer, RECT *update_rect )
413
{
414
unsigned int i;
415
416
assert( screen_buffer->cursor_y >= screen_buffer->height );
417
screen_buffer->cursor_y = screen_buffer->height - 1;
418
419
if (screen_buffer->console->tty_output)
420
update_output( screen_buffer, update_rect );
421
else
422
SetRect( update_rect, 0, 0, screen_buffer->width - 1, screen_buffer->height - 1 );
423
424
memmove( screen_buffer->data, screen_buffer->data + screen_buffer->width,
425
screen_buffer->width * (screen_buffer->height - 1) * sizeof(*screen_buffer->data) );
426
for (i = 0; i < screen_buffer->width; i++)
427
screen_buffer->data[screen_buffer->width * (screen_buffer->height - 1) + i] = empty_char_info;
428
if (is_active( screen_buffer ))
429
{
430
screen_buffer->console->tty_cursor_y--;
431
if (screen_buffer->console->tty_cursor_y != screen_buffer->height - 2)
432
set_tty_cursor( screen_buffer->console, 0, screen_buffer->height - 2 );
433
set_tty_cursor( screen_buffer->console, 0, screen_buffer->height - 1 );
434
}
435
}
436
437
static void write_char( struct screen_buffer *screen_buffer, WCHAR ch, RECT *update_rect, unsigned int *home_y )
438
{
439
if (screen_buffer->cursor_x == screen_buffer->width)
440
{
441
screen_buffer->cursor_x = 0;
442
screen_buffer->cursor_y++;
443
}
444
if (screen_buffer->cursor_y == screen_buffer->height)
445
{
446
if (home_y)
447
{
448
if (!*home_y) return;
449
(*home_y)--;
450
}
451
new_line( screen_buffer, update_rect );
452
}
453
454
screen_buffer->data[screen_buffer->cursor_y * screen_buffer->width + screen_buffer->cursor_x].ch = ch;
455
screen_buffer->data[screen_buffer->cursor_y * screen_buffer->width + screen_buffer->cursor_x].attr = screen_buffer->attr;
456
update_rect->left = min( update_rect->left, screen_buffer->cursor_x );
457
update_rect->top = min( update_rect->top, screen_buffer->cursor_y );
458
update_rect->right = max( update_rect->right, screen_buffer->cursor_x );
459
update_rect->bottom = max( update_rect->bottom, screen_buffer->cursor_y );
460
screen_buffer->cursor_x++;
461
}
462
463
static NTSTATUS read_complete( struct console *console, NTSTATUS status, const void *buf, size_t size, int signal )
464
{
465
SERVER_START_REQ( get_next_console_request )
466
{
467
req->handle = wine_server_obj_handle( console->server );
468
req->signal = signal;
469
req->read = 1;
470
req->status = status;
471
if (console->read_ioctl == IOCTL_CONDRV_READ_CONSOLE_CONTROL)
472
wine_server_add_data( req, &console->key_state, sizeof(console->key_state) );
473
wine_server_add_data( req, buf, size );
474
status = wine_server_call( req );
475
}
476
SERVER_END_REQ;
477
if (status && (console->read_ioctl || status != STATUS_INVALID_HANDLE)) ERR( "failed: %#lx\n", status );
478
console->signaled = signal;
479
console->read_ioctl = 0;
480
console->pending_read = 0;
481
return status;
482
}
483
484
static NTSTATUS read_console_input( struct console *console, size_t out_size )
485
{
486
size_t count = min( out_size / sizeof(INPUT_RECORD), console->record_count );
487
488
TRACE("count %Iu\n", count);
489
490
read_complete( console, STATUS_SUCCESS, console->records, count * sizeof(*console->records),
491
console->record_count > count );
492
493
if (count < console->record_count)
494
memmove( console->records, console->records + count,
495
(console->record_count - count) * sizeof(*console->records) );
496
console->record_count -= count;
497
return STATUS_SUCCESS;
498
}
499
500
static void read_from_buffer( struct console *console, size_t out_size )
501
{
502
size_t len, read_len = 0;
503
char *buf = NULL;
504
505
switch( console->read_ioctl )
506
{
507
case IOCTL_CONDRV_READ_CONSOLE:
508
case IOCTL_CONDRV_READ_CONSOLE_CONTROL:
509
out_size = min( out_size, console->read_buffer_count * sizeof(WCHAR) );
510
read_complete( console, STATUS_SUCCESS, console->read_buffer, out_size, console->record_count != 0 );
511
read_len = out_size / sizeof(WCHAR);
512
break;
513
case IOCTL_CONDRV_READ_FILE:
514
read_len = len = 0;
515
while (read_len < console->read_buffer_count && len < out_size)
516
{
517
len += WideCharToMultiByte( console->input_cp, 0, console->read_buffer + read_len, 1, NULL, 0, NULL, NULL );
518
read_len++;
519
}
520
if (len)
521
{
522
if (!(buf = malloc( len )))
523
{
524
read_complete( console, STATUS_NO_MEMORY, NULL, 0, console->record_count != 0 );
525
return;
526
}
527
WideCharToMultiByte( console->input_cp, 0, console->read_buffer, read_len, buf, len, NULL, NULL );
528
}
529
len = min( out_size, len );
530
read_complete( console, STATUS_SUCCESS, buf, len, console->record_count != 0 );
531
free( buf );
532
break;
533
}
534
535
if (read_len < console->read_buffer_count)
536
{
537
memmove( console->read_buffer, console->read_buffer + read_len,
538
(console->read_buffer_count - read_len) * sizeof(WCHAR) );
539
}
540
if (!(console->read_buffer_count -= read_len))
541
free( console->read_buffer );
542
}
543
544
static void append_input_history( struct console *console, const WCHAR *str, size_t len )
545
{
546
struct history_line *ptr;
547
548
if (!console->history_size) return;
549
550
/* don't duplicate entry */
551
if (console->history_mode && console->history_index &&
552
console->history[console->history_index - 1]->len == len &&
553
!memcmp( console->history[console->history_index - 1]->text, str, len ))
554
return;
555
556
if (!(ptr = malloc( offsetof( struct history_line, text[len / sizeof(WCHAR)] )))) return;
557
ptr->len = len;
558
memcpy( ptr->text, str, len );
559
560
if (console->history_index < console->history_size)
561
{
562
console->history[console->history_index++] = ptr;
563
}
564
else
565
{
566
free( console->history[0]) ;
567
memmove( &console->history[0], &console->history[1],
568
(console->history_size - 1) * sizeof(*console->history) );
569
console->history[console->history_size - 1] = ptr;
570
}
571
}
572
573
static void edit_line_update( struct console *console, unsigned int begin, unsigned int length )
574
{
575
struct edit_line *ctx = &console->edit_line;
576
if (!length) return;
577
ctx->update_begin = min( ctx->update_begin, begin );
578
ctx->update_end = max( ctx->update_end, begin + length - 1 );
579
}
580
581
static BOOL edit_line_grow( struct console *console, size_t length )
582
{
583
struct edit_line *ctx = &console->edit_line;
584
WCHAR *new_buf;
585
size_t new_size;
586
587
if (ctx->len + length < ctx->size) return TRUE;
588
589
/* round up size to 32 byte-WCHAR boundary */
590
new_size = (ctx->len + length + 32) & ~31;
591
if (!(new_buf = realloc( ctx->buf, sizeof(WCHAR) * new_size )))
592
{
593
ctx->status = STATUS_NO_MEMORY;
594
return FALSE;
595
}
596
ctx->buf = new_buf;
597
ctx->size = new_size;
598
return TRUE;
599
}
600
601
static void edit_line_delete( struct console *console, int begin, int end )
602
{
603
struct edit_line *ctx = &console->edit_line;
604
unsigned int len = end - begin;
605
606
edit_line_update( console, begin, ctx->len - begin );
607
if (end < ctx->len)
608
memmove( &ctx->buf[begin], &ctx->buf[end], (ctx->len - end) * sizeof(WCHAR));
609
ctx->len -= len;
610
edit_line_update( console, 0, ctx->len );
611
ctx->buf[ctx->len] = 0;
612
}
613
614
static void edit_line_insert( struct console *console, const WCHAR *str, unsigned int len )
615
{
616
struct edit_line *ctx = &console->edit_line;
617
unsigned int update_len;
618
619
if (!len) return;
620
if (ctx->insert_mode)
621
{
622
if (!edit_line_grow( console, len )) return;
623
if (ctx->len > ctx->cursor)
624
memmove( &ctx->buf[ctx->cursor + len], &ctx->buf[ctx->cursor],
625
(ctx->len - ctx->cursor) * sizeof(WCHAR) );
626
ctx->len += len;
627
update_len = ctx->len - ctx->cursor;
628
}
629
else
630
{
631
if (ctx->cursor + len > ctx->len)
632
{
633
if (!edit_line_grow( console, (ctx->cursor + len) - ctx->len) )
634
return;
635
ctx->len = ctx->cursor + len;
636
}
637
update_len = len;
638
}
639
memcpy( &ctx->buf[ctx->cursor], str, len * sizeof(WCHAR) );
640
ctx->buf[ctx->len] = 0;
641
edit_line_update( console, ctx->cursor, update_len );
642
ctx->cursor += len;
643
}
644
645
static void edit_line_save_yank( struct console *console, unsigned int begin, unsigned int end )
646
{
647
struct edit_line *ctx = &console->edit_line;
648
unsigned int len = end - begin;
649
if (len <= 0) return;
650
651
free(ctx->yanked);
652
ctx->yanked = malloc( (len + 1) * sizeof(WCHAR) );
653
if (!ctx->yanked)
654
{
655
ctx->status = STATUS_NO_MEMORY;
656
return;
657
}
658
memcpy( ctx->yanked, &ctx->buf[begin], len * sizeof(WCHAR) );
659
ctx->yanked[len] = 0;
660
}
661
662
static int edit_line_left_word_transition( struct console *console, int offset )
663
{
664
offset--;
665
while (offset >= 0 && !iswalnum( console->edit_line.buf[offset] )) offset--;
666
while (offset >= 0 && iswalnum( console->edit_line.buf[offset] )) offset--;
667
if (offset >= 0) offset++;
668
return max( offset, 0 );
669
}
670
671
static int edit_line_right_word_transition( struct console *console, int offset )
672
{
673
offset++;
674
while (offset <= console->edit_line.len && iswalnum( console->edit_line.buf[offset] ))
675
offset++;
676
while (offset <= console->edit_line.len && !iswalnum( console->edit_line.buf[offset] ))
677
offset++;
678
return min(offset, console->edit_line.len);
679
}
680
681
static WCHAR *edit_line_history( struct console *console, unsigned int index )
682
{
683
WCHAR *ptr = NULL;
684
685
if (index < console->history_index)
686
{
687
if ((ptr = malloc( console->history[index]->len + sizeof(WCHAR) )))
688
{
689
memcpy( ptr, console->history[index]->text, console->history[index]->len );
690
ptr[console->history[index]->len / sizeof(WCHAR)] = 0;
691
}
692
}
693
else if(console->edit_line.current_history)
694
{
695
ptr = wcsdup( console->edit_line.current_history );
696
}
697
return ptr;
698
}
699
700
static void edit_line_move_to_history( struct console *console, int index )
701
{
702
struct edit_line *ctx = &console->edit_line;
703
WCHAR *line = edit_line_history(console, index);
704
size_t len = line ? lstrlenW(line) : 0;
705
706
/* save current line edition for recall when needed */
707
if (ctx->history_index == console->history_index)
708
{
709
free( ctx->current_history );
710
ctx->current_history = malloc( (ctx->len + 1) * sizeof(WCHAR) );
711
if (ctx->current_history)
712
{
713
memcpy( ctx->current_history, ctx->buf, (ctx->len + 1) * sizeof(WCHAR) );
714
}
715
else
716
{
717
free( line );
718
ctx->status = STATUS_NO_MEMORY;
719
return;
720
}
721
}
722
723
/* need to clean also the screen if new string is shorter than old one */
724
edit_line_delete(console, 0, ctx->len);
725
ctx->cursor = 0;
726
/* insert new string */
727
if (edit_line_grow(console, len + 1))
728
{
729
edit_line_insert( console, line, len );
730
ctx->history_index = index;
731
}
732
free(line);
733
}
734
735
static void edit_line_find_in_history( struct console *console )
736
{
737
struct edit_line *ctx = &console->edit_line;
738
int start_pos = ctx->history_index;
739
unsigned int len, oldoffset;
740
WCHAR *line;
741
742
if (!console->history_index) return;
743
if (ctx->history_index && ctx->history_index == console->history_index)
744
{
745
start_pos--;
746
ctx->history_index--;
747
}
748
749
do
750
{
751
line = edit_line_history(console, ctx->history_index);
752
753
if (ctx->history_index) ctx->history_index--;
754
else ctx->history_index = console->history_index - 1;
755
756
len = lstrlenW(line) + 1;
757
if (len >= ctx->cursor && !memcmp( ctx->buf, line, ctx->cursor * sizeof(WCHAR) ))
758
{
759
/* need to clean also the screen if new string is shorter than old one */
760
edit_line_delete(console, 0, ctx->len);
761
762
if (edit_line_grow(console, len))
763
{
764
oldoffset = ctx->cursor;
765
ctx->cursor = 0;
766
edit_line_insert( console, line, len - 1 );
767
ctx->cursor = oldoffset;
768
free(line);
769
return;
770
}
771
}
772
free(line);
773
}
774
while (ctx->history_index != start_pos);
775
}
776
777
static void edit_line_copy_from_history( struct console *console, int copycount )
778
{
779
if (console->edit_line.history_index)
780
{
781
struct edit_line *ctx = &console->edit_line;
782
unsigned int index = ctx->history_index - 1;
783
WCHAR *line = console->history[index]->text;
784
unsigned int len = console->history[index]->len / sizeof(WCHAR);
785
786
if (len > ctx->cursor)
787
{
788
unsigned int ccount = (copycount > 0) ? copycount : len - ctx->cursor;
789
790
if (ctx->cursor == ctx->len)
791
{
792
/* Insert new string. */
793
if (edit_line_grow(console, ccount))
794
{
795
edit_line_insert( console, &line[ctx->cursor], ccount );
796
}
797
}
798
else
799
{
800
ctx->cursor += ccount;
801
}
802
}
803
}
804
}
805
806
static void edit_line_copy_one_from_history( struct console *console )
807
{
808
edit_line_copy_from_history(console, 1);
809
}
810
811
static void edit_line_copy_all_from_history( struct console *console )
812
{
813
edit_line_copy_from_history(console, -1);
814
}
815
816
static void edit_line_move_left( struct console *console )
817
{
818
if (console->edit_line.cursor > 0) console->edit_line.cursor--;
819
}
820
821
static void edit_line_move_right( struct console *console )
822
{
823
struct edit_line *ctx = &console->edit_line;
824
if (ctx->cursor < ctx->len) ctx->cursor++;
825
}
826
827
static void edit_line_move_left_word( struct console *console )
828
{
829
console->edit_line.cursor = edit_line_left_word_transition( console, console->edit_line.cursor );
830
}
831
832
static void edit_line_move_right_word( struct console *console )
833
{
834
console->edit_line.cursor = edit_line_right_word_transition( console, console->edit_line.cursor );
835
}
836
837
static void edit_line_move_home( struct console *console )
838
{
839
console->edit_line.cursor = 0;
840
}
841
842
static void edit_line_move_end( struct console *console )
843
{
844
console->edit_line.cursor = console->edit_line.len;
845
}
846
847
static void edit_line_set_mark( struct console *console )
848
{
849
console->edit_line.mark = console->edit_line.cursor;
850
}
851
852
static void edit_line_exchange_mark( struct console *console )
853
{
854
struct edit_line *ctx = &console->edit_line;
855
unsigned int cursor;
856
857
if (ctx->mark > ctx->len) return;
858
cursor = ctx->cursor;
859
ctx->cursor = ctx->mark;
860
ctx->mark = cursor;
861
}
862
863
static void edit_line_copy_marked_zone( struct console *console )
864
{
865
struct edit_line *ctx = &console->edit_line;
866
unsigned int begin, end;
867
868
if (ctx->mark > ctx->len || ctx->mark == ctx->cursor) return;
869
if (ctx->mark > ctx->cursor)
870
{
871
begin = ctx->cursor;
872
end = ctx->mark;
873
}
874
else
875
{
876
begin = ctx->mark;
877
end = ctx->cursor;
878
}
879
edit_line_save_yank( console, begin, end );
880
}
881
882
static void edit_line_transpose_char( struct console *console )
883
{
884
struct edit_line *ctx = &console->edit_line;
885
WCHAR c;
886
887
if (!ctx->cursor || ctx->cursor == ctx->len) return;
888
889
c = ctx->buf[ctx->cursor];
890
ctx->buf[ctx->cursor] = ctx->buf[ctx->cursor - 1];
891
ctx->buf[ctx->cursor - 1] = c;
892
893
edit_line_update( console, ctx->cursor - 1, 2 );
894
ctx->cursor++;
895
}
896
897
static void edit_line_transpose_words( struct console *console )
898
{
899
struct edit_line *ctx = &console->edit_line;
900
unsigned int left_offset = edit_line_left_word_transition( console, ctx->cursor );
901
unsigned int right_offset = edit_line_right_word_transition( console, ctx->cursor );
902
if (left_offset < ctx->cursor && right_offset > ctx->cursor)
903
{
904
unsigned int len_r = right_offset - ctx->cursor;
905
unsigned int len_l = ctx->cursor - left_offset;
906
WCHAR *tmp = malloc( len_r * sizeof(WCHAR) );
907
if (!tmp)
908
{
909
ctx->status = STATUS_NO_MEMORY;
910
return;
911
}
912
913
memcpy( tmp, &ctx->buf[ctx->cursor], len_r * sizeof(WCHAR) );
914
memmove( &ctx->buf[left_offset + len_r], &ctx->buf[left_offset],
915
len_l * sizeof(WCHAR) );
916
memcpy( &ctx->buf[left_offset], tmp, len_r * sizeof(WCHAR) );
917
free(tmp);
918
919
edit_line_update( console, left_offset, len_l + len_r );
920
ctx->cursor = right_offset;
921
}
922
}
923
924
static void edit_line_lower_case_word( struct console *console )
925
{
926
struct edit_line *ctx = &console->edit_line;
927
unsigned int new_offset = edit_line_right_word_transition( console, ctx->cursor );
928
if (new_offset != ctx->cursor)
929
{
930
CharLowerBuffW( ctx->buf + ctx->cursor, new_offset - ctx->cursor + 1 );
931
edit_line_update( console, ctx->cursor, new_offset - ctx->cursor + 1 );
932
ctx->cursor = new_offset;
933
}
934
}
935
936
static void edit_line_upper_case_word( struct console *console )
937
{
938
struct edit_line *ctx = &console->edit_line;
939
unsigned int new_offset = edit_line_right_word_transition( console, ctx->cursor );
940
if (new_offset != ctx->cursor)
941
{
942
CharUpperBuffW( ctx->buf + ctx->cursor, new_offset - ctx->cursor + 1 );
943
edit_line_update( console, ctx->cursor, new_offset - ctx->cursor + 1 );
944
ctx->cursor = new_offset;
945
}
946
}
947
948
static void edit_line_capitalize_word( struct console *console )
949
{
950
struct edit_line *ctx = &console->edit_line;
951
unsigned int new_offset = edit_line_right_word_transition( console, ctx->cursor );
952
if (new_offset != ctx->cursor)
953
{
954
CharUpperBuffW( ctx->buf + ctx->cursor, 1 );
955
CharLowerBuffW( ctx->buf + ctx->cursor + 1, new_offset - ctx->cursor );
956
edit_line_update( console, ctx->cursor, new_offset - ctx->cursor + 1 );
957
ctx->cursor = new_offset;
958
}
959
}
960
961
static void edit_line_yank( struct console *console )
962
{
963
struct edit_line *ctx = &console->edit_line;
964
if (ctx->yanked) edit_line_insert( console, ctx->yanked, wcslen(ctx->yanked) );
965
}
966
967
static void edit_line_kill_suffix( struct console *console )
968
{
969
struct edit_line *ctx = &console->edit_line;
970
edit_line_save_yank( console, ctx->cursor, ctx->len );
971
edit_line_delete( console, ctx->cursor, ctx->len );
972
}
973
974
static void edit_line_kill_prefix( struct console *console )
975
{
976
struct edit_line *ctx = &console->edit_line;
977
if (ctx->cursor)
978
{
979
edit_line_save_yank( console, 0, ctx->cursor );
980
edit_line_delete( console, 0, ctx->cursor );
981
ctx->cursor = 0;
982
}
983
}
984
985
static void edit_line_clear( struct console *console )
986
{
987
struct edit_line *ctx = &console->edit_line;
988
edit_line_delete( console, 0, ctx->len );
989
ctx->cursor = 0;
990
}
991
992
static void edit_line_kill_marked_zone( struct console *console )
993
{
994
struct edit_line *ctx = &console->edit_line;
995
unsigned int begin, end;
996
997
if (ctx->mark > ctx->len || ctx->mark == ctx->cursor)
998
return;
999
if (ctx->mark > ctx->cursor)
1000
{
1001
begin = ctx->cursor;
1002
end = ctx->mark;
1003
}
1004
else
1005
{
1006
begin = ctx->mark;
1007
end = ctx->cursor;
1008
}
1009
edit_line_save_yank( console, begin, end );
1010
edit_line_delete( console, begin, end );
1011
ctx->cursor = begin;
1012
}
1013
1014
static void edit_line_delete_prev( struct console *console )
1015
{
1016
struct edit_line *ctx = &console->edit_line;
1017
if (ctx->cursor)
1018
{
1019
edit_line_delete( console, ctx->cursor - 1, ctx->cursor );
1020
ctx->cursor--;
1021
}
1022
}
1023
1024
static void edit_line_delete_char( struct console *console )
1025
{
1026
struct edit_line *ctx = &console->edit_line;
1027
if (ctx->cursor < ctx->len)
1028
edit_line_delete( console, ctx->cursor, ctx->cursor + 1 );
1029
}
1030
1031
static void edit_line_delete_left_word( struct console *console )
1032
{
1033
struct edit_line *ctx = &console->edit_line;
1034
unsigned int new_offset = edit_line_left_word_transition( console, ctx->cursor );
1035
if (new_offset != ctx->cursor)
1036
{
1037
edit_line_delete( console, new_offset, ctx->cursor );
1038
ctx->cursor = new_offset;
1039
}
1040
}
1041
1042
static void edit_line_delete_right_word( struct console *console )
1043
{
1044
struct edit_line *ctx = &console->edit_line;
1045
unsigned int new_offset = edit_line_right_word_transition( console, ctx->cursor );
1046
if (new_offset != ctx->cursor)
1047
{
1048
edit_line_delete( console, ctx->cursor, new_offset );
1049
}
1050
}
1051
1052
static void edit_line_move_to_prev_hist( struct console *console )
1053
{
1054
if (console->edit_line.history_index)
1055
edit_line_move_to_history( console, console->edit_line.history_index - 1 );
1056
}
1057
1058
static void edit_line_move_to_next_hist( struct console *console )
1059
{
1060
if (console->edit_line.history_index < console->history_index)
1061
edit_line_move_to_history( console, console->edit_line.history_index + 1 );
1062
}
1063
1064
static void edit_line_move_to_first_hist( struct console *console )
1065
{
1066
if (console->edit_line.history_index)
1067
edit_line_move_to_history( console, 0 );
1068
}
1069
1070
static void edit_line_move_to_last_hist( struct console *console )
1071
{
1072
if (console->edit_line.history_index != console->history_index)
1073
edit_line_move_to_history( console, console->history_index );
1074
}
1075
1076
static void edit_line_redraw( struct console *console )
1077
{
1078
if (console->mode & ENABLE_ECHO_INPUT)
1079
edit_line_update( console, 0, console->edit_line.len );
1080
}
1081
1082
static void edit_line_toggle_insert( struct console *console )
1083
{
1084
struct edit_line *ctx = &console->edit_line;
1085
ctx->insert_key = !ctx->insert_key;
1086
console->active->cursor_size = ctx->insert_key ? 100 : 25;
1087
}
1088
1089
static void edit_line_done( struct console *console )
1090
{
1091
console->edit_line.status = STATUS_SUCCESS;
1092
}
1093
1094
struct edit_line_key_entry
1095
{
1096
WCHAR val; /* vk or unicode char */
1097
void (*func)( struct console *console );
1098
};
1099
1100
struct edit_line_key_map
1101
{
1102
DWORD key_state; /* keyState (from INPUT_RECORD) to match */
1103
BOOL is_char; /* check vk or char */
1104
const struct edit_line_key_entry *entries;
1105
};
1106
1107
#define CTRL(x) ((x) - '@')
1108
static const struct edit_line_key_entry std_key_map[] =
1109
{
1110
{ VK_BACK, edit_line_delete_prev },
1111
{ VK_RETURN, edit_line_done },
1112
{ VK_DELETE, edit_line_delete_char },
1113
{ 0 }
1114
};
1115
1116
static const struct edit_line_key_entry emacs_key_map_ctrl[] =
1117
{
1118
{ CTRL('@'), edit_line_set_mark },
1119
{ CTRL('A'), edit_line_move_home },
1120
{ CTRL('B'), edit_line_move_left },
1121
{ CTRL('D'), edit_line_delete_char },
1122
{ CTRL('E'), edit_line_move_end },
1123
{ CTRL('F'), edit_line_move_right },
1124
{ CTRL('H'), edit_line_delete_prev },
1125
{ CTRL('J'), edit_line_done },
1126
{ CTRL('K'), edit_line_kill_suffix },
1127
{ CTRL('L'), edit_line_redraw },
1128
{ CTRL('M'), edit_line_done },
1129
{ CTRL('N'), edit_line_move_to_next_hist },
1130
{ CTRL('P'), edit_line_move_to_prev_hist },
1131
{ CTRL('T'), edit_line_transpose_char },
1132
{ CTRL('W'), edit_line_kill_marked_zone },
1133
{ CTRL('X'), edit_line_exchange_mark },
1134
{ CTRL('Y'), edit_line_yank },
1135
{ 0 }
1136
};
1137
1138
static const struct edit_line_key_entry emacs_key_map_alt[] =
1139
{
1140
{ 0x7f, edit_line_delete_left_word },
1141
{ '<', edit_line_move_to_first_hist },
1142
{ '>', edit_line_move_to_last_hist },
1143
{ 'b', edit_line_move_left_word },
1144
{ 'c', edit_line_capitalize_word },
1145
{ 'd', edit_line_delete_right_word },
1146
{ 'f', edit_line_move_right_word },
1147
{ 'l', edit_line_lower_case_word },
1148
{ 't', edit_line_transpose_words },
1149
{ 'u', edit_line_upper_case_word },
1150
{ 'w', edit_line_copy_marked_zone },
1151
{ 0 }
1152
};
1153
1154
static const struct edit_line_key_entry emacs_std_key_map[] =
1155
{
1156
{ VK_PRIOR, edit_line_move_to_prev_hist },
1157
{ VK_NEXT, edit_line_move_to_next_hist },
1158
{ VK_END, edit_line_move_end },
1159
{ VK_HOME, edit_line_move_home },
1160
{ VK_RIGHT, edit_line_move_right },
1161
{ VK_LEFT, edit_line_move_left },
1162
{ VK_INSERT, edit_line_toggle_insert },
1163
{ 0 }
1164
};
1165
1166
static const struct edit_line_key_map emacs_key_map[] =
1167
{
1168
{ 0, 0, std_key_map },
1169
{ 0, 0, emacs_std_key_map },
1170
{ RIGHT_ALT_PRESSED, 1, emacs_key_map_alt },
1171
{ LEFT_ALT_PRESSED, 1, emacs_key_map_alt },
1172
{ RIGHT_CTRL_PRESSED, 1, emacs_key_map_ctrl },
1173
{ LEFT_CTRL_PRESSED, 1, emacs_key_map_ctrl },
1174
{ 0 }
1175
};
1176
1177
static const struct edit_line_key_entry win32_std_key_map[] =
1178
{
1179
{ VK_LEFT, edit_line_move_left },
1180
{ VK_RIGHT, edit_line_move_right },
1181
{ VK_HOME, edit_line_move_home },
1182
{ VK_END, edit_line_move_end },
1183
{ VK_UP, edit_line_move_to_prev_hist },
1184
{ VK_DOWN, edit_line_move_to_next_hist },
1185
{ VK_INSERT, edit_line_toggle_insert },
1186
{ VK_F8, edit_line_find_in_history },
1187
{ VK_ESCAPE, edit_line_clear },
1188
{ VK_F1, edit_line_copy_one_from_history },
1189
{ VK_F3, edit_line_copy_all_from_history },
1190
{ 0 }
1191
};
1192
1193
static const struct edit_line_key_entry win32_key_map_ctrl[] =
1194
{
1195
{ VK_LEFT, edit_line_move_left_word },
1196
{ VK_RIGHT, edit_line_move_right_word },
1197
{ VK_END, edit_line_kill_suffix },
1198
{ VK_HOME, edit_line_kill_prefix },
1199
{ 'M', edit_line_done },
1200
{ 0 }
1201
};
1202
1203
static const struct edit_line_key_map win32_key_map[] =
1204
{
1205
{ 0, 0, std_key_map },
1206
{ SHIFT_PRESSED, 0, std_key_map },
1207
{ 0, 0, win32_std_key_map },
1208
{ RIGHT_CTRL_PRESSED, 0, win32_key_map_ctrl },
1209
{ LEFT_CTRL_PRESSED, 0, win32_key_map_ctrl },
1210
{ 0 }
1211
};
1212
#undef CTRL
1213
1214
static unsigned int edit_line_string_width( const WCHAR *str, unsigned int len)
1215
{
1216
unsigned int i, offset = 0;
1217
for (i = 0; i < len; i++) offset += str[i] < ' ' ? 2 : 1;
1218
return offset;
1219
}
1220
1221
static void update_read_output( struct console *console, BOOL newline )
1222
{
1223
struct screen_buffer *screen_buffer = console->active;
1224
struct edit_line *ctx = &console->edit_line;
1225
int offset = 0, j, end_offset;
1226
RECT update_rect;
1227
1228
empty_update_rect( screen_buffer, &update_rect );
1229
1230
if (ctx->update_end >= ctx->update_begin)
1231
{
1232
TRACE( "update %d-%d %s\n", ctx->update_begin, ctx->update_end,
1233
debugstr_wn( ctx->buf + ctx->update_begin, ctx->update_end - ctx->update_begin + 1 ));
1234
1235
hide_tty_cursor( screen_buffer->console );
1236
1237
offset = edit_line_string_width( ctx->buf, ctx->update_begin );
1238
screen_buffer->cursor_x = (ctx->home_x + offset) % screen_buffer->width;
1239
screen_buffer->cursor_y = ctx->home_y + (ctx->home_x + offset) / screen_buffer->width;
1240
for (j = ctx->update_begin; j <= ctx->update_end; j++)
1241
{
1242
if (screen_buffer->cursor_y >= screen_buffer->height && !ctx->home_y) break;
1243
if (j >= ctx->len) break;
1244
if (ctx->buf[j] < ' ')
1245
{
1246
write_char( screen_buffer, '^', &update_rect, &ctx->home_y );
1247
write_char( screen_buffer, '@' + ctx->buf[j], &update_rect, &ctx->home_y );
1248
offset += 2;
1249
}
1250
else
1251
{
1252
write_char( screen_buffer, ctx->buf[j], &update_rect, &ctx->home_y );
1253
offset++;
1254
}
1255
}
1256
end_offset = ctx->end_offset;
1257
ctx->end_offset = offset;
1258
if (j >= ctx->len)
1259
{
1260
/* clear trailing characters if buffer was shortened */
1261
while (offset < end_offset && screen_buffer->cursor_y < screen_buffer->height)
1262
{
1263
write_char( screen_buffer, ' ', &update_rect, &ctx->home_y );
1264
offset++;
1265
}
1266
}
1267
}
1268
1269
if (newline)
1270
{
1271
offset = edit_line_string_width( ctx->buf, ctx->len );
1272
screen_buffer->cursor_x = 0;
1273
screen_buffer->cursor_y = ctx->home_y + (ctx->home_x + offset) / screen_buffer->width;
1274
if (++screen_buffer->cursor_y >= screen_buffer->height)
1275
new_line( screen_buffer, &update_rect );
1276
}
1277
else
1278
{
1279
offset = edit_line_string_width( ctx->buf, ctx->cursor );
1280
screen_buffer->cursor_y = ctx->home_y + (ctx->home_x + offset) / screen_buffer->width;
1281
if (screen_buffer->cursor_y < screen_buffer->height)
1282
{
1283
screen_buffer->cursor_x = (ctx->home_x + offset) % screen_buffer->width;
1284
}
1285
else
1286
{
1287
screen_buffer->cursor_x = screen_buffer->width - 1;
1288
screen_buffer->cursor_y = screen_buffer->height - 1;
1289
}
1290
}
1291
1292
/* always try to use relative cursor positions in UNIX mode so that it works even if cursor
1293
* position is out of sync */
1294
if (update_rect.left <= update_rect.right && update_rect.top <= update_rect.bottom)
1295
{
1296
if (console->is_unix)
1297
set_tty_cursor_relative( screen_buffer->console, update_rect.left, update_rect.top );
1298
update_output( screen_buffer, &update_rect );
1299
scroll_to_cursor( screen_buffer );
1300
}
1301
if (console->is_unix)
1302
set_tty_cursor_relative( screen_buffer->console, screen_buffer->cursor_x, screen_buffer->cursor_y );
1303
tty_sync( screen_buffer->console );
1304
update_window_config( screen_buffer->console, TRUE );
1305
}
1306
1307
/* can end on any ctrl-character: from 0x00 up to 0x1F) */
1308
#define FIRST_NON_CONTROL_CHAR (L' ')
1309
1310
static NTSTATUS process_console_input( struct console *console )
1311
{
1312
struct edit_line *ctx = &console->edit_line;
1313
unsigned int i;
1314
WCHAR ctrl_value = FIRST_NON_CONTROL_CHAR;
1315
unsigned int ctrl_keyvalue = 0;
1316
1317
switch (console->read_ioctl)
1318
{
1319
case IOCTL_CONDRV_READ_INPUT:
1320
if (console->record_count) read_console_input( console, console->pending_read );
1321
return STATUS_SUCCESS;
1322
case IOCTL_CONDRV_READ_CONSOLE:
1323
case IOCTL_CONDRV_READ_CONSOLE_CONTROL:
1324
case IOCTL_CONDRV_READ_FILE:
1325
break;
1326
default:
1327
assert( !console->read_ioctl );
1328
if (console->record_count && !console->signaled)
1329
read_complete( console, STATUS_PENDING, NULL, 0, TRUE ); /* signal server */
1330
return STATUS_SUCCESS;
1331
}
1332
1333
ctx->update_begin = ctx->len + 1;
1334
ctx->update_end = 0;
1335
1336
for (i = 0; i < console->record_count && ctx->status == STATUS_PENDING; i++)
1337
{
1338
void (*func)( struct console *console ) = NULL;
1339
INPUT_RECORD ir = console->records[i];
1340
1341
if (ir.EventType != KEY_EVENT || !ir.Event.KeyEvent.bKeyDown) continue;
1342
1343
TRACE( "key code=%02x scan=%02x char=%02x state=%08lx\n",
1344
ir.Event.KeyEvent.wVirtualKeyCode, ir.Event.KeyEvent.wVirtualScanCode,
1345
ir.Event.KeyEvent.uChar.UnicodeChar, ir.Event.KeyEvent.dwControlKeyState );
1346
1347
if (console->mode & ENABLE_LINE_INPUT)
1348
{
1349
const struct edit_line_key_entry *entry;
1350
const struct edit_line_key_map *map;
1351
unsigned int state;
1352
1353
/* mask out some bits which don't interest us */
1354
state = ir.Event.KeyEvent.dwControlKeyState & ~(NUMLOCK_ON|SCROLLLOCK_ON|CAPSLOCK_ON|ENHANCED_KEY);
1355
1356
if (ctx->ctrl_mask &&
1357
ir.Event.KeyEvent.uChar.UnicodeChar &&
1358
ir.Event.KeyEvent.uChar.UnicodeChar < FIRST_NON_CONTROL_CHAR)
1359
{
1360
if (ctx->ctrl_mask & (1u << ir.Event.KeyEvent.uChar.UnicodeChar))
1361
{
1362
ctrl_value = ir.Event.KeyEvent.uChar.UnicodeChar;
1363
ctrl_keyvalue = ir.Event.KeyEvent.dwControlKeyState;
1364
ctx->status = STATUS_SUCCESS;
1365
TRACE("Found ctrl char in mask: ^%c %x\n", ir.Event.KeyEvent.uChar.UnicodeChar + '@', ctx->ctrl_mask);
1366
continue;
1367
}
1368
if (ir.Event.KeyEvent.uChar.UnicodeChar == 10) continue;
1369
}
1370
func = NULL;
1371
for (map = console->edition_mode ? emacs_key_map : win32_key_map; map->entries != NULL; map++)
1372
{
1373
if (map->key_state != state)
1374
continue;
1375
if (map->is_char)
1376
{
1377
for (entry = &map->entries[0]; entry->func != 0; entry++)
1378
if (entry->val == ir.Event.KeyEvent.uChar.UnicodeChar) break;
1379
}
1380
else
1381
{
1382
for (entry = &map->entries[0]; entry->func != 0; entry++)
1383
if (entry->val == ir.Event.KeyEvent.wVirtualKeyCode) break;
1384
1385
}
1386
if (entry->func)
1387
{
1388
func = entry->func;
1389
break;
1390
}
1391
}
1392
}
1393
1394
ctx->insert_mode = ((console->mode & (ENABLE_INSERT_MODE | ENABLE_EXTENDED_FLAGS)) ==
1395
(ENABLE_INSERT_MODE | ENABLE_EXTENDED_FLAGS))
1396
^ ctx->insert_key;
1397
1398
if (func) func( console );
1399
else if (ir.Event.KeyEvent.uChar.UnicodeChar)
1400
edit_line_insert( console, &ir.Event.KeyEvent.uChar.UnicodeChar, 1 );
1401
1402
if (!(console->mode & ENABLE_LINE_INPUT) && ctx->status == STATUS_PENDING)
1403
{
1404
if (console->read_ioctl == IOCTL_CONDRV_READ_FILE)
1405
{
1406
if (WideCharToMultiByte(console->input_cp, 0, ctx->buf, ctx->len, NULL, 0, NULL, NULL)
1407
>= console->pending_read)
1408
ctx->status = STATUS_SUCCESS;
1409
}
1410
else if (ctx->len >= console->pending_read / sizeof(WCHAR))
1411
ctx->status = STATUS_SUCCESS;
1412
}
1413
}
1414
1415
if (console->record_count > i) memmove( console->records, console->records + i,
1416
(console->record_count - i) * sizeof(*console->records) );
1417
console->record_count -= i;
1418
1419
if (ctx->status == STATUS_PENDING && !(console->mode & ENABLE_LINE_INPUT) && ctx->len)
1420
ctx->status = STATUS_SUCCESS;
1421
1422
if (console->mode & ENABLE_ECHO_INPUT) update_read_output( console, !ctx->status && ctrl_value == FIRST_NON_CONTROL_CHAR );
1423
if (ctx->status == STATUS_PENDING) return STATUS_SUCCESS;
1424
1425
if (!ctx->status && (console->mode & ENABLE_LINE_INPUT))
1426
{
1427
if (ctrl_value < FIRST_NON_CONTROL_CHAR)
1428
{
1429
edit_line_insert( console, &ctrl_value, 1 );
1430
console->key_state = ctrl_keyvalue;
1431
}
1432
else
1433
{
1434
if (ctx->len) append_input_history( console, ctx->buf, ctx->len * sizeof(WCHAR) );
1435
if (edit_line_grow(console, 2))
1436
{
1437
ctx->buf[ctx->len++] = '\r';
1438
ctx->buf[ctx->len++] = '\n';
1439
ctx->buf[ctx->len] = 0;
1440
}
1441
}
1442
TRACE( "return %s\n", debugstr_wn( ctx->buf, ctx->len ));
1443
}
1444
1445
console->read_buffer = ctx->buf;
1446
console->read_buffer_count = ctx->len;
1447
console->read_buffer_size = ctx->size;
1448
1449
if (ctx->status) read_complete( console, ctx->status, NULL, 0, console->record_count );
1450
else read_from_buffer( console, console->pending_read );
1451
1452
/* reset context */
1453
free( ctx->yanked );
1454
free( ctx->current_history );
1455
memset( &console->edit_line, 0, sizeof(console->edit_line) );
1456
return STATUS_SUCCESS;
1457
}
1458
1459
static NTSTATUS read_console( struct console *console, unsigned int ioctl, size_t out_size,
1460
const WCHAR *initial, unsigned int initial_len, unsigned int ctrl_mask )
1461
{
1462
struct edit_line *ctx = &console->edit_line;
1463
TRACE("\n");
1464
1465
if (out_size > INT_MAX)
1466
{
1467
read_complete( console, STATUS_NO_MEMORY, NULL, 0, console->record_count );
1468
return STATUS_NO_MEMORY;
1469
}
1470
1471
console->read_ioctl = ioctl;
1472
console->key_state = 0;
1473
if (!out_size || console->read_buffer_count)
1474
{
1475
read_from_buffer( console, out_size );
1476
return STATUS_SUCCESS;
1477
}
1478
1479
ctx->history_index = console->history_index;
1480
ctx->home_x = console->active->cursor_x;
1481
ctx->home_y = console->active->cursor_y;
1482
ctx->status = STATUS_PENDING;
1483
if (initial_len && edit_line_grow( console, initial_len + 1 ))
1484
{
1485
unsigned offset = edit_line_string_width( initial, initial_len );
1486
if (offset > ctx->home_x)
1487
{
1488
int deltay;
1489
offset -= ctx->home_x + 1;
1490
deltay = offset / console->active->width + 1;
1491
if (ctx->home_y >= deltay)
1492
ctx->home_y -= deltay;
1493
else
1494
{
1495
ctx->home_y = 0;
1496
FIXME("Support for negative ordinates is missing\n");
1497
}
1498
ctx->home_x = console->active->width - 1 - (offset % console->active->width);
1499
}
1500
else
1501
ctx->home_x -= offset;
1502
ctx->cursor = initial_len;
1503
memcpy( ctx->buf, initial, initial_len * sizeof(WCHAR) );
1504
ctx->buf[initial_len] = 0;
1505
ctx->len = initial_len;
1506
ctx->end_offset = initial_len;
1507
}
1508
else if (edit_line_grow( console, 1 )) ctx->buf[0] = 0;
1509
ctx->ctrl_mask = ctrl_mask;
1510
1511
console->pending_read = out_size;
1512
return process_console_input( console );
1513
}
1514
1515
static BOOL map_to_ctrlevent( struct console *console, const INPUT_RECORD *record,
1516
unsigned int* event)
1517
{
1518
if (record->EventType == KEY_EVENT)
1519
{
1520
if ((console->mode & ENABLE_PROCESSED_INPUT) &&
1521
record->Event.KeyEvent.uChar.UnicodeChar == 'C' - 64 &&
1522
!(record->Event.KeyEvent.dwControlKeyState & ENHANCED_KEY))
1523
{
1524
*event = CTRL_C_EVENT;
1525
return TRUE;
1526
}
1527
/* we want to get ctrl-pause/break, but it's already translated by user32 into VK_CANCEL */
1528
if (record->Event.KeyEvent.uChar.UnicodeChar == 0 &&
1529
record->Event.KeyEvent.wVirtualKeyCode == VK_CANCEL &&
1530
record->Event.KeyEvent.dwControlKeyState == LEFT_CTRL_PRESSED)
1531
{
1532
*event = CTRL_BREAK_EVENT;
1533
return TRUE;
1534
}
1535
}
1536
return FALSE;
1537
}
1538
1539
/* add input events to a console input queue */
1540
NTSTATUS write_console_input( struct console *console, const INPUT_RECORD *records,
1541
unsigned int count, BOOL flush )
1542
{
1543
unsigned int i;
1544
1545
TRACE( "%u\n", count );
1546
1547
if (!count) return STATUS_SUCCESS;
1548
if (console->record_count + count > console->record_size)
1549
{
1550
INPUT_RECORD *new_rec;
1551
if (!(new_rec = realloc( console->records, (console->record_size * 2 + count) * sizeof(INPUT_RECORD) )))
1552
return STATUS_NO_MEMORY;
1553
console->records = new_rec;
1554
console->record_size = console->record_size * 2 + count;
1555
}
1556
1557
for (i = 0; i < count; i++)
1558
{
1559
unsigned int event;
1560
1561
if (map_to_ctrlevent( console, &records[i], &event ))
1562
{
1563
if (records[i].Event.KeyEvent.bKeyDown)
1564
{
1565
struct condrv_ctrl_event ctrl_event;
1566
IO_STATUS_BLOCK io;
1567
1568
ctrl_event.event = event;
1569
ctrl_event.group_id = 0;
1570
NtDeviceIoControlFile( console->server, NULL, NULL, NULL, &io, IOCTL_CONDRV_CTRL_EVENT,
1571
&ctrl_event, sizeof(ctrl_event), NULL, 0 );
1572
}
1573
}
1574
else
1575
console->records[console->record_count++] = records[i];
1576
}
1577
1578
return flush ? process_console_input( console ) : STATUS_SUCCESS;
1579
}
1580
1581
static void set_key_input_record( INPUT_RECORD *record, WCHAR ch, unsigned int vk, BOOL is_down, unsigned int ctrl_state )
1582
{
1583
record->EventType = KEY_EVENT;
1584
record->Event.KeyEvent.bKeyDown = is_down;
1585
record->Event.KeyEvent.wRepeatCount = 1;
1586
record->Event.KeyEvent.uChar.UnicodeChar = ch;
1587
record->Event.KeyEvent.wVirtualKeyCode = vk;
1588
record->Event.KeyEvent.wVirtualScanCode = MapVirtualKeyW( vk, MAPVK_VK_TO_VSC );
1589
record->Event.KeyEvent.dwControlKeyState = ctrl_state;
1590
}
1591
1592
static NTSTATUS key_press( struct console *console, WCHAR ch, unsigned int vk, unsigned int ctrl_state )
1593
{
1594
INPUT_RECORD records[8];
1595
unsigned int count = 0, ctrl = 0;
1596
1597
if (ctrl_state & SHIFT_PRESSED)
1598
{
1599
ctrl |= SHIFT_PRESSED;
1600
set_key_input_record( &records[count++], 0, VK_SHIFT, TRUE, ctrl );
1601
}
1602
if (ctrl_state & LEFT_ALT_PRESSED)
1603
{
1604
ctrl |= LEFT_ALT_PRESSED;
1605
set_key_input_record( &records[count++], 0, VK_MENU, TRUE, ctrl );
1606
}
1607
if (ctrl_state & LEFT_CTRL_PRESSED)
1608
{
1609
ctrl |= LEFT_CTRL_PRESSED;
1610
set_key_input_record( &records[count++], 0, VK_CONTROL, TRUE, ctrl );
1611
}
1612
1613
set_key_input_record( &records[count++], ch, vk, TRUE, ctrl );
1614
set_key_input_record( &records[count++], ch, vk, FALSE, ctrl );
1615
1616
if (ctrl & LEFT_CTRL_PRESSED)
1617
{
1618
ctrl &= ~LEFT_CTRL_PRESSED;
1619
set_key_input_record( &records[count++], 0, VK_CONTROL, FALSE, ctrl );
1620
}
1621
if (ctrl & LEFT_ALT_PRESSED)
1622
{
1623
ctrl &= ~LEFT_ALT_PRESSED;
1624
set_key_input_record( &records[count++], 0, VK_MENU, FALSE, ctrl );
1625
}
1626
if (ctrl & SHIFT_PRESSED)
1627
{
1628
ctrl &= ~SHIFT_PRESSED;
1629
set_key_input_record( &records[count++], 0, VK_SHIFT, FALSE, ctrl );
1630
}
1631
1632
return write_console_input( console, records, count, FALSE );
1633
}
1634
1635
static void char_key_press( struct console *console, WCHAR ch, unsigned int ctrl )
1636
{
1637
unsigned int vk = VkKeyScanW( ch );
1638
if (vk == ~0) vk = 0;
1639
if (vk & 0x0100) ctrl |= SHIFT_PRESSED;
1640
if (vk & 0x0200) ctrl |= LEFT_CTRL_PRESSED;
1641
if (vk & 0x0400) ctrl |= LEFT_ALT_PRESSED;
1642
vk &= 0xff;
1643
key_press( console, ch, vk, ctrl );
1644
}
1645
1646
static unsigned int escape_char_to_vk( WCHAR ch, unsigned int *ctrl, WCHAR *outuch )
1647
{
1648
if (ctrl) *ctrl = 0;
1649
if (outuch) *outuch = '\0';
1650
1651
switch (ch)
1652
{
1653
case 'A': return VK_UP;
1654
case 'B': return VK_DOWN;
1655
case 'C': return VK_RIGHT;
1656
case 'D': return VK_LEFT;
1657
case 'H': return VK_HOME;
1658
case 'F': return VK_END;
1659
case 'P': return VK_F1;
1660
case 'Q': return VK_F2;
1661
case 'R': return VK_F3;
1662
case 'S': return VK_F4;
1663
case 'Z': if (ctrl && outuch) {*ctrl = SHIFT_PRESSED; *outuch = '\t'; return VK_TAB;}
1664
return 0;
1665
default: return 0;
1666
}
1667
}
1668
1669
static unsigned int escape_number_to_vk( unsigned int n )
1670
{
1671
switch(n)
1672
{
1673
case 2: return VK_INSERT;
1674
case 3: return VK_DELETE;
1675
case 5: return VK_PRIOR;
1676
case 6: return VK_NEXT;
1677
case 15: return VK_F5;
1678
case 17: return VK_F6;
1679
case 18: return VK_F7;
1680
case 19: return VK_F8;
1681
case 20: return VK_F9;
1682
case 21: return VK_F10;
1683
case 23: return VK_F11;
1684
case 24: return VK_F12;
1685
default: return 0;
1686
}
1687
}
1688
1689
static unsigned int convert_modifiers( unsigned int n )
1690
{
1691
unsigned int ctrl = 0;
1692
if (!n || n > 16) return 0;
1693
n--;
1694
if (n & 1) ctrl |= SHIFT_PRESSED;
1695
if (n & 2) ctrl |= LEFT_ALT_PRESSED;
1696
if (n & 4) ctrl |= LEFT_CTRL_PRESSED;
1697
return ctrl;
1698
}
1699
1700
static unsigned int process_csi_sequence( struct console *console, const WCHAR *buf, size_t size )
1701
{
1702
unsigned int n, count = 0, params[8], params_cnt = 0, vk, ctrl;
1703
WCHAR outuch;
1704
1705
for (;;)
1706
{
1707
n = 0;
1708
while (count < size && '0' <= buf[count] && buf[count] <= '9')
1709
n = n * 10 + buf[count++] - '0';
1710
if (params_cnt < ARRAY_SIZE(params)) params[params_cnt++] = n;
1711
else FIXME( "too many params, skipping %u\n", n );
1712
if (count == size) return 0;
1713
if (buf[count] != ';') break;
1714
if (++count == size) return 0;
1715
}
1716
1717
if ((vk = escape_char_to_vk( buf[count], &ctrl, &outuch )))
1718
{
1719
key_press( console, outuch, vk, params_cnt >= 2 ? convert_modifiers( params[1] ) : ctrl );
1720
return count + 1;
1721
}
1722
1723
switch (buf[count])
1724
{
1725
case '~':
1726
vk = escape_number_to_vk( params[0] );
1727
key_press( console, 0, vk, params_cnt == 2 ? convert_modifiers( params[1] ) : 0 );
1728
return count + 1;
1729
1730
default:
1731
FIXME( "unhandled sequence %s\n", debugstr_wn( buf, size ));
1732
return 0;
1733
}
1734
}
1735
1736
static unsigned int process_input_escape( struct console *console, const WCHAR *buf, size_t size )
1737
{
1738
unsigned int vk = 0, count = 0, nlen;
1739
1740
if (!size)
1741
{
1742
key_press( console, 0, VK_ESCAPE, 0 );
1743
return 0;
1744
}
1745
1746
switch(buf[0])
1747
{
1748
case '[':
1749
if (++count == size) break;
1750
if ((nlen = process_csi_sequence( console, buf + 1, size - 1 ))) return count + nlen;
1751
break;
1752
1753
case 'O':
1754
if (++count == size) break;
1755
vk = escape_char_to_vk( buf[1], NULL, NULL );
1756
if (vk)
1757
{
1758
key_press( console, 0, vk, 0 );
1759
return count + 1;
1760
}
1761
}
1762
1763
char_key_press( console, buf[0], LEFT_ALT_PRESSED );
1764
return 1;
1765
}
1766
1767
static DWORD WINAPI tty_input( void *param )
1768
{
1769
struct console *console = param;
1770
IO_STATUS_BLOCK io;
1771
HANDLE event;
1772
char read_buf[4096];
1773
WCHAR buf[4096];
1774
DWORD count, i;
1775
BOOL signaled;
1776
NTSTATUS status;
1777
1778
if (console->is_unix)
1779
{
1780
unsigned int h = condrv_handle( console->tty_input );
1781
status = NtDeviceIoControlFile( console->server, NULL, NULL, NULL, &io, IOCTL_CONDRV_SETUP_INPUT,
1782
&h, sizeof(h), NULL, 0 );
1783
if (status) ERR( "input setup failed: %#lx\n", status );
1784
}
1785
1786
event = CreateEventW( NULL, TRUE, FALSE, NULL );
1787
1788
for (;;)
1789
{
1790
status = NtReadFile( console->tty_input, event, NULL, NULL, &io, read_buf, sizeof(read_buf), NULL, NULL );
1791
if (status == STATUS_PENDING)
1792
{
1793
if ((status = NtWaitForSingleObject( event, FALSE, NULL ))) break;
1794
status = io.Status;
1795
}
1796
if (status) break;
1797
1798
EnterCriticalSection( &console_section );
1799
signaled = console->record_count != 0;
1800
1801
/* FIXME: Handle partial char read */
1802
count = MultiByteToWideChar( get_tty_cp( console ), 0, read_buf, io.Information, buf, ARRAY_SIZE(buf) );
1803
1804
TRACE( "%s\n", debugstr_wn(buf, count) );
1805
1806
for (i = 0; i < count; i++)
1807
{
1808
WCHAR ch = buf[i];
1809
switch (ch)
1810
{
1811
case 3: /* end of text */
1812
if (console->is_unix)
1813
{
1814
key_press( console, ch, 'C', LEFT_CTRL_PRESSED );
1815
break;
1816
}
1817
LeaveCriticalSection( &console_section );
1818
goto done;
1819
case '\n':
1820
key_press( console, '\n', VK_RETURN, LEFT_CTRL_PRESSED );
1821
break;
1822
case '\b':
1823
key_press( console, ch, 'H', LEFT_CTRL_PRESSED );
1824
break;
1825
case 0x1b:
1826
i += process_input_escape( console, buf + i + 1, count - i - 1 );
1827
break;
1828
case 0x1c: /* map ctrl-\ unix-ism into ctrl-break/pause windows-ism for unix consoles */
1829
if (console->is_unix)
1830
key_press( console, 0, VK_CANCEL, LEFT_CTRL_PRESSED );
1831
else
1832
char_key_press( console, ch, 0 );
1833
break;
1834
case 0x7f:
1835
key_press( console, '\b', VK_BACK, 0 );
1836
break;
1837
default:
1838
char_key_press( console, ch, 0 );
1839
}
1840
}
1841
1842
process_console_input( console );
1843
if (!signaled && console->record_count)
1844
{
1845
assert( !console->read_ioctl );
1846
read_complete( console, STATUS_SUCCESS, NULL, 0, TRUE ); /* signal console */
1847
}
1848
LeaveCriticalSection( &console_section );
1849
}
1850
1851
TRACE( "NtReadFile failed: %#lx\n", status );
1852
1853
done:
1854
EnterCriticalSection( &console_section );
1855
if (console->read_ioctl) read_complete( console, status, NULL, 0, FALSE );
1856
if (console->is_unix)
1857
{
1858
unsigned int h = 0;
1859
status = NtDeviceIoControlFile( console->server, NULL, NULL, NULL, &io, IOCTL_CONDRV_SETUP_INPUT,
1860
&h, sizeof(h), NULL, 0 );
1861
if (status) ERR( "input restore failed: %#lx\n", status );
1862
}
1863
CloseHandle( console->input_thread );
1864
console->input_thread = NULL;
1865
LeaveCriticalSection( &console_section );
1866
1867
return 0;
1868
}
1869
1870
static BOOL ensure_tty_input_thread( struct console *console )
1871
{
1872
if (!console->tty_input) return TRUE;
1873
if (!console->input_thread)
1874
console->input_thread = CreateThread( NULL, 0, tty_input, console, 0, NULL );
1875
return console->input_thread != NULL;
1876
}
1877
1878
static NTSTATUS screen_buffer_activate( struct screen_buffer *screen_buffer )
1879
{
1880
RECT update_rect;
1881
TRACE( "%p\n", screen_buffer );
1882
screen_buffer->console->active = screen_buffer;
1883
SetRect( &update_rect, 0, 0, screen_buffer->width - 1, screen_buffer->height - 1 );
1884
update_output( screen_buffer, &update_rect );
1885
tty_sync( screen_buffer->console );
1886
update_window_config( screen_buffer->console, FALSE );
1887
return STATUS_SUCCESS;
1888
}
1889
1890
static NTSTATUS get_output_info( struct screen_buffer *screen_buffer, size_t *out_size )
1891
{
1892
struct condrv_output_info *info;
1893
1894
*out_size = min( *out_size, sizeof(*info) + screen_buffer->font.face_len * sizeof(WCHAR) );
1895
if (!(info = alloc_ioctl_buffer( *out_size ))) return STATUS_NO_MEMORY;
1896
1897
info->cursor_size = screen_buffer->cursor_size;
1898
info->cursor_visible = screen_buffer->cursor_visible;
1899
info->cursor_x = get_bounded_cursor_x( screen_buffer );
1900
info->cursor_y = screen_buffer->cursor_y;
1901
info->width = screen_buffer->width;
1902
info->height = screen_buffer->height;
1903
info->attr = screen_buffer->attr;
1904
info->popup_attr = screen_buffer->popup_attr;
1905
info->win_left = screen_buffer->win.left;
1906
info->win_top = screen_buffer->win.top;
1907
info->win_right = screen_buffer->win.right;
1908
info->win_bottom = screen_buffer->win.bottom;
1909
info->max_width = screen_buffer->max_width;
1910
info->max_height = screen_buffer->max_height;
1911
info->font_width = screen_buffer->font.width;
1912
info->font_height = screen_buffer->font.height;
1913
info->font_weight = screen_buffer->font.weight;
1914
info->font_pitch_family = screen_buffer->font.pitch_family;
1915
memcpy( info->color_map, screen_buffer->color_map, sizeof(info->color_map) );
1916
if (*out_size > sizeof(*info)) memcpy( info + 1, screen_buffer->font.face_name, *out_size - sizeof(*info) );
1917
1918
TRACE( "%p cursor_size=%u cursor_visible=%x cursor=(%u,%u) width=%u height=%u win=%s attr=%x popup_attr=%x"
1919
" font_width=%u font_height=%u %s\n", screen_buffer, info->cursor_size, info->cursor_visible,
1920
info->cursor_x, info->cursor_y, info->width, info->height, wine_dbgstr_rect(&screen_buffer->win),
1921
info->attr, info->popup_attr, info->font_width, info->font_height,
1922
debugstr_wn( (const WCHAR *)(info + 1), (*out_size - sizeof(*info)) / sizeof(WCHAR) ) );
1923
return STATUS_SUCCESS;
1924
}
1925
1926
void notify_screen_buffer_size( struct screen_buffer *screen_buffer )
1927
{
1928
if (is_active( screen_buffer ) && screen_buffer->console->mode & ENABLE_WINDOW_INPUT)
1929
{
1930
INPUT_RECORD ir;
1931
ir.EventType = WINDOW_BUFFER_SIZE_EVENT;
1932
ir.Event.WindowBufferSizeEvent.dwSize.X = screen_buffer->width;
1933
ir.Event.WindowBufferSizeEvent.dwSize.Y = screen_buffer->height;
1934
write_console_input( screen_buffer->console, &ir, 1, TRUE );
1935
}
1936
}
1937
1938
NTSTATUS change_screen_buffer_size( struct screen_buffer *screen_buffer, int new_width, int new_height )
1939
{
1940
int i, old_width, old_height, copy_width, copy_height;
1941
char_info_t *new_data;
1942
1943
if (!(new_data = malloc( new_width * new_height * sizeof(*new_data) ))) return STATUS_NO_MEMORY;
1944
1945
old_width = screen_buffer->width;
1946
old_height = screen_buffer->height;
1947
copy_width = min( old_width, new_width );
1948
copy_height = min( old_height, new_height );
1949
1950
/* copy all the rows */
1951
for (i = 0; i < copy_height; i++)
1952
{
1953
memcpy( &new_data[i * new_width], &screen_buffer->data[i * old_width],
1954
copy_width * sizeof(char_info_t) );
1955
}
1956
1957
/* clear the end of each row */
1958
if (new_width > old_width)
1959
{
1960
/* fill first row */
1961
for (i = old_width; i < new_width; i++) new_data[i] = empty_char_info;
1962
/* and blast it to the other rows */
1963
for (i = 1; i < copy_height; i++)
1964
memcpy( &new_data[i * new_width + old_width], &new_data[old_width],
1965
(new_width - old_width) * sizeof(char_info_t) );
1966
}
1967
1968
/* clear remaining rows */
1969
if (new_height > old_height)
1970
{
1971
/* fill first row */
1972
for (i = 0; i < new_width; i++) new_data[old_height * new_width + i] = empty_char_info;
1973
/* and blast it to the other rows */
1974
for (i = old_height+1; i < new_height; i++)
1975
memcpy( &new_data[i * new_width], &new_data[old_height * new_width],
1976
new_width * sizeof(char_info_t) );
1977
}
1978
free( screen_buffer->data );
1979
screen_buffer->data = new_data;
1980
screen_buffer->width = new_width;
1981
screen_buffer->height = new_height;
1982
return STATUS_SUCCESS;
1983
}
1984
1985
static NTSTATUS set_output_info( struct screen_buffer *screen_buffer,
1986
const struct condrv_output_info_params *params, size_t in_size )
1987
{
1988
const struct condrv_output_info *info = &params->info;
1989
NTSTATUS status;
1990
1991
TRACE( "%p\n", screen_buffer );
1992
1993
if (params->mask & SET_CONSOLE_OUTPUT_INFO_CURSOR_GEOM)
1994
{
1995
if (info->cursor_size < 1 || info->cursor_size > 100) return STATUS_INVALID_PARAMETER;
1996
1997
screen_buffer->cursor_size = info->cursor_size;
1998
screen_buffer->cursor_visible = !!info->cursor_visible;
1999
}
2000
if (params->mask & SET_CONSOLE_OUTPUT_INFO_CURSOR_POS)
2001
{
2002
if (info->cursor_x < 0 || info->cursor_x >= screen_buffer->width ||
2003
info->cursor_y < 0 || info->cursor_y >= screen_buffer->height)
2004
{
2005
return STATUS_INVALID_PARAMETER;
2006
}
2007
2008
if (screen_buffer->cursor_x != info->cursor_x || screen_buffer->cursor_y != info->cursor_y)
2009
{
2010
struct console *console = screen_buffer->console;
2011
screen_buffer->cursor_x = info->cursor_x;
2012
screen_buffer->cursor_y = info->cursor_y;
2013
if (console->use_relative_cursor)
2014
set_tty_cursor_relative( console, screen_buffer->cursor_x, screen_buffer->cursor_y );
2015
scroll_to_cursor( screen_buffer );
2016
}
2017
}
2018
if (params->mask & SET_CONSOLE_OUTPUT_INFO_SIZE)
2019
{
2020
enter_absolute_mode( screen_buffer->console );
2021
/* new screen-buffer cannot be smaller than actual window */
2022
if (info->width < screen_buffer->win.right - screen_buffer->win.left + 1 ||
2023
info->height < screen_buffer->win.bottom - screen_buffer->win.top + 1)
2024
{
2025
return STATUS_INVALID_PARAMETER;
2026
}
2027
/* FIXME: there are also some basic minimum and max size to deal with */
2028
if ((status = change_screen_buffer_size( screen_buffer, info->width, info->height ))) return status;
2029
2030
/* scroll window to display sb */
2031
if (screen_buffer->win.right >= info->width)
2032
{
2033
screen_buffer->win.right -= screen_buffer->win.left;
2034
screen_buffer->win.left = 0;
2035
}
2036
if (screen_buffer->win.bottom >= info->height)
2037
{
2038
screen_buffer->win.bottom -= screen_buffer->win.top;
2039
screen_buffer->win.top = 0;
2040
}
2041
if (screen_buffer->cursor_x >= info->width) screen_buffer->cursor_x = info->width - 1;
2042
if (screen_buffer->cursor_y >= info->height) screen_buffer->cursor_y = info->height - 1;
2043
2044
notify_screen_buffer_size( screen_buffer );
2045
}
2046
if (params->mask & SET_CONSOLE_OUTPUT_INFO_ATTR)
2047
{
2048
screen_buffer->attr = info->attr;
2049
}
2050
if (params->mask & SET_CONSOLE_OUTPUT_INFO_POPUP_ATTR)
2051
{
2052
screen_buffer->popup_attr = info->popup_attr;
2053
}
2054
if (params->mask & SET_CONSOLE_OUTPUT_INFO_DISPLAY_WINDOW)
2055
{
2056
enter_absolute_mode( screen_buffer->console );
2057
if (info->win_left < 0 || info->win_left > info->win_right ||
2058
info->win_right >= screen_buffer->width ||
2059
info->win_top < 0 || info->win_top > info->win_bottom ||
2060
info->win_bottom >= screen_buffer->height)
2061
{
2062
return STATUS_INVALID_PARAMETER;
2063
}
2064
if (screen_buffer->win.left != info->win_left || screen_buffer->win.top != info->win_top ||
2065
screen_buffer->win.right != info->win_right || screen_buffer->win.bottom != info->win_bottom)
2066
{
2067
screen_buffer->win.left = info->win_left;
2068
screen_buffer->win.top = info->win_top;
2069
screen_buffer->win.right = info->win_right;
2070
screen_buffer->win.bottom = info->win_bottom;
2071
}
2072
}
2073
if (params->mask & SET_CONSOLE_OUTPUT_INFO_MAX_SIZE)
2074
{
2075
enter_absolute_mode( screen_buffer->console );
2076
screen_buffer->max_width = info->max_width;
2077
screen_buffer->max_height = info->max_height;
2078
}
2079
if (params->mask & SET_CONSOLE_OUTPUT_INFO_FONT)
2080
{
2081
WCHAR *face_name = (WCHAR *)(params + 1);
2082
size_t face_name_size = in_size - sizeof(*params);
2083
unsigned int height = info->font_height;
2084
unsigned int weight = FW_NORMAL;
2085
2086
if (!face_name_size)
2087
{
2088
face_name = screen_buffer->font.face_name;
2089
face_name_size = screen_buffer->font.face_len * sizeof(WCHAR);
2090
}
2091
2092
if (!height) height = 12;
2093
if (info->font_weight >= FW_SEMIBOLD) weight = FW_BOLD;
2094
2095
update_console_font( screen_buffer->console, face_name, face_name_size, height, weight );
2096
}
2097
2098
if (is_active( screen_buffer ))
2099
{
2100
tty_sync( screen_buffer->console );
2101
update_window_config( screen_buffer->console, FALSE );
2102
}
2103
return STATUS_SUCCESS;
2104
}
2105
2106
static NTSTATUS write_console( struct screen_buffer *screen_buffer, const WCHAR *buffer, size_t len )
2107
{
2108
RECT update_rect;
2109
size_t i, j;
2110
2111
TRACE( "%s\n", debugstr_wn(buffer, len) );
2112
2113
empty_update_rect( screen_buffer, &update_rect );
2114
2115
for (i = 0; i < len; i++)
2116
{
2117
if (screen_buffer->mode & ENABLE_PROCESSED_OUTPUT)
2118
{
2119
switch (buffer[i])
2120
{
2121
case '\b':
2122
screen_buffer->cursor_x = get_bounded_cursor_x( screen_buffer );
2123
if (screen_buffer->cursor_x) screen_buffer->cursor_x--;
2124
continue;
2125
case '\t':
2126
j = min( screen_buffer->width - screen_buffer->cursor_x, 8 - (screen_buffer->cursor_x % 8) );
2127
if (!j) j = 8;
2128
while (j--) write_char( screen_buffer, ' ', &update_rect, NULL );
2129
continue;
2130
case '\n':
2131
screen_buffer->cursor_x = 0;
2132
if (++screen_buffer->cursor_y == screen_buffer->height)
2133
new_line( screen_buffer, &update_rect );
2134
else if (screen_buffer->mode & ENABLE_WRAP_AT_EOL_OUTPUT)
2135
{
2136
update_output( screen_buffer, &update_rect );
2137
set_tty_cursor( screen_buffer->console, screen_buffer->cursor_x, screen_buffer->cursor_y );
2138
}
2139
continue;
2140
case '\a':
2141
FIXME( "beep\n" );
2142
continue;
2143
case '\r':
2144
screen_buffer->cursor_x = 0;
2145
continue;
2146
}
2147
}
2148
if (screen_buffer->cursor_x == screen_buffer->width && !(screen_buffer->mode & ENABLE_WRAP_AT_EOL_OUTPUT))
2149
screen_buffer->cursor_x = update_rect.left;
2150
write_char( screen_buffer, buffer[i], &update_rect, NULL );
2151
}
2152
2153
if (screen_buffer->cursor_x == screen_buffer->width)
2154
{
2155
if (screen_buffer->mode & ENABLE_WRAP_AT_EOL_OUTPUT)
2156
{
2157
if (!(screen_buffer->mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING))
2158
{
2159
screen_buffer->cursor_x = 0;
2160
if (++screen_buffer->cursor_y == screen_buffer->height)
2161
new_line( screen_buffer, &update_rect );
2162
}
2163
}
2164
else screen_buffer->cursor_x = update_rect.left;
2165
}
2166
2167
scroll_to_cursor( screen_buffer );
2168
update_output( screen_buffer, &update_rect );
2169
tty_sync( screen_buffer->console );
2170
update_window_config( screen_buffer->console, TRUE );
2171
return STATUS_SUCCESS;
2172
}
2173
2174
static NTSTATUS write_output( struct screen_buffer *screen_buffer, const struct condrv_output_params *params,
2175
size_t in_size, size_t *out_size )
2176
{
2177
unsigned int i, entry_size, entry_cnt, x, y;
2178
char_info_t *dest;
2179
char *src;
2180
2181
enter_absolute_mode( screen_buffer->console );
2182
if (*out_size == sizeof(SMALL_RECT) && !params->width) return STATUS_INVALID_PARAMETER;
2183
2184
entry_size = params->mode == CHAR_INFO_MODE_TEXTATTR ? sizeof(char_info_t) : sizeof(WCHAR);
2185
entry_cnt = (in_size - sizeof(*params)) / entry_size;
2186
2187
TRACE( "(%u,%u) cnt %u\n", params->x, params->y, entry_cnt );
2188
2189
if (params->x >= screen_buffer->width)
2190
{
2191
*out_size = 0;
2192
return STATUS_SUCCESS;
2193
}
2194
2195
for (i = 0, src = (char *)(params + 1); i < entry_cnt; i++, src += entry_size)
2196
{
2197
if (params->width)
2198
{
2199
x = params->x + i % params->width;
2200
y = params->y + i / params->width;
2201
if (x >= screen_buffer->width) continue;
2202
}
2203
else
2204
{
2205
x = (params->x + i) % screen_buffer->width;
2206
y = params->y + (params->x + i) / screen_buffer->width;
2207
}
2208
if (y >= screen_buffer->height) break;
2209
2210
dest = &screen_buffer->data[y * screen_buffer->width + x];
2211
switch(params->mode)
2212
{
2213
case CHAR_INFO_MODE_TEXT:
2214
dest->ch = *(const WCHAR *)src;
2215
break;
2216
case CHAR_INFO_MODE_ATTR:
2217
dest->attr = *(const unsigned short *)src;
2218
break;
2219
case CHAR_INFO_MODE_TEXTATTR:
2220
*dest = *(const char_info_t *)src;
2221
break;
2222
default:
2223
return STATUS_INVALID_PARAMETER;
2224
}
2225
}
2226
2227
if (i && is_active( screen_buffer ))
2228
{
2229
RECT update_rect;
2230
2231
update_rect.left = params->x;
2232
update_rect.top = params->y;
2233
if (params->width)
2234
{
2235
update_rect.bottom = min( params->y + entry_cnt / params->width, screen_buffer->height ) - 1;
2236
update_rect.right = min( params->x + params->width, screen_buffer->width ) - 1;
2237
}
2238
else
2239
{
2240
update_rect.bottom = params->y + (params->x + i - 1) / screen_buffer->width;
2241
if (update_rect.bottom != params->y)
2242
{
2243
update_rect.left = 0;
2244
update_rect.right = screen_buffer->width - 1;
2245
}
2246
else
2247
{
2248
update_rect.right = params->x + i - 1;
2249
}
2250
}
2251
update_output( screen_buffer, &update_rect );
2252
tty_sync( screen_buffer->console );
2253
}
2254
2255
if (*out_size == sizeof(SMALL_RECT))
2256
{
2257
SMALL_RECT *region;
2258
unsigned int width = params->width;
2259
x = params->x;
2260
y = params->y;
2261
if (!(region = alloc_ioctl_buffer( sizeof(*region )))) return STATUS_NO_MEMORY;
2262
region->Left = x;
2263
region->Top = y;
2264
region->Right = min( x + width, screen_buffer->width ) - 1;
2265
region->Bottom = min( y + entry_cnt / width, screen_buffer->height ) - 1;
2266
}
2267
else
2268
{
2269
DWORD *result;
2270
if (!(result = alloc_ioctl_buffer( sizeof(*result )))) return STATUS_NO_MEMORY;
2271
*result = i;
2272
}
2273
2274
return STATUS_SUCCESS;
2275
}
2276
2277
static NTSTATUS read_output( struct screen_buffer *screen_buffer, const struct condrv_output_params *params,
2278
size_t *out_size )
2279
{
2280
enum char_info_mode mode;
2281
unsigned int x, y, width;
2282
unsigned int i, count;
2283
2284
enter_absolute_mode( screen_buffer->console );
2285
x = params->x;
2286
y = params->y;
2287
mode = params->mode;
2288
width = params->width;
2289
TRACE( "(%u %u) mode %u width %u\n", x, y, mode, width );
2290
2291
switch(mode)
2292
{
2293
case CHAR_INFO_MODE_TEXT:
2294
{
2295
WCHAR *data;
2296
char_info_t *src;
2297
if (x >= screen_buffer->width || y >= screen_buffer->height)
2298
{
2299
*out_size = 0;
2300
return STATUS_SUCCESS;
2301
}
2302
src = screen_buffer->data + y * screen_buffer->width + x;
2303
count = min( screen_buffer->data + screen_buffer->height * screen_buffer->width - src,
2304
*out_size / sizeof(*data) );
2305
*out_size = count * sizeof(*data);
2306
if (!(data = alloc_ioctl_buffer( *out_size ))) return STATUS_NO_MEMORY;
2307
for (i = 0; i < count; i++) data[i] = src[i].ch;
2308
}
2309
break;
2310
case CHAR_INFO_MODE_ATTR:
2311
{
2312
unsigned short *data;
2313
char_info_t *src;
2314
if (x >= screen_buffer->width || y >= screen_buffer->height)
2315
{
2316
*out_size = 0;
2317
return STATUS_SUCCESS;
2318
}
2319
src = screen_buffer->data + y * screen_buffer->width + x;
2320
count = min( screen_buffer->data + screen_buffer->height * screen_buffer->width - src,
2321
*out_size / sizeof(*data) );
2322
*out_size = count * sizeof(*data);
2323
if (!(data = alloc_ioctl_buffer( *out_size ))) return STATUS_NO_MEMORY;
2324
for (i = 0; i < count; i++) data[i] = src[i].attr;
2325
}
2326
break;
2327
case CHAR_INFO_MODE_TEXTATTR:
2328
{
2329
SMALL_RECT *region;
2330
char_info_t *data;
2331
if (!width || *out_size < sizeof(*region) || x >= screen_buffer->width || y >= screen_buffer->height)
2332
return STATUS_INVALID_PARAMETER;
2333
count = min( (*out_size - sizeof(*region)) / (width * sizeof(*data)), screen_buffer->height - y );
2334
width = min( width, screen_buffer->width - x );
2335
*out_size = sizeof(*region) + width * count * sizeof(*data);
2336
if (!(region = alloc_ioctl_buffer( *out_size ))) return STATUS_NO_MEMORY;
2337
region->Left = x;
2338
region->Top = y;
2339
region->Right = x + width - 1;
2340
region->Bottom = y + count - 1;
2341
data = (char_info_t *)(region + 1);
2342
for (i = 0; i < count; i++)
2343
{
2344
memcpy( &data[i * width], &screen_buffer->data[(y + i) * screen_buffer->width + x],
2345
width * sizeof(*data) );
2346
}
2347
}
2348
break;
2349
default:
2350
return STATUS_INVALID_PARAMETER;
2351
}
2352
2353
return STATUS_SUCCESS;
2354
}
2355
2356
static NTSTATUS fill_output( struct screen_buffer *screen_buffer, const struct condrv_fill_output_params *params )
2357
{
2358
char_info_t *end, *dest;
2359
DWORD i, count, *result;
2360
2361
TRACE( "(%u %u) mode %u\n", params->x, params->y, params->mode );
2362
2363
enter_absolute_mode( screen_buffer->console );
2364
if (params->y >= screen_buffer->height) return STATUS_SUCCESS;
2365
dest = screen_buffer->data + min( params->y * screen_buffer->width + params->x,
2366
screen_buffer->height * screen_buffer->width );
2367
2368
end = screen_buffer->data + screen_buffer->height * screen_buffer->width;
2369
2370
count = params->count;
2371
if (count > end - dest) count = end - dest;
2372
2373
switch(params->mode)
2374
{
2375
case CHAR_INFO_MODE_TEXT:
2376
for (i = 0; i < count; i++) dest[i].ch = params->ch;
2377
break;
2378
case CHAR_INFO_MODE_ATTR:
2379
for (i = 0; i < count; i++) dest[i].attr = params->attr;
2380
break;
2381
case CHAR_INFO_MODE_TEXTATTR:
2382
for (i = 0; i < count; i++)
2383
{
2384
dest[i].ch = params->ch;
2385
dest[i].attr = params->attr;
2386
}
2387
break;
2388
default:
2389
return STATUS_INVALID_PARAMETER;
2390
}
2391
2392
if (count && is_active(screen_buffer))
2393
{
2394
RECT update_rect;
2395
SetRect( &update_rect,
2396
params->x % screen_buffer->width,
2397
params->y + params->x / screen_buffer->width,
2398
(params->x + i - 1) % screen_buffer->width,
2399
params->y + (params->x + i - 1) / screen_buffer->width );
2400
update_output( screen_buffer, &update_rect );
2401
tty_sync( screen_buffer->console );
2402
}
2403
2404
if (!(result = alloc_ioctl_buffer( sizeof(*result) ))) return STATUS_NO_MEMORY;
2405
*result = count;
2406
return STATUS_SUCCESS;
2407
}
2408
2409
static NTSTATUS scroll_output( struct screen_buffer *screen_buffer, const struct condrv_scroll_params *params )
2410
{
2411
int x, y, xsrc, ysrc, w, h;
2412
char_info_t *psrc, *pdst;
2413
SMALL_RECT src, dst;
2414
RECT update_rect;
2415
SMALL_RECT clip;
2416
2417
enter_absolute_mode( screen_buffer->console );
2418
xsrc = params->scroll.Left;
2419
ysrc = params->scroll.Top;
2420
w = params->scroll.Right - params->scroll.Left + 1;
2421
h = params->scroll.Bottom - params->scroll.Top + 1;
2422
2423
TRACE( "(%d %d) -> (%u %u) w %u h %u\n", xsrc, ysrc, params->origin.X, params->origin.Y, w, h );
2424
2425
clip.Left = max( params->clip.Left, 0 );
2426
clip.Top = max( params->clip.Top, 0 );
2427
clip.Right = min( params->clip.Right, screen_buffer->width - 1 );
2428
clip.Bottom = min( params->clip.Bottom, screen_buffer->height - 1 );
2429
if (clip.Left > clip.Right || clip.Top > clip.Bottom || params->scroll.Left < 0 || params->scroll.Top < 0 ||
2430
params->scroll.Right >= screen_buffer->width || params->scroll.Bottom >= screen_buffer->height ||
2431
params->scroll.Right < params->scroll.Left || params->scroll.Top > params->scroll.Bottom ||
2432
params->origin.X < 0 || params->origin.X >= screen_buffer->width || params->origin.Y < 0 ||
2433
params->origin.Y >= screen_buffer->height)
2434
return STATUS_INVALID_PARAMETER;
2435
2436
src.Left = max( xsrc, clip.Left );
2437
src.Top = max( ysrc, clip.Top );
2438
src.Right = min( xsrc + w - 1, clip.Right );
2439
src.Bottom = min( ysrc + h - 1, clip.Bottom );
2440
2441
dst.Left = params->origin.X;
2442
dst.Top = params->origin.Y;
2443
dst.Right = params->origin.X + w - 1;
2444
dst.Bottom = params->origin.Y + h - 1;
2445
2446
if (dst.Left < clip.Left)
2447
{
2448
xsrc += clip.Left - dst.Left;
2449
w -= clip.Left - dst.Left;
2450
dst.Left = clip.Left;
2451
}
2452
if (dst.Top < clip.Top)
2453
{
2454
ysrc += clip.Top - dst.Top;
2455
h -= clip.Top - dst.Top;
2456
dst.Top = clip.Top;
2457
}
2458
if (dst.Right > clip.Right) w -= dst.Right - clip.Right;
2459
if (dst.Bottom > clip.Bottom) h -= dst.Bottom - clip.Bottom;
2460
2461
if (w > 0 && h > 0)
2462
{
2463
if (ysrc < dst.Top)
2464
{
2465
psrc = &screen_buffer->data[(ysrc + h - 1) * screen_buffer->width + xsrc];
2466
pdst = &screen_buffer->data[(dst.Top + h - 1) * screen_buffer->width + dst.Left];
2467
2468
for (y = h; y > 0; y--)
2469
{
2470
memcpy( pdst, psrc, w * sizeof(*pdst) );
2471
pdst -= screen_buffer->width;
2472
psrc -= screen_buffer->width;
2473
}
2474
}
2475
else
2476
{
2477
psrc = &screen_buffer->data[ysrc * screen_buffer->width + xsrc];
2478
pdst = &screen_buffer->data[dst.Top * screen_buffer->width + dst.Left];
2479
2480
for (y = 0; y < h; y++)
2481
{
2482
/* we use memmove here because when psrc and pdst are the same,
2483
* copies are done on the same row, so the dst and src blocks
2484
* can overlap */
2485
memmove( pdst, psrc, w * sizeof(*pdst) );
2486
pdst += screen_buffer->width;
2487
psrc += screen_buffer->width;
2488
}
2489
}
2490
}
2491
2492
for (y = src.Top; y <= src.Bottom; y++)
2493
{
2494
int left = src.Left;
2495
int right = src.Right;
2496
if (dst.Top <= y && y <= dst.Bottom)
2497
{
2498
if (dst.Left <= src.Left) left = max( left, dst.Right + 1 );
2499
if (dst.Left >= src.Left) right = min( right, dst.Left - 1 );
2500
}
2501
for (x = left; x <= right; x++) screen_buffer->data[y * screen_buffer->width + x] = params->fill;
2502
}
2503
2504
SetRect( &update_rect, min( src.Left, dst.Left ), min( src.Top, dst.Top ),
2505
max( src.Right, dst.Right ), max( src.Bottom, dst.Bottom ));
2506
update_output( screen_buffer, &update_rect );
2507
tty_sync( screen_buffer->console );
2508
return STATUS_SUCCESS;
2509
}
2510
2511
static WCHAR *set_title( const WCHAR *in_title, size_t size )
2512
{
2513
WCHAR *title = NULL;
2514
2515
title = malloc( size + sizeof(WCHAR) );
2516
if (!title) return NULL;
2517
2518
memcpy( title, in_title, size );
2519
title[ size / sizeof(WCHAR) ] = 0;
2520
2521
return title;
2522
}
2523
2524
static NTSTATUS set_console_title( struct console *console, const WCHAR *in_title, size_t size )
2525
{
2526
WCHAR *title = NULL;
2527
2528
TRACE( "%s\n", debugstr_wn(in_title, size / sizeof(WCHAR)) );
2529
2530
if (!(title = set_title( in_title, size )))
2531
return STATUS_NO_MEMORY;
2532
2533
free( console->title );
2534
console->title = title;
2535
2536
if (!console->title_orig && !(console->title_orig = set_title( in_title, size )))
2537
{
2538
free( console->title );
2539
console->title = NULL;
2540
return STATUS_NO_MEMORY;
2541
}
2542
2543
if (console->tty_output)
2544
{
2545
size_t len;
2546
char *vt;
2547
2548
tty_write( console, "\x1b]0;", 4 );
2549
len = WideCharToMultiByte( get_tty_cp( console ), 0, console->title, size / sizeof(WCHAR),
2550
NULL, 0, NULL, NULL);
2551
if ((vt = tty_alloc_buffer( console, len )))
2552
WideCharToMultiByte( get_tty_cp( console ), 0, console->title, size / sizeof(WCHAR),
2553
vt, len, NULL, NULL );
2554
tty_write( console, "\x07", 1 );
2555
tty_sync( console );
2556
}
2557
if (console->win)
2558
SetWindowTextW( console->win, console->title );
2559
return STATUS_SUCCESS;
2560
}
2561
2562
static NTSTATUS screen_buffer_ioctl( struct screen_buffer *screen_buffer, unsigned int code,
2563
const void *in_data, size_t in_size, size_t *out_size )
2564
{
2565
switch (code)
2566
{
2567
case IOCTL_CONDRV_CLOSE_OUTPUT:
2568
if (in_size || *out_size) return STATUS_INVALID_PARAMETER;
2569
destroy_screen_buffer( screen_buffer );
2570
return STATUS_SUCCESS;
2571
2572
case IOCTL_CONDRV_ACTIVATE:
2573
if (in_size || *out_size) return STATUS_INVALID_PARAMETER;
2574
return screen_buffer_activate( screen_buffer );
2575
2576
case IOCTL_CONDRV_GET_MODE:
2577
{
2578
DWORD *mode;
2579
TRACE( "returning mode %x\n", screen_buffer->mode );
2580
if (in_size || *out_size != sizeof(*mode)) return STATUS_INVALID_PARAMETER;
2581
if (!(mode = alloc_ioctl_buffer( *out_size ))) return STATUS_NO_MEMORY;
2582
*mode = screen_buffer->mode;
2583
return STATUS_SUCCESS;
2584
}
2585
2586
case IOCTL_CONDRV_SET_MODE:
2587
if (in_size != sizeof(unsigned int) || *out_size) return STATUS_INVALID_PARAMETER;
2588
screen_buffer->mode = *(unsigned int *)in_data;
2589
TRACE( "set %x mode\n", screen_buffer->mode );
2590
return STATUS_SUCCESS;
2591
2592
case IOCTL_CONDRV_IS_UNIX:
2593
return screen_buffer->console->is_unix ? STATUS_SUCCESS : STATUS_NOT_SUPPORTED;
2594
2595
case IOCTL_CONDRV_WRITE_CONSOLE:
2596
if (in_size % sizeof(WCHAR) || *out_size) return STATUS_INVALID_PARAMETER;
2597
return write_console( screen_buffer, in_data, in_size / sizeof(WCHAR) );
2598
2599
case IOCTL_CONDRV_WRITE_FILE:
2600
{
2601
unsigned int len;
2602
WCHAR *buf;
2603
NTSTATUS status;
2604
2605
len = MultiByteToWideChar( screen_buffer->console->output_cp, 0, in_data, in_size,
2606
NULL, 0 );
2607
if (!len) return STATUS_SUCCESS;
2608
if (!(buf = malloc( len * sizeof(WCHAR) ))) return STATUS_NO_MEMORY;
2609
MultiByteToWideChar( screen_buffer->console->output_cp, 0, in_data, in_size, buf, len );
2610
status = write_console( screen_buffer, buf, len );
2611
free( buf );
2612
return status;
2613
}
2614
2615
case IOCTL_CONDRV_WRITE_OUTPUT:
2616
if ((*out_size != sizeof(DWORD) && *out_size != sizeof(SMALL_RECT)) ||
2617
in_size < sizeof(struct condrv_output_params))
2618
return STATUS_INVALID_PARAMETER;
2619
return write_output( screen_buffer, in_data, in_size, out_size );
2620
2621
case IOCTL_CONDRV_READ_OUTPUT:
2622
if (in_size != sizeof(struct condrv_output_params)) return STATUS_INVALID_PARAMETER;
2623
return read_output( screen_buffer, in_data, out_size );
2624
2625
case IOCTL_CONDRV_GET_OUTPUT_INFO:
2626
if (in_size || *out_size < sizeof(struct condrv_output_info)) return STATUS_INVALID_PARAMETER;
2627
return get_output_info( screen_buffer, out_size );
2628
2629
case IOCTL_CONDRV_SET_OUTPUT_INFO:
2630
if (in_size < sizeof(struct condrv_output_info_params) || *out_size)
2631
return STATUS_INVALID_PARAMETER;
2632
return set_output_info( screen_buffer, in_data, in_size );
2633
2634
case IOCTL_CONDRV_FILL_OUTPUT:
2635
if (in_size != sizeof(struct condrv_fill_output_params) || *out_size != sizeof(DWORD))
2636
return STATUS_INVALID_PARAMETER;
2637
return fill_output( screen_buffer, in_data );
2638
2639
case IOCTL_CONDRV_SCROLL:
2640
if (in_size != sizeof(struct condrv_scroll_params) || *out_size)
2641
return STATUS_INVALID_PARAMETER;
2642
return scroll_output( screen_buffer, in_data );
2643
2644
default:
2645
WARN( "invalid ioctl %x\n", code );
2646
return STATUS_INVALID_HANDLE;
2647
}
2648
}
2649
2650
static NTSTATUS console_input_ioctl( struct console *console, unsigned int code, const void *in_data,
2651
size_t in_size, size_t *out_size )
2652
{
2653
NTSTATUS status;
2654
2655
switch (code)
2656
{
2657
case IOCTL_CONDRV_GET_MODE:
2658
{
2659
DWORD *mode;
2660
TRACE( "returning mode %x\n", console->mode );
2661
if (in_size || *out_size != sizeof(*mode)) return STATUS_INVALID_PARAMETER;
2662
if (!(mode = alloc_ioctl_buffer( *out_size ))) return STATUS_NO_MEMORY;
2663
*mode = console->mode;
2664
return STATUS_SUCCESS;
2665
}
2666
2667
case IOCTL_CONDRV_SET_MODE:
2668
if (in_size != sizeof(unsigned int) || *out_size) return STATUS_INVALID_PARAMETER;
2669
console->mode = *(unsigned int *)in_data;
2670
TRACE( "set %x mode\n", console->mode );
2671
return STATUS_SUCCESS;
2672
2673
case IOCTL_CONDRV_IS_UNIX:
2674
return console->is_unix ? STATUS_SUCCESS : STATUS_NOT_SUPPORTED;
2675
2676
case IOCTL_CONDRV_READ_CONSOLE:
2677
if (in_size || *out_size % sizeof(WCHAR)) return STATUS_INVALID_PARAMETER;
2678
ensure_tty_input_thread( console );
2679
status = read_console( console, code, *out_size, NULL, 0, 0 );
2680
*out_size = 0;
2681
return status;
2682
2683
case IOCTL_CONDRV_READ_CONSOLE_CONTROL:
2684
if ((in_size < sizeof(DWORD)) || ((in_size - sizeof(DWORD)) % sizeof(WCHAR)) ||
2685
(*out_size < sizeof(DWORD)) || ((*out_size - sizeof(DWORD)) % sizeof(WCHAR)))
2686
return STATUS_INVALID_PARAMETER;
2687
ensure_tty_input_thread( console );
2688
status = read_console( console, code, *out_size - sizeof(DWORD),
2689
(const WCHAR*)((const char*)in_data + sizeof(DWORD)),
2690
(in_size - sizeof(DWORD)) / sizeof(WCHAR),
2691
*(DWORD*)in_data );
2692
*out_size = 0;
2693
return status;
2694
2695
case IOCTL_CONDRV_READ_FILE:
2696
ensure_tty_input_thread( console );
2697
status = read_console( console, code, *out_size, NULL, 0, 0 );
2698
*out_size = 0;
2699
return status;
2700
2701
case IOCTL_CONDRV_READ_INPUT:
2702
{
2703
if (in_size) return STATUS_INVALID_PARAMETER;
2704
ensure_tty_input_thread( console );
2705
if (!console->record_count && *out_size)
2706
{
2707
TRACE( "pending read\n" );
2708
console->read_ioctl = IOCTL_CONDRV_READ_INPUT;
2709
console->pending_read = *out_size;
2710
return STATUS_PENDING;
2711
}
2712
status = read_console_input( console, *out_size );
2713
*out_size = 0;
2714
return status;
2715
}
2716
2717
case IOCTL_CONDRV_WRITE_INPUT:
2718
if (in_size % sizeof(INPUT_RECORD) || *out_size) return STATUS_INVALID_PARAMETER;
2719
return write_console_input( console, in_data, in_size / sizeof(INPUT_RECORD), TRUE );
2720
2721
case IOCTL_CONDRV_PEEK:
2722
{
2723
void *result;
2724
TRACE( "peek\n" );
2725
if (in_size) return STATUS_INVALID_PARAMETER;
2726
ensure_tty_input_thread( console );
2727
*out_size = min( *out_size, console->record_count * sizeof(INPUT_RECORD) );
2728
if (!(result = alloc_ioctl_buffer( *out_size ))) return STATUS_NO_MEMORY;
2729
if (*out_size) memcpy( result, console->records, *out_size );
2730
return STATUS_SUCCESS;
2731
}
2732
2733
case IOCTL_CONDRV_GET_INPUT_INFO:
2734
{
2735
struct condrv_input_info *info;
2736
TRACE( "get info\n" );
2737
if (in_size || *out_size != sizeof(*info)) return STATUS_INVALID_PARAMETER;
2738
if (!(info = alloc_ioctl_buffer( sizeof(*info )))) return STATUS_NO_MEMORY;
2739
info->input_cp = console->input_cp;
2740
info->output_cp = console->output_cp;
2741
return STATUS_SUCCESS;
2742
}
2743
2744
case IOCTL_CONDRV_GET_INPUT_COUNT:
2745
{
2746
DWORD *count;
2747
TRACE( "get input count\n" );
2748
if (in_size || *out_size != sizeof(*count)) return STATUS_INVALID_PARAMETER;
2749
ensure_tty_input_thread( console );
2750
if (!(count = alloc_ioctl_buffer( sizeof(*count )))) return STATUS_NO_MEMORY;
2751
*count = console->record_count;
2752
return STATUS_SUCCESS;
2753
}
2754
2755
case IOCTL_CONDRV_GET_WINDOW:
2756
{
2757
condrv_handle_t *result;
2758
TRACE( "get window\n" );
2759
if (in_size || *out_size != sizeof(*result)) return STATUS_INVALID_PARAMETER;
2760
if (!(result = alloc_ioctl_buffer( sizeof(*result )))) return STATUS_NO_MEMORY;
2761
if (!console->win && !console->no_window) init_message_window( console );
2762
*result = condrv_handle( console->win );
2763
return STATUS_SUCCESS;
2764
}
2765
2766
case IOCTL_CONDRV_SET_INPUT_INFO:
2767
{
2768
const struct condrv_input_info_params *params = in_data;
2769
TRACE( "set info\n" );
2770
if (in_size != sizeof(*params) || *out_size) return STATUS_INVALID_PARAMETER;
2771
if (params->mask & SET_CONSOLE_INPUT_INFO_INPUT_CODEPAGE)
2772
{
2773
if (!IsValidCodePage( params->info.input_cp )) return STATUS_INVALID_PARAMETER;
2774
console->input_cp = params->info.input_cp;
2775
}
2776
if (params->mask & SET_CONSOLE_INPUT_INFO_OUTPUT_CODEPAGE)
2777
{
2778
if (!IsValidCodePage( params->info.output_cp )) return STATUS_INVALID_PARAMETER;
2779
console->output_cp = params->info.output_cp;
2780
}
2781
return STATUS_SUCCESS;
2782
}
2783
2784
case IOCTL_CONDRV_GET_TITLE:
2785
{
2786
BOOL current_title;
2787
WCHAR *title;
2788
size_t title_len, str_size;
2789
struct condrv_title_params *params;
2790
if (in_size != sizeof(BOOL)) return STATUS_INVALID_PARAMETER;
2791
current_title = *(BOOL *)in_data;
2792
title = current_title ? console->title : console->title_orig;
2793
title_len = title ? wcslen( title ) : 0;
2794
str_size = min( *out_size - sizeof(*params), title_len * sizeof(WCHAR) );
2795
*out_size = sizeof(*params) + str_size;
2796
if (!(params = alloc_ioctl_buffer( *out_size ))) return STATUS_NO_MEMORY;
2797
TRACE( "returning %s %s\n", current_title ? "title" : "original title", debugstr_w(title) );
2798
if (str_size) memcpy( params->buffer, title, str_size );
2799
params->title_len = title_len;
2800
return STATUS_SUCCESS;
2801
}
2802
2803
case IOCTL_CONDRV_SET_TITLE:
2804
if (in_size % sizeof(WCHAR) || *out_size) return STATUS_INVALID_PARAMETER;
2805
return set_console_title( console, in_data, in_size );
2806
2807
case IOCTL_CONDRV_BEEP:
2808
if (in_size || *out_size) return STATUS_INVALID_PARAMETER;
2809
if (console->is_unix)
2810
{
2811
tty_write( console, "\a", 1 );
2812
tty_sync( console );
2813
}
2814
return STATUS_SUCCESS;
2815
2816
case IOCTL_CONDRV_FLUSH:
2817
if (in_size || *out_size) return STATUS_INVALID_PARAMETER;
2818
TRACE( "flush\n" );
2819
console->record_count = 0;
2820
return STATUS_SUCCESS;
2821
2822
default:
2823
WARN( "unsupported ioctl %x\n", code );
2824
return STATUS_INVALID_HANDLE;
2825
}
2826
}
2827
2828
static NTSTATUS process_console_ioctls( struct console *console )
2829
{
2830
size_t out_size = 0, in_size;
2831
unsigned int code;
2832
int output;
2833
NTSTATUS status = STATUS_SUCCESS;
2834
2835
for (;;)
2836
{
2837
if (status) out_size = 0;
2838
2839
console->signaled = console->record_count != 0;
2840
SERVER_START_REQ( get_next_console_request )
2841
{
2842
req->handle = wine_server_obj_handle( console->server );
2843
req->status = status;
2844
req->signal = console->signaled;
2845
wine_server_add_data( req, ioctl_buffer, out_size );
2846
wine_server_set_reply( req, ioctl_buffer, ioctl_buffer_size );
2847
status = wine_server_call( req );
2848
code = reply->code;
2849
output = reply->output;
2850
out_size = reply->out_size;
2851
in_size = wine_server_reply_size( reply );
2852
}
2853
SERVER_END_REQ;
2854
2855
if (status == STATUS_PENDING) return STATUS_SUCCESS;
2856
if (status == STATUS_BUFFER_OVERFLOW)
2857
{
2858
if (!alloc_ioctl_buffer( out_size )) return STATUS_NO_MEMORY;
2859
status = STATUS_SUCCESS;
2860
continue;
2861
}
2862
if (status)
2863
{
2864
TRACE( "failed to get next request: %#lx\n", status );
2865
return status;
2866
}
2867
2868
if (code == IOCTL_CONDRV_INIT_OUTPUT)
2869
{
2870
TRACE( "initializing output %x\n", output );
2871
enter_absolute_mode( console );
2872
if (console->active)
2873
create_screen_buffer( console, output, console->active->width, console->active->height );
2874
else
2875
create_screen_buffer( console, output, 80, 150 );
2876
}
2877
else if (!output)
2878
{
2879
status = console_input_ioctl( console, code, ioctl_buffer, in_size, &out_size );
2880
}
2881
else
2882
{
2883
struct wine_rb_entry *entry;
2884
if (!(entry = wine_rb_get( &screen_buffer_map, LongToPtr(output) )))
2885
{
2886
ERR( "invalid screen buffer id %x\n", output );
2887
status = STATUS_INVALID_HANDLE;
2888
}
2889
else
2890
{
2891
status = screen_buffer_ioctl( WINE_RB_ENTRY_VALUE( entry, struct screen_buffer, entry ), code,
2892
ioctl_buffer, in_size, &out_size );
2893
}
2894
}
2895
}
2896
}
2897
2898
static BOOL is_key_message( const MSG *msg )
2899
{
2900
return msg->message == WM_KEYDOWN || msg->message == WM_SYSKEYDOWN ||
2901
msg->message == WM_KEYUP || msg->message == WM_SYSKEYUP;
2902
}
2903
2904
static int main_loop( struct console *console, HANDLE signal )
2905
{
2906
HANDLE signal_event = NULL;
2907
HANDLE wait_handles[3];
2908
unsigned int wait_cnt = 0;
2909
unsigned short signal_id;
2910
IO_STATUS_BLOCK signal_io;
2911
NTSTATUS status;
2912
DWORD res;
2913
2914
if (signal)
2915
{
2916
if (!(signal_event = CreateEventW( NULL, TRUE, FALSE, NULL ))) return 1;
2917
status = NtReadFile( signal, signal_event, NULL, NULL, &signal_io, &signal_id,
2918
sizeof(signal_id), NULL, NULL );
2919
if (status && status != STATUS_PENDING) return 1;
2920
}
2921
2922
if (!alloc_ioctl_buffer( 4096 )) return 1;
2923
2924
wait_handles[wait_cnt++] = console->server;
2925
if (signal) wait_handles[wait_cnt++] = signal_event;
2926
if (console->input_thread) wait_handles[wait_cnt++] = console->input_thread;
2927
2928
for (;;)
2929
{
2930
if (console->win)
2931
res = MsgWaitForMultipleObjects( wait_cnt, wait_handles, FALSE, INFINITE, QS_ALLINPUT );
2932
else
2933
res = WaitForMultipleObjects( wait_cnt, wait_handles, FALSE, INFINITE );
2934
2935
if (res == WAIT_OBJECT_0 + wait_cnt)
2936
{
2937
MSG msg;
2938
2939
while (PeekMessageW( &msg, 0, 0, 0, PM_REMOVE ))
2940
{
2941
BOOL translated = FALSE;
2942
if (msg.message == WM_QUIT) return 0;
2943
if (is_key_message( &msg ) && msg.wParam == VK_PROCESSKEY)
2944
translated = TranslateMessage( &msg );
2945
if (!translated || msg.hwnd != console->win)
2946
DispatchMessageW( &msg );
2947
}
2948
continue;
2949
}
2950
2951
switch (res)
2952
{
2953
case WAIT_OBJECT_0:
2954
EnterCriticalSection( &console_section );
2955
status = process_console_ioctls( console );
2956
LeaveCriticalSection( &console_section );
2957
if (status) return 0;
2958
break;
2959
2960
case WAIT_OBJECT_0 + 1:
2961
if (signal_io.Status || signal_io.Information != sizeof(signal_id))
2962
{
2963
TRACE( "signaled quit\n" );
2964
return 0;
2965
}
2966
FIXME( "unimplemented signal %x\n", signal_id );
2967
status = NtReadFile( signal, signal_event, NULL, NULL, &signal_io, &signal_id,
2968
sizeof(signal_id), NULL, NULL );
2969
if (status && status != STATUS_PENDING) return 1;
2970
break;
2971
2972
default:
2973
TRACE( "wait failed, quit\n");
2974
return 0;
2975
}
2976
}
2977
2978
return 0;
2979
}
2980
2981
static void teardown( struct console *console )
2982
{
2983
if (console->is_unix)
2984
{
2985
set_tty_attr( console, empty_char_info.attr );
2986
tty_flush( console );
2987
}
2988
}
2989
2990
int __cdecl wmain(int argc, WCHAR *argv[])
2991
{
2992
int headless = 0, i, width = 0, height = 0, ret;
2993
HANDLE signal = NULL;
2994
WCHAR *end;
2995
2996
static struct console console;
2997
2998
for (i = 0; i < argc; i++) TRACE("%s ", wine_dbgstr_w(argv[i]));
2999
TRACE("\n");
3000
3001
console.mode = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT |
3002
ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT | ENABLE_INSERT_MODE |
3003
ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS | ENABLE_AUTO_POSITION;
3004
console.input_cp = console.output_cp = GetOEMCP();
3005
console.history_size = 50;
3006
if (!(console.history = calloc( console.history_size, sizeof(*console.history) ))) return 1;
3007
3008
for (i = 1; i < argc; i++)
3009
{
3010
if (!wcscmp( argv[i], L"--headless"))
3011
{
3012
headless = 1;
3013
continue;
3014
}
3015
if (!wcscmp( argv[i], L"--unix"))
3016
{
3017
console.is_unix = 1;
3018
console.use_relative_cursor = 1;
3019
headless = 1;
3020
continue;
3021
}
3022
if (!wcscmp( argv[i], L"--width" ))
3023
{
3024
if (++i == argc) return 1;
3025
width = wcstol( argv[i], &end, 0 );
3026
if ((!width && !console.is_unix) || width > 0xffff || *end) return 1;
3027
continue;
3028
}
3029
if (!wcscmp( argv[i], L"--height" ))
3030
{
3031
if (++i == argc) return 1;
3032
height = wcstol( argv[i], &end, 0 );
3033
if ((!height && !console.is_unix) || height > 0xffff || *end) return 1;
3034
continue;
3035
}
3036
if (!wcscmp( argv[i], L"--signal" ))
3037
{
3038
if (++i == argc) return 1;
3039
signal = ULongToHandle( wcstol( argv[i], &end, 0 ));
3040
if (*end) return 1;
3041
continue;
3042
}
3043
if (!wcscmp( argv[i], L"--server" ))
3044
{
3045
if (++i == argc) return 1;
3046
console.server = ULongToHandle( wcstol( argv[i], &end, 0 ));
3047
if (*end) return 1;
3048
continue;
3049
}
3050
FIXME( "unknown option %s\n", debugstr_w(argv[i]) );
3051
return 1;
3052
}
3053
3054
if (!console.server)
3055
{
3056
ERR( "no server handle\n" );
3057
return 1;
3058
}
3059
3060
if (!width) width = 80;
3061
if (!height) height = 150;
3062
3063
if (!(console.active = create_screen_buffer( &console, 1, width, height ))) return 1;
3064
if (headless)
3065
{
3066
console.tty_input = GetStdHandle( STD_INPUT_HANDLE );
3067
console.tty_output = GetStdHandle( STD_OUTPUT_HANDLE );
3068
3069
if (console.tty_input || console.tty_output)
3070
{
3071
init_tty_output( &console );
3072
if (!console.is_unix && !ensure_tty_input_thread( &console )) return 1;
3073
}
3074
else console.no_window = TRUE;
3075
}
3076
else
3077
{
3078
STARTUPINFOW si;
3079
if (!init_window( &console )) return 1;
3080
GetStartupInfoW( &si );
3081
set_console_title( &console, si.lpTitle, wcslen( si.lpTitle ) * sizeof(WCHAR) );
3082
ShowWindow( console.win, (si.dwFlags & STARTF_USESHOWWINDOW) ? si.wShowWindow : SW_SHOW );
3083
}
3084
3085
ret = main_loop( &console, signal );
3086
teardown( &console );
3087
3088
return ret;
3089
}
3090
3091