mfplat: Read queue subscriber within the critical section.
[wine/zf.git] / programs / conhost / conhost.c
blobf83528cb5f66b1088e5804ef04e3d5d99e107e95
1 /*
2 * Copyright 1998 Alexandre Julliard
3 * Copyright 2001 Eric Pouech
4 * Copyright 2012 Detlef Riekenberg
5 * Copyright 2020 Jacek Caban
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.
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.
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
22 #include <assert.h>
23 #include <limits.h>
25 #include "conhost.h"
27 #include "wine/server.h"
28 #include "wine/debug.h"
30 WINE_DEFAULT_DEBUG_CHANNEL(console);
32 static const char_info_t empty_char_info = { ' ', 0x0007 }; /* white on black space */
34 static CRITICAL_SECTION console_section;
35 static CRITICAL_SECTION_DEBUG critsect_debug =
37 0, 0, &console_section,
38 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
39 0, 0, { (DWORD_PTR)(__FILE__ ": console_section") }
41 static CRITICAL_SECTION console_section = { &critsect_debug, -1, 0, 0, 0, 0 };
43 static void *ioctl_buffer;
44 static size_t ioctl_buffer_size;
46 static void *alloc_ioctl_buffer( size_t size )
48 if (size > ioctl_buffer_size)
50 void *new_buffer;
51 if (!(new_buffer = realloc( ioctl_buffer, size ))) return NULL;
52 ioctl_buffer = new_buffer;
53 ioctl_buffer_size = size;
55 return ioctl_buffer;
58 static int screen_buffer_compare_id( const void *key, const struct wine_rb_entry *entry )
60 struct screen_buffer *screen_buffer = WINE_RB_ENTRY_VALUE( entry, struct screen_buffer, entry );
61 return PtrToLong(key) - screen_buffer->id;
64 static struct wine_rb_tree screen_buffer_map = { screen_buffer_compare_id };
66 static void destroy_screen_buffer( struct screen_buffer *screen_buffer )
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->data );
72 free( screen_buffer );
75 static struct screen_buffer *create_screen_buffer( struct console *console, int id, int width, int height )
77 struct screen_buffer *screen_buffer;
78 unsigned int i;
80 if (!(screen_buffer = calloc( 1, sizeof(*screen_buffer) ))) return NULL;
81 screen_buffer->console = console;
82 screen_buffer->id = id;
83 screen_buffer->mode = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT;
84 screen_buffer->cursor_size = 25;
85 screen_buffer->cursor_visible = 1;
86 screen_buffer->width = width;
87 screen_buffer->height = height;
88 screen_buffer->attr = 0x07;
89 screen_buffer->popup_attr = 0xf5;
90 screen_buffer->font.weight = FW_NORMAL;
91 screen_buffer->font.pitch_family = FIXED_PITCH | FF_DONTCARE;
93 if (console->active)
95 screen_buffer->max_width = console->active->max_width;
96 screen_buffer->max_height = console->active->max_height;
97 screen_buffer->win.right = console->active->win.right - console->active->win.left;
98 screen_buffer->win.bottom = console->active->win.bottom - console->active->win.top;
100 else
102 screen_buffer->max_width = width;
103 screen_buffer->max_height = height;
104 screen_buffer->win.right = width - 1;
105 screen_buffer->win.bottom = height - 1;
108 if (wine_rb_put( &screen_buffer_map, LongToPtr(id), &screen_buffer->entry ))
110 free( screen_buffer );
111 ERR( "id %x already exists\n", id );
112 return NULL;
115 if (!(screen_buffer->data = malloc( screen_buffer->width * screen_buffer->height *
116 sizeof(*screen_buffer->data) )))
118 destroy_screen_buffer( screen_buffer );
119 return NULL;
122 /* clear the first row */
123 for (i = 0; i < screen_buffer->width; i++) screen_buffer->data[i] = empty_char_info;
124 /* and copy it to all other rows */
125 for (i = 1; i < screen_buffer->height; i++)
126 memcpy( &screen_buffer->data[i * screen_buffer->width], screen_buffer->data,
127 screen_buffer->width * sizeof(char_info_t) );
129 return screen_buffer;
132 static BOOL is_active( struct screen_buffer *screen_buffer )
134 return screen_buffer == screen_buffer->console->active;
137 static unsigned int get_tty_cp( struct console *console )
139 return console->is_unix ? CP_UNIXCP : CP_UTF8;
142 static void tty_flush( struct console *console )
144 if (!console->tty_output || !console->tty_buffer_count) return;
145 TRACE("%s\n", debugstr_an(console->tty_buffer, console->tty_buffer_count));
146 if (!WriteFile( console->tty_output, console->tty_buffer, console->tty_buffer_count,
147 NULL, NULL ))
148 WARN( "write failed: %u\n", GetLastError() );
149 console->tty_buffer_count = 0;
152 static void tty_write( struct console *console, const char *buffer, size_t size )
154 if (!size || !console->tty_output) return;
155 if (console->tty_buffer_count + size > sizeof(console->tty_buffer))
156 tty_flush( console );
157 if (console->tty_buffer_count + size <= sizeof(console->tty_buffer))
159 memcpy( console->tty_buffer + console->tty_buffer_count, buffer, size );
160 console->tty_buffer_count += size;
162 else
164 assert( !console->tty_buffer_count );
165 if (!WriteFile( console->tty_output, buffer, size, NULL, NULL ))
166 WARN( "write failed: %u\n", GetLastError() );
170 static void *tty_alloc_buffer( struct console *console, size_t size )
172 void *ret;
173 if (console->tty_buffer_count + size > sizeof(console->tty_buffer)) return NULL;
174 ret = console->tty_buffer + console->tty_buffer_count;
175 console->tty_buffer_count += size;
176 return ret;
179 static void hide_tty_cursor( struct console *console )
181 if (console->tty_cursor_visible)
183 tty_write( console, "\x1b[?25l", 6 );
184 console->tty_cursor_visible = FALSE;
188 static void set_tty_cursor( struct console *console, unsigned int x, unsigned int y )
190 char buf[64];
192 if (console->tty_cursor_x == x && console->tty_cursor_y == y) return;
194 if (!x && y == console->tty_cursor_y + 1) strcpy( buf, "\r\n" );
195 else if (!x && y == console->tty_cursor_y) strcpy( buf, "\r" );
196 else if (y == console->tty_cursor_y)
198 if (console->tty_cursor_x >= console->active->width)
200 if (console->is_unix)
202 /* Unix will usually have the cursor at width-1 in this case. instead of depending
203 * on the exact behaviour, move the cursor to the first column and move forward
204 * from there. */
205 tty_write( console, "\r", 1 );
206 console->tty_cursor_x = 0;
208 else if (console->active->mode & ENABLE_WRAP_AT_EOL_OUTPUT)
210 console->tty_cursor_x--;
212 if (console->tty_cursor_x == x) return;
214 if (x + 1 == console->tty_cursor_x) strcpy( buf, "\b" );
215 else if (x > console->tty_cursor_x) sprintf( buf, "\x1b[%uC", x - console->tty_cursor_x );
216 else sprintf( buf, "\x1b[%uD", console->tty_cursor_x - x );
218 else if (x || y)
220 hide_tty_cursor( console );
221 sprintf( buf, "\x1b[%u;%uH", y + 1, x + 1);
223 else strcpy( buf, "\x1b[H" );
224 console->tty_cursor_x = x;
225 console->tty_cursor_y = y;
226 tty_write( console, buf, strlen(buf) );
229 static void set_tty_cursor_relative( struct console *console, unsigned int x, unsigned int y )
231 if (y < console->tty_cursor_y)
233 char buf[64];
234 sprintf( buf, "\x1b[%uA", console->tty_cursor_y - y );
235 tty_write( console, buf, strlen(buf) );
236 console->tty_cursor_y = y;
238 else
240 while (console->tty_cursor_y < y)
242 console->tty_cursor_x = 0;
243 console->tty_cursor_y++;
244 tty_write( console, "\r\n", 2 );
247 set_tty_cursor( console, x, y );
250 static void set_tty_attr( struct console *console, unsigned int attr )
252 char buf[8];
254 if ((attr & 0x0f) != (console->tty_attr & 0x0f))
256 if ((attr & 0x0f) != 7)
258 unsigned int n = 30;
259 if (attr & FOREGROUND_BLUE) n += 4;
260 if (attr & FOREGROUND_GREEN) n += 2;
261 if (attr & FOREGROUND_RED) n += 1;
262 if (attr & FOREGROUND_INTENSITY) n += 60;
263 sprintf(buf, "\x1b[%um", n);
264 tty_write( console, buf, strlen(buf) );
266 else tty_write( console, "\x1b[m", 3 );
269 if ((attr & 0xf0) != (console->tty_attr & 0xf0) && attr != 7)
271 unsigned int n = 40;
272 if (attr & BACKGROUND_BLUE) n += 4;
273 if (attr & BACKGROUND_GREEN) n += 2;
274 if (attr & BACKGROUND_RED) n += 1;
275 if (attr & BACKGROUND_INTENSITY) n += 60;
276 sprintf(buf, "\x1b[%um", n);
277 tty_write( console, buf, strlen(buf) );
280 console->tty_attr = attr;
283 static void tty_sync( struct console *console )
285 if (!console->tty_output) return;
287 if (console->active->cursor_visible)
289 set_tty_cursor( console, get_bounded_cursor_x( console->active ), console->active->cursor_y );
290 if (!console->tty_cursor_visible)
292 tty_write( console, "\x1b[?25h", 6 ); /* show cursor */
293 console->tty_cursor_visible = TRUE;
296 else if (console->tty_cursor_visible)
297 hide_tty_cursor( console );
298 tty_flush( console );
301 static void init_tty_output( struct console *console )
303 if (!console->is_unix)
305 /* initialize tty output, but don't flush */
306 tty_write( console, "\x1b[2J", 4 ); /* clear screen */
307 set_tty_attr( console, console->active->attr );
308 tty_write( console, "\x1b[H", 3 ); /* move cursor to (0,0) */
310 else console->tty_attr = empty_char_info.attr;
311 console->tty_cursor_visible = TRUE;
314 static void scroll_to_cursor( struct screen_buffer *screen_buffer )
316 unsigned int cursor_x = get_bounded_cursor_x( screen_buffer );
317 int w = screen_buffer->win.right - screen_buffer->win.left + 1;
318 int h = screen_buffer->win.bottom - screen_buffer->win.top + 1;
320 if (cursor_x < screen_buffer->win.left)
321 screen_buffer->win.left = min( cursor_x, screen_buffer->width - w );
322 else if (cursor_x > screen_buffer->win.right)
323 screen_buffer->win.left = max( cursor_x, w ) - w + 1;
324 screen_buffer->win.right = screen_buffer->win.left + w - 1;
326 if (screen_buffer->cursor_y < screen_buffer->win.top)
327 screen_buffer->win.top = min( screen_buffer->cursor_y, screen_buffer->height - h );
328 else if (screen_buffer->cursor_y > screen_buffer->win.bottom)
329 screen_buffer->win.top = max( screen_buffer->cursor_y, h ) - h + 1;
330 screen_buffer->win.bottom = screen_buffer->win.top + h - 1;
333 static void update_output( struct screen_buffer *screen_buffer, RECT *rect )
335 int x, y, size, trailing_spaces;
336 char_info_t *ch;
337 char buf[8];
339 if (!is_active( screen_buffer ) || rect->top > rect->bottom || rect->right < rect->left)
340 return;
342 TRACE( "%s\n", wine_dbgstr_rect( rect ));
344 if (screen_buffer->console->win)
346 update_window_region( screen_buffer->console, rect );
347 return;
349 if (!screen_buffer->console->tty_output) return;
351 hide_tty_cursor( screen_buffer->console );
353 for (y = rect->top; y <= rect->bottom; y++)
355 for (trailing_spaces = 0; trailing_spaces < screen_buffer->width; trailing_spaces++)
357 ch = &screen_buffer->data[(y + 1) * screen_buffer->width - trailing_spaces - 1];
358 if (ch->ch != ' ' || ch->attr != 7) break;
360 if (trailing_spaces < 4) trailing_spaces = 0;
362 for (x = rect->left; x <= rect->right; x++)
364 ch = &screen_buffer->data[y * screen_buffer->width + x];
365 set_tty_attr( screen_buffer->console, ch->attr );
366 set_tty_cursor( screen_buffer->console, x, y );
368 if (x + trailing_spaces >= screen_buffer->width)
370 tty_write( screen_buffer->console, "\x1b[K", 3 );
371 break;
374 size = WideCharToMultiByte( get_tty_cp( screen_buffer->console ), 0,
375 &ch->ch, 1, buf, sizeof(buf), NULL, NULL );
376 tty_write( screen_buffer->console, buf, size );
377 screen_buffer->console->tty_cursor_x++;
381 empty_update_rect( screen_buffer, rect );
384 static void new_line( struct screen_buffer *screen_buffer, RECT *update_rect )
386 unsigned int i;
388 assert( screen_buffer->cursor_y >= screen_buffer->height );
389 screen_buffer->cursor_y = screen_buffer->height - 1;
391 if (screen_buffer->console->tty_output)
392 update_output( screen_buffer, update_rect );
393 else
394 SetRect( update_rect, 0, 0, screen_buffer->width - 1, screen_buffer->height - 1 );
396 memmove( screen_buffer->data, screen_buffer->data + screen_buffer->width,
397 screen_buffer->width * (screen_buffer->height - 1) * sizeof(*screen_buffer->data) );
398 for (i = 0; i < screen_buffer->width; i++)
399 screen_buffer->data[screen_buffer->width * (screen_buffer->height - 1) + i] = empty_char_info;
400 if (is_active( screen_buffer ))
402 screen_buffer->console->tty_cursor_y--;
403 if (screen_buffer->console->tty_cursor_y != screen_buffer->height - 2)
404 set_tty_cursor( screen_buffer->console, 0, screen_buffer->height - 2 );
405 set_tty_cursor( screen_buffer->console, 0, screen_buffer->height - 1 );
409 static void write_char( struct screen_buffer *screen_buffer, WCHAR ch, RECT *update_rect, unsigned int *home_y )
411 if (screen_buffer->cursor_x == screen_buffer->width)
413 screen_buffer->cursor_x = 0;
414 screen_buffer->cursor_y++;
416 if (screen_buffer->cursor_y == screen_buffer->height)
418 if (home_y)
420 if (!*home_y) return;
421 (*home_y)--;
423 new_line( screen_buffer, update_rect );
426 screen_buffer->data[screen_buffer->cursor_y * screen_buffer->width + screen_buffer->cursor_x].ch = ch;
427 screen_buffer->data[screen_buffer->cursor_y * screen_buffer->width + screen_buffer->cursor_x].attr = screen_buffer->attr;
428 update_rect->left = min( update_rect->left, screen_buffer->cursor_x );
429 update_rect->top = min( update_rect->top, screen_buffer->cursor_y );
430 update_rect->right = max( update_rect->right, screen_buffer->cursor_x );
431 update_rect->bottom = max( update_rect->bottom, screen_buffer->cursor_y );
432 screen_buffer->cursor_x++;
435 static NTSTATUS read_complete( struct console *console, NTSTATUS status, const void *buf, size_t size, int signal )
437 SERVER_START_REQ( get_next_console_request )
439 req->handle = wine_server_obj_handle( console->server );
440 req->signal = signal;
441 req->read = 1;
442 req->status = status;
443 wine_server_add_data( req, buf, size );
444 status = wine_server_call( req );
446 SERVER_END_REQ;
447 if (status && (console->read_ioctl || status != STATUS_INVALID_HANDLE)) ERR( "failed: %#x\n", status );
448 console->signaled = signal;
449 console->read_ioctl = 0;
450 console->pending_read = 0;
451 return status;
454 static NTSTATUS read_console_input( struct console *console, size_t out_size )
456 size_t count = min( out_size / sizeof(INPUT_RECORD), console->record_count );
458 TRACE("count %u\n", count);
460 read_complete( console, STATUS_SUCCESS, console->records, count * sizeof(*console->records),
461 console->record_count > count );
463 if (count < console->record_count)
464 memmove( console->records, console->records + count,
465 (console->record_count - count) * sizeof(*console->records) );
466 console->record_count -= count;
467 return STATUS_SUCCESS;
470 static void read_from_buffer( struct console *console, size_t out_size )
472 size_t len, read_len = 0;
473 char *buf = NULL;
475 switch( console->read_ioctl )
477 case IOCTL_CONDRV_READ_CONSOLE:
478 out_size = min( out_size, console->read_buffer_count * sizeof(WCHAR) );
479 read_complete( console, STATUS_SUCCESS, console->read_buffer, out_size, console->record_count != 0 );
480 read_len = out_size / sizeof(WCHAR);
481 break;
482 case IOCTL_CONDRV_READ_FILE:
483 read_len = len = 0;
484 while (read_len < console->read_buffer_count && len < out_size)
486 len += WideCharToMultiByte( console->input_cp, 0, console->read_buffer + read_len, 1, NULL, 0, NULL, NULL );
487 read_len++;
489 if (len)
491 if (!(buf = malloc( len )))
493 read_complete( console, STATUS_NO_MEMORY, NULL, 0, console->record_count != 0 );
494 return;
496 WideCharToMultiByte( console->input_cp, 0, console->read_buffer, read_len, buf, len, NULL, NULL );
498 len = min( out_size, len );
499 read_complete( console, STATUS_SUCCESS, buf, len, console->record_count != 0 );
500 free( buf );
501 break;
504 if (read_len < console->read_buffer_count)
506 memmove( console->read_buffer, console->read_buffer + read_len,
507 (console->read_buffer_count - read_len) * sizeof(WCHAR) );
509 if (!(console->read_buffer_count -= read_len))
510 free( console->read_buffer );
513 static void append_input_history( struct console *console, const WCHAR *str, size_t len )
515 struct history_line *ptr;
517 if (!console->history_size) return;
519 /* don't duplicate entry */
520 if (console->history_mode && console->history_index &&
521 console->history[console->history_index - 1]->len == len &&
522 !memcmp( console->history[console->history_index - 1]->text, str, len ))
523 return;
525 if (!(ptr = malloc( offsetof( struct history_line, text[len / sizeof(WCHAR)] )))) return;
526 ptr->len = len;
527 memcpy( ptr->text, str, len );
529 if (console->history_index < console->history_size)
531 console->history[console->history_index++] = ptr;
533 else
535 free( console->history[0]) ;
536 memmove( &console->history[0], &console->history[1],
537 (console->history_size - 1) * sizeof(*console->history) );
538 console->history[console->history_size - 1] = ptr;
542 static void edit_line_update( struct console *console, unsigned int begin, unsigned int length )
544 struct edit_line *ctx = &console->edit_line;
545 if (!length) return;
546 ctx->update_begin = min( ctx->update_begin, begin );
547 ctx->update_end = max( ctx->update_end, begin + length - 1 );
550 static BOOL edit_line_grow( struct console *console, size_t length )
552 struct edit_line *ctx = &console->edit_line;
553 WCHAR *new_buf;
554 size_t new_size;
556 if (ctx->len + length < ctx->size) return TRUE;
558 /* round up size to 32 byte-WCHAR boundary */
559 new_size = (ctx->len + length + 32) & ~31;
560 if (!(new_buf = realloc( ctx->buf, sizeof(WCHAR) * new_size )))
562 ctx->status = STATUS_NO_MEMORY;
563 return FALSE;
565 ctx->buf = new_buf;
566 ctx->size = new_size;
567 return TRUE;
570 static void edit_line_delete( struct console *console, int begin, int end )
572 struct edit_line *ctx = &console->edit_line;
573 unsigned int len = end - begin;
575 edit_line_update( console, begin, ctx->len - begin );
576 if (end < ctx->len)
577 memmove( &ctx->buf[begin], &ctx->buf[end], (ctx->len - end) * sizeof(WCHAR));
578 ctx->len -= len;
579 edit_line_update( console, 0, ctx->len );
580 ctx->buf[ctx->len] = 0;
583 static void edit_line_insert( struct console *console, const WCHAR *str, unsigned int len )
585 struct edit_line *ctx = &console->edit_line;
586 unsigned int update_len;
588 if (!len) return;
589 if (ctx->insert_mode)
591 if (!edit_line_grow( console, len )) return;
592 if (ctx->len > ctx->cursor)
593 memmove( &ctx->buf[ctx->cursor + len], &ctx->buf[ctx->cursor],
594 (ctx->len - ctx->cursor) * sizeof(WCHAR) );
595 ctx->len += len;
596 update_len = ctx->len - ctx->cursor;
598 else
600 if (ctx->cursor + len > ctx->len)
602 if (!edit_line_grow( console, (ctx->cursor + len) - ctx->len) )
603 return;
604 ctx->len = ctx->cursor + len;
606 update_len = len;
608 memcpy( &ctx->buf[ctx->cursor], str, len * sizeof(WCHAR) );
609 ctx->buf[ctx->len] = 0;
610 edit_line_update( console, ctx->cursor, update_len );
611 ctx->cursor += len;
614 static void edit_line_save_yank( struct console *console, unsigned int begin, unsigned int end )
616 struct edit_line *ctx = &console->edit_line;
617 unsigned int len = end - begin;
618 if (len <= 0) return;
620 free(ctx->yanked);
621 ctx->yanked = malloc( (len + 1) * sizeof(WCHAR) );
622 if (!ctx->yanked)
624 ctx->status = STATUS_NO_MEMORY;
625 return;
627 memcpy( ctx->yanked, &ctx->buf[begin], len * sizeof(WCHAR) );
628 ctx->yanked[len] = 0;
631 static int edit_line_left_word_transition( struct console *console, int offset )
633 offset--;
634 while (offset >= 0 && !iswalnum( console->edit_line.buf[offset] )) offset--;
635 while (offset >= 0 && iswalnum( console->edit_line.buf[offset] )) offset--;
636 if (offset >= 0) offset++;
637 return max( offset, 0 );
640 static int edit_line_right_word_transition( struct console *console, int offset )
642 offset++;
643 while (offset <= console->edit_line.len && iswalnum( console->edit_line.buf[offset] ))
644 offset++;
645 while (offset <= console->edit_line.len && !iswalnum( console->edit_line.buf[offset] ))
646 offset++;
647 return min(offset, console->edit_line.len);
650 static WCHAR *edit_line_history( struct console *console, unsigned int index )
652 WCHAR *ptr = NULL;
654 if (index < console->history_index)
656 if ((ptr = malloc( console->history[index]->len + sizeof(WCHAR) )))
658 memcpy( ptr, console->history[index]->text, console->history[index]->len );
659 ptr[console->history[index]->len / sizeof(WCHAR)] = 0;
662 else if(console->edit_line.current_history)
664 if ((ptr = malloc( (lstrlenW(console->edit_line.current_history) + 1) * sizeof(WCHAR) )))
665 lstrcpyW( ptr, console->edit_line.current_history );
667 return ptr;
670 static void edit_line_move_to_history( struct console *console, int index )
672 struct edit_line *ctx = &console->edit_line;
673 WCHAR *line = edit_line_history(console, index);
674 size_t len = line ? lstrlenW(line) : 0;
676 /* save current line edition for recall when needed */
677 if (ctx->history_index == console->history_index)
679 free( ctx->current_history );
680 ctx->current_history = malloc( (ctx->len + 1) * sizeof(WCHAR) );
681 if (ctx->current_history)
683 memcpy( ctx->current_history, ctx->buf, (ctx->len + 1) * sizeof(WCHAR) );
685 else
687 ctx->status = STATUS_NO_MEMORY;
688 return;
692 /* need to clean also the screen if new string is shorter than old one */
693 edit_line_delete(console, 0, ctx->len);
694 ctx->cursor = 0;
695 /* insert new string */
696 if (edit_line_grow(console, len + 1))
698 edit_line_insert( console, line, len );
699 ctx->history_index = index;
701 free(line);
704 static void edit_line_find_in_history( struct console *console )
706 struct edit_line *ctx = &console->edit_line;
707 int start_pos = ctx->history_index;
708 unsigned int len, oldoffset;
709 WCHAR *line;
711 if (!console->history_index) return;
712 if (ctx->history_index && ctx->history_index == console->history_index)
714 start_pos--;
715 ctx->history_index--;
720 line = edit_line_history(console, ctx->history_index);
722 if (ctx->history_index) ctx->history_index--;
723 else ctx->history_index = console->history_index - 1;
725 len = lstrlenW(line) + 1;
726 if (len >= ctx->cursor && !memcmp( ctx->buf, line, ctx->cursor * sizeof(WCHAR) ))
728 /* need to clean also the screen if new string is shorter than old one */
729 edit_line_delete(console, 0, ctx->len);
731 if (edit_line_grow(console, len))
733 oldoffset = ctx->cursor;
734 ctx->cursor = 0;
735 edit_line_insert( console, line, len - 1 );
736 ctx->cursor = oldoffset;
737 free(line);
738 return;
741 free(line);
743 while (ctx->history_index != start_pos);
746 static void edit_line_move_left( struct console *console )
748 if (console->edit_line.cursor > 0) console->edit_line.cursor--;
751 static void edit_line_move_right( struct console *console )
753 struct edit_line *ctx = &console->edit_line;
754 if (ctx->cursor < ctx->len) ctx->cursor++;
757 static void edit_line_move_left_word( struct console *console )
759 console->edit_line.cursor = edit_line_left_word_transition( console, console->edit_line.cursor );
762 static void edit_line_move_right_word( struct console *console )
764 console->edit_line.cursor = edit_line_right_word_transition( console, console->edit_line.cursor );
767 static void edit_line_move_home( struct console *console )
769 console->edit_line.cursor = 0;
772 static void edit_line_move_end( struct console *console )
774 console->edit_line.cursor = console->edit_line.len;
777 static void edit_line_set_mark( struct console *console )
779 console->edit_line.mark = console->edit_line.cursor;
782 static void edit_line_exchange_mark( struct console *console )
784 struct edit_line *ctx = &console->edit_line;
785 unsigned int cursor;
787 if (ctx->mark > ctx->len) return;
788 cursor = ctx->cursor;
789 ctx->cursor = ctx->mark;
790 ctx->mark = cursor;
793 static void edit_line_copy_marked_zone( struct console *console )
795 struct edit_line *ctx = &console->edit_line;
796 unsigned int begin, end;
798 if (ctx->mark > ctx->len || ctx->mark == ctx->cursor) return;
799 if (ctx->mark > ctx->cursor)
801 begin = ctx->cursor;
802 end = ctx->mark;
804 else
806 begin = ctx->mark;
807 end = ctx->cursor;
809 edit_line_save_yank( console, begin, end );
812 static void edit_line_transpose_char( struct console *console )
814 struct edit_line *ctx = &console->edit_line;
815 WCHAR c;
817 if (!ctx->cursor || ctx->cursor == ctx->len) return;
819 c = ctx->buf[ctx->cursor];
820 ctx->buf[ctx->cursor] = ctx->buf[ctx->cursor - 1];
821 ctx->buf[ctx->cursor - 1] = c;
823 edit_line_update( console, ctx->cursor - 1, 2 );
824 ctx->cursor++;
827 static void edit_line_transpose_words( struct console *console )
829 struct edit_line *ctx = &console->edit_line;
830 unsigned int left_offset = edit_line_left_word_transition( console, ctx->cursor );
831 unsigned int right_offset = edit_line_right_word_transition( console, ctx->cursor );
832 if (left_offset < ctx->cursor && right_offset > ctx->cursor)
834 unsigned int len_r = right_offset - ctx->cursor;
835 unsigned int len_l = ctx->cursor - left_offset;
836 char *tmp = malloc( len_r * sizeof(WCHAR) );
837 if (!tmp)
839 ctx->status = STATUS_NO_MEMORY;
840 return;
843 memcpy( tmp, &ctx->buf[ctx->cursor], len_r * sizeof(WCHAR) );
844 memmove( &ctx->buf[left_offset + len_r], &ctx->buf[left_offset],
845 len_l * sizeof(WCHAR) );
846 memcpy( &ctx->buf[left_offset], tmp, len_r * sizeof(WCHAR) );
847 free(tmp);
849 edit_line_update( console, left_offset, len_l + len_r );
850 ctx->cursor = right_offset;
854 static void edit_line_lower_case_word( struct console *console )
856 struct edit_line *ctx = &console->edit_line;
857 unsigned int new_offset = edit_line_right_word_transition( console, ctx->cursor );
858 if (new_offset != ctx->cursor)
860 CharLowerBuffW( ctx->buf + ctx->cursor, new_offset - ctx->cursor + 1 );
861 edit_line_update( console, ctx->cursor, new_offset - ctx->cursor + 1 );
862 ctx->cursor = new_offset;
866 static void edit_line_upper_case_word( struct console *console )
868 struct edit_line *ctx = &console->edit_line;
869 unsigned int new_offset = edit_line_right_word_transition( console, ctx->cursor );
870 if (new_offset != ctx->cursor)
872 CharUpperBuffW( ctx->buf + ctx->cursor, new_offset - ctx->cursor + 1 );
873 edit_line_update( console, ctx->cursor, new_offset - ctx->cursor + 1 );
874 ctx->cursor = new_offset;
878 static void edit_line_capitalize_word( struct console *console )
880 struct edit_line *ctx = &console->edit_line;
881 unsigned int new_offset = edit_line_right_word_transition( console, ctx->cursor );
882 if (new_offset != ctx->cursor)
884 CharUpperBuffW( ctx->buf + ctx->cursor, 1 );
885 CharLowerBuffW( ctx->buf + ctx->cursor + 1, new_offset - ctx->cursor );
886 edit_line_update( console, ctx->cursor, new_offset - ctx->cursor + 1 );
887 ctx->cursor = new_offset;
891 static void edit_line_yank( struct console *console )
893 struct edit_line *ctx = &console->edit_line;
894 if (ctx->yanked) edit_line_insert( console, ctx->yanked, wcslen(ctx->yanked) );
897 static void edit_line_kill_suffix( struct console *console )
899 struct edit_line *ctx = &console->edit_line;
900 edit_line_save_yank( console, ctx->cursor, ctx->len );
901 edit_line_delete( console, ctx->cursor, ctx->len );
904 static void edit_line_kill_prefix( struct console *console )
906 struct edit_line *ctx = &console->edit_line;
907 if (ctx->cursor)
909 edit_line_save_yank( console, 0, ctx->cursor );
910 edit_line_delete( console, 0, ctx->cursor );
911 ctx->cursor = 0;
915 static void edit_line_kill_marked_zone( struct console *console )
917 struct edit_line *ctx = &console->edit_line;
918 unsigned int begin, end;
920 if (ctx->mark > ctx->len || ctx->mark == ctx->cursor)
921 return;
922 if (ctx->mark > ctx->cursor)
924 begin = ctx->cursor;
925 end = ctx->mark;
927 else
929 begin = ctx->mark;
930 end = ctx->cursor;
932 edit_line_save_yank( console, begin, end );
933 edit_line_delete( console, begin, end );
934 ctx->cursor = begin;
937 static void edit_line_delete_prev( struct console *console )
939 struct edit_line *ctx = &console->edit_line;
940 if (ctx->cursor)
942 edit_line_delete( console, ctx->cursor - 1, ctx->cursor );
943 ctx->cursor--;
947 static void edit_line_delete_char( struct console *console )
949 struct edit_line *ctx = &console->edit_line;
950 if (ctx->cursor < ctx->len)
951 edit_line_delete( console, ctx->cursor, ctx->cursor + 1 );
954 static void edit_line_delete_left_word( struct console *console )
956 struct edit_line *ctx = &console->edit_line;
957 unsigned int new_offset = edit_line_left_word_transition( console, ctx->cursor );
958 if (new_offset != ctx->cursor)
960 edit_line_delete( console, new_offset, ctx->cursor );
961 ctx->cursor = new_offset;
965 static void edit_line_delete_right_word( struct console *console )
967 struct edit_line *ctx = &console->edit_line;
968 unsigned int new_offset = edit_line_right_word_transition( console, ctx->cursor );
969 if (new_offset != ctx->cursor)
971 edit_line_delete( console, ctx->cursor, new_offset );
975 static void edit_line_move_to_prev_hist( struct console *console )
977 if (console->edit_line.history_index)
978 edit_line_move_to_history( console, console->edit_line.history_index - 1 );
981 static void edit_line_move_to_next_hist( struct console *console )
983 if (console->edit_line.history_index < console->history_index)
984 edit_line_move_to_history( console, console->edit_line.history_index + 1 );
987 static void edit_line_move_to_first_hist( struct console *console )
989 if (console->edit_line.history_index)
990 edit_line_move_to_history( console, 0 );
993 static void edit_line_move_to_last_hist( struct console *console )
995 if (console->edit_line.history_index != console->history_index)
996 edit_line_move_to_history( console, console->history_index );
999 static void edit_line_redraw( struct console *console )
1001 if (console->mode & ENABLE_ECHO_INPUT)
1002 edit_line_update( console, 0, console->edit_line.len );
1005 static void edit_line_toggle_insert( struct console *console )
1007 struct edit_line *ctx = &console->edit_line;
1008 ctx->insert_key = !ctx->insert_key;
1009 console->active->cursor_size = ctx->insert_key ? 100 : 25;
1012 static void edit_line_done( struct console *console )
1014 console->edit_line.status = STATUS_SUCCESS;
1017 struct edit_line_key_entry
1019 WCHAR val; /* vk or unicode char */
1020 void (*func)( struct console *console );
1023 struct edit_line_key_map
1025 DWORD key_state; /* keyState (from INPUT_RECORD) to match */
1026 BOOL is_char; /* check vk or char */
1027 const struct edit_line_key_entry *entries;
1030 #define CTRL(x) ((x) - '@')
1031 static const struct edit_line_key_entry std_key_map[] =
1033 { VK_BACK, edit_line_delete_prev },
1034 { VK_RETURN, edit_line_done },
1035 { VK_DELETE, edit_line_delete_char },
1036 { 0 }
1039 static const struct edit_line_key_entry emacs_key_map_ctrl[] =
1041 { CTRL('@'), edit_line_set_mark },
1042 { CTRL('A'), edit_line_move_home },
1043 { CTRL('B'), edit_line_move_left },
1044 { CTRL('D'), edit_line_delete_char },
1045 { CTRL('E'), edit_line_move_end },
1046 { CTRL('F'), edit_line_move_right },
1047 { CTRL('H'), edit_line_delete_prev },
1048 { CTRL('J'), edit_line_done },
1049 { CTRL('K'), edit_line_kill_suffix },
1050 { CTRL('L'), edit_line_redraw },
1051 { CTRL('M'), edit_line_done },
1052 { CTRL('N'), edit_line_move_to_next_hist },
1053 { CTRL('P'), edit_line_move_to_prev_hist },
1054 { CTRL('T'), edit_line_transpose_char },
1055 { CTRL('W'), edit_line_kill_marked_zone },
1056 { CTRL('X'), edit_line_exchange_mark },
1057 { CTRL('Y'), edit_line_yank },
1058 { 0 }
1061 static const struct edit_line_key_entry emacs_key_map_alt[] =
1063 { 0x7f, edit_line_delete_left_word },
1064 { '<', edit_line_move_to_first_hist },
1065 { '>', edit_line_move_to_last_hist },
1066 { 'b', edit_line_move_left_word },
1067 { 'c', edit_line_capitalize_word },
1068 { 'd', edit_line_delete_right_word },
1069 { 'f', edit_line_move_right_word },
1070 { 'l', edit_line_lower_case_word },
1071 { 't', edit_line_transpose_words },
1072 { 'u', edit_line_upper_case_word },
1073 { 'w', edit_line_copy_marked_zone },
1074 { 0 }
1077 static const struct edit_line_key_entry emacs_std_key_map[] =
1079 { VK_PRIOR, edit_line_move_to_prev_hist },
1080 { VK_NEXT, edit_line_move_to_next_hist },
1081 { VK_END, edit_line_move_end },
1082 { VK_HOME, edit_line_move_home },
1083 { VK_RIGHT, edit_line_move_right },
1084 { VK_LEFT, edit_line_move_left },
1085 { VK_INSERT, edit_line_toggle_insert },
1086 { 0 }
1089 static const struct edit_line_key_map emacs_key_map[] =
1091 { 0, 0, std_key_map },
1092 { 0, 0, emacs_std_key_map },
1093 { RIGHT_ALT_PRESSED, 1, emacs_key_map_alt },
1094 { LEFT_ALT_PRESSED, 1, emacs_key_map_alt },
1095 { RIGHT_CTRL_PRESSED, 1, emacs_key_map_ctrl },
1096 { LEFT_CTRL_PRESSED, 1, emacs_key_map_ctrl },
1097 { 0 }
1100 static const struct edit_line_key_entry win32_std_key_map[] =
1102 { VK_LEFT, edit_line_move_left },
1103 { VK_RIGHT, edit_line_move_right },
1104 { VK_HOME, edit_line_move_home },
1105 { VK_END, edit_line_move_end },
1106 { VK_UP, edit_line_move_to_prev_hist },
1107 { VK_DOWN, edit_line_move_to_next_hist },
1108 { VK_INSERT, edit_line_toggle_insert },
1109 { VK_F8, edit_line_find_in_history },
1110 { 0 }
1113 static const struct edit_line_key_entry win32_key_map_ctrl[] =
1115 { VK_LEFT, edit_line_move_left_word },
1116 { VK_RIGHT, edit_line_move_right_word },
1117 { VK_END, edit_line_kill_suffix },
1118 { VK_HOME, edit_line_kill_prefix },
1119 { 0 }
1122 static const struct edit_line_key_map win32_key_map[] =
1124 { 0, 0, std_key_map },
1125 { SHIFT_PRESSED, 0, std_key_map },
1126 { 0, 0, win32_std_key_map },
1127 { RIGHT_CTRL_PRESSED, 0, win32_key_map_ctrl },
1128 { LEFT_CTRL_PRESSED, 0, win32_key_map_ctrl },
1129 { 0 }
1131 #undef CTRL
1133 static unsigned int edit_line_string_width( const WCHAR *str, unsigned int len)
1135 unsigned int i, offset = 0;
1136 for (i = 0; i < len; i++) offset += str[i] < ' ' ? 2 : 1;
1137 return offset;
1140 static void update_read_output( struct console *console )
1142 struct screen_buffer *screen_buffer = console->active;
1143 struct edit_line *ctx = &console->edit_line;
1144 int offset = 0, j, end_offset;
1145 RECT update_rect;
1147 empty_update_rect( screen_buffer, &update_rect );
1149 if (ctx->update_end >= ctx->update_begin)
1151 TRACE( "update %d-%d %s\n", ctx->update_begin, ctx->update_end,
1152 debugstr_wn( ctx->buf + ctx->update_begin, ctx->update_end - ctx->update_begin + 1 ));
1154 hide_tty_cursor( screen_buffer->console );
1156 offset = edit_line_string_width( ctx->buf, ctx->update_begin );
1157 screen_buffer->cursor_x = (ctx->home_x + offset) % screen_buffer->width;
1158 screen_buffer->cursor_y = ctx->home_y + (ctx->home_x + offset) / screen_buffer->width;
1159 for (j = ctx->update_begin; j <= ctx->update_end; j++)
1161 if (screen_buffer->cursor_y >= screen_buffer->height && !ctx->home_y) break;
1162 if (j >= ctx->len) break;
1163 if (ctx->buf[j] < ' ')
1165 write_char( screen_buffer, '^', &update_rect, &ctx->home_y );
1166 write_char( screen_buffer, '@' + ctx->buf[j], &update_rect, &ctx->home_y );
1167 offset += 2;
1169 else
1171 write_char( screen_buffer, ctx->buf[j], &update_rect, &ctx->home_y );
1172 offset++;
1175 end_offset = ctx->end_offset;
1176 ctx->end_offset = offset;
1177 if (j >= ctx->len)
1179 /* clear trailing characters if buffer was shortened */
1180 while (offset < end_offset && screen_buffer->cursor_y < screen_buffer->height)
1182 write_char( screen_buffer, ' ', &update_rect, &ctx->home_y );
1183 offset++;
1188 if (!ctx->status)
1190 offset = edit_line_string_width( ctx->buf, ctx->len );
1191 screen_buffer->cursor_x = 0;
1192 screen_buffer->cursor_y = ctx->home_y + (ctx->home_x + offset) / screen_buffer->width;
1193 if (++screen_buffer->cursor_y >= screen_buffer->height)
1194 new_line( screen_buffer, &update_rect );
1196 else
1198 offset = edit_line_string_width( ctx->buf, ctx->cursor );
1199 screen_buffer->cursor_y = ctx->home_y + (ctx->home_x + offset) / screen_buffer->width;
1200 if (screen_buffer->cursor_y < screen_buffer->height)
1202 screen_buffer->cursor_x = (ctx->home_x + offset) % screen_buffer->width;
1204 else
1206 screen_buffer->cursor_x = screen_buffer->width - 1;
1207 screen_buffer->cursor_y = screen_buffer->height - 1;
1211 /* always try to use relative cursor positions in UNIX mode so that it works even if cursor
1212 * position is out of sync */
1213 if (update_rect.left <= update_rect.right && update_rect.top <= update_rect.bottom)
1215 if (console->is_unix)
1216 set_tty_cursor_relative( screen_buffer->console, update_rect.left, update_rect.top );
1217 update_output( screen_buffer, &update_rect );
1218 scroll_to_cursor( screen_buffer );
1220 if (console->is_unix)
1221 set_tty_cursor_relative( screen_buffer->console, screen_buffer->cursor_x, screen_buffer->cursor_y );
1222 tty_sync( screen_buffer->console );
1223 update_window_config( screen_buffer->console, TRUE );
1226 static NTSTATUS process_console_input( struct console *console )
1228 struct edit_line *ctx = &console->edit_line;
1229 unsigned int i;
1231 switch (console->read_ioctl)
1233 case IOCTL_CONDRV_READ_INPUT:
1234 if (console->record_count) read_console_input( console, console->pending_read );
1235 return STATUS_SUCCESS;
1236 case IOCTL_CONDRV_READ_CONSOLE:
1237 case IOCTL_CONDRV_READ_FILE:
1238 break;
1239 default:
1240 assert( !console->read_ioctl );
1241 if (console->record_count && !console->signaled)
1242 read_complete( console, STATUS_PENDING, NULL, 0, TRUE ); /* signal server */
1243 return STATUS_SUCCESS;
1246 ctx->update_begin = ctx->len + 1;
1247 ctx->update_end = 0;
1249 for (i = 0; i < console->record_count && ctx->status == STATUS_PENDING; i++)
1251 void (*func)( struct console *console ) = NULL;
1252 INPUT_RECORD ir = console->records[i];
1254 if (ir.EventType != KEY_EVENT || !ir.Event.KeyEvent.bKeyDown) continue;
1256 TRACE( "key code=%02x scan=%02x char=%02x state=%08x\n",
1257 ir.Event.KeyEvent.wVirtualKeyCode, ir.Event.KeyEvent.wVirtualScanCode,
1258 ir.Event.KeyEvent.uChar.UnicodeChar, ir.Event.KeyEvent.dwControlKeyState );
1260 if (console->mode & ENABLE_LINE_INPUT)
1262 const struct edit_line_key_entry *entry;
1263 const struct edit_line_key_map *map;
1264 unsigned int state;
1266 /* mask out some bits which don't interest us */
1267 state = ir.Event.KeyEvent.dwControlKeyState & ~(NUMLOCK_ON|SCROLLLOCK_ON|CAPSLOCK_ON|ENHANCED_KEY);
1269 func = NULL;
1270 for (map = console->edition_mode ? emacs_key_map : win32_key_map; map->entries != NULL; map++)
1272 if (map->key_state != state)
1273 continue;
1274 if (map->is_char)
1276 for (entry = &map->entries[0]; entry->func != 0; entry++)
1277 if (entry->val == ir.Event.KeyEvent.uChar.UnicodeChar) break;
1279 else
1281 for (entry = &map->entries[0]; entry->func != 0; entry++)
1282 if (entry->val == ir.Event.KeyEvent.wVirtualKeyCode) break;
1285 if (entry->func)
1287 func = entry->func;
1288 break;
1293 ctx->insert_mode = ((console->mode & (ENABLE_INSERT_MODE | ENABLE_EXTENDED_FLAGS)) ==
1294 (ENABLE_INSERT_MODE | ENABLE_EXTENDED_FLAGS))
1295 ^ ctx->insert_key;
1297 if (func) func( console );
1298 else if (ir.Event.KeyEvent.uChar.UnicodeChar)
1299 edit_line_insert( console, &ir.Event.KeyEvent.uChar.UnicodeChar, 1 );
1301 if (!(console->mode & ENABLE_LINE_INPUT) && ctx->status == STATUS_PENDING)
1303 if (console->read_ioctl == IOCTL_CONDRV_READ_FILE)
1305 if (WideCharToMultiByte(console->input_cp, 0, ctx->buf, ctx->len, NULL, 0, NULL, NULL)
1306 >= console->pending_read)
1307 ctx->status = STATUS_SUCCESS;
1309 else if (ctx->len >= console->pending_read / sizeof(WCHAR))
1310 ctx->status = STATUS_SUCCESS;
1314 if (console->record_count > i) memmove( console->records, console->records + i,
1315 (console->record_count - i) * sizeof(*console->records) );
1316 console->record_count -= i;
1318 if (ctx->status == STATUS_PENDING && !(console->mode & ENABLE_LINE_INPUT) && ctx->len)
1319 ctx->status = STATUS_SUCCESS;
1321 if (console->mode & ENABLE_ECHO_INPUT) update_read_output( console );
1322 if (ctx->status == STATUS_PENDING) return STATUS_SUCCESS;
1324 if (!ctx->status && (console->mode & ENABLE_LINE_INPUT))
1326 if (ctx->len) append_input_history( console, ctx->buf, ctx->len * sizeof(WCHAR) );
1327 if (edit_line_grow(console, 2))
1329 ctx->buf[ctx->len++] = '\r';
1330 ctx->buf[ctx->len++] = '\n';
1331 ctx->buf[ctx->len] = 0;
1332 TRACE( "return %s\n", debugstr_wn( ctx->buf, ctx->len ));
1336 console->read_buffer = ctx->buf;
1337 console->read_buffer_count = ctx->len;
1338 console->read_buffer_size = ctx->size;
1340 if (ctx->status) read_complete( console, ctx->status, NULL, 0, console->record_count );
1341 else read_from_buffer( console, console->pending_read );
1343 /* reset context */
1344 free( ctx->yanked );
1345 free( ctx->current_history );
1346 memset( &console->edit_line, 0, sizeof(console->edit_line) );
1347 return STATUS_SUCCESS;
1350 static NTSTATUS read_console( struct console *console, unsigned int ioctl, size_t out_size )
1352 TRACE("\n");
1354 if (out_size > INT_MAX)
1356 read_complete( console, STATUS_NO_MEMORY, NULL, 0, console->record_count );
1357 return STATUS_NO_MEMORY;
1360 console->read_ioctl = ioctl;
1361 if (!out_size || console->read_buffer_count)
1363 read_from_buffer( console, out_size );
1364 return STATUS_SUCCESS;
1367 console->edit_line.history_index = console->history_index;
1368 console->edit_line.home_x = console->active->cursor_x;
1369 console->edit_line.home_y = console->active->cursor_y;
1370 console->edit_line.status = STATUS_PENDING;
1371 if (edit_line_grow( console, 1 )) console->edit_line.buf[0] = 0;
1373 console->pending_read = out_size;
1374 return process_console_input( console );
1377 /* add input events to a console input queue */
1378 NTSTATUS write_console_input( struct console *console, const INPUT_RECORD *records,
1379 unsigned int count, BOOL flush )
1381 TRACE( "%u\n", count );
1383 if (!count) return STATUS_SUCCESS;
1384 if (console->record_count + count > console->record_size)
1386 INPUT_RECORD *new_rec;
1387 if (!(new_rec = realloc( console->records, (console->record_size * 2 + count) * sizeof(INPUT_RECORD) )))
1388 return STATUS_NO_MEMORY;
1389 console->records = new_rec;
1390 console->record_size = console->record_size * 2 + count;
1392 memcpy( console->records + console->record_count, records, count * sizeof(INPUT_RECORD) );
1394 if (console->mode & ENABLE_PROCESSED_INPUT)
1396 unsigned int i = 0;
1397 while (i < count)
1399 if (records[i].EventType == KEY_EVENT &&
1400 records[i].Event.KeyEvent.uChar.UnicodeChar == 'C' - 64 &&
1401 !(records[i].Event.KeyEvent.dwControlKeyState & ENHANCED_KEY))
1403 if (i != count - 1)
1404 memcpy( &console->records[console->record_count + i],
1405 &console->records[console->record_count + i + 1],
1406 (count - i - 1) * sizeof(INPUT_RECORD) );
1407 count--;
1408 if (records[i].Event.KeyEvent.bKeyDown)
1410 struct condrv_ctrl_event ctrl_event;
1411 IO_STATUS_BLOCK io;
1413 ctrl_event.event = CTRL_C_EVENT;
1414 ctrl_event.group_id = 0;
1415 NtDeviceIoControlFile( console->server, NULL, NULL, NULL, &io, IOCTL_CONDRV_CTRL_EVENT,
1416 &ctrl_event, sizeof(ctrl_event), NULL, 0 );
1420 else i++;
1423 console->record_count += count;
1424 return flush ? process_console_input( console ) : STATUS_SUCCESS;
1427 static void set_key_input_record( INPUT_RECORD *record, WCHAR ch, unsigned int vk, BOOL is_down, unsigned int ctrl_state )
1429 record->EventType = KEY_EVENT;
1430 record->Event.KeyEvent.bKeyDown = is_down;
1431 record->Event.KeyEvent.wRepeatCount = 1;
1432 record->Event.KeyEvent.uChar.UnicodeChar = ch;
1433 record->Event.KeyEvent.wVirtualKeyCode = vk;
1434 record->Event.KeyEvent.wVirtualScanCode = MapVirtualKeyW( vk, MAPVK_VK_TO_VSC );
1435 record->Event.KeyEvent.dwControlKeyState = ctrl_state;
1438 static NTSTATUS key_press( struct console *console, WCHAR ch, unsigned int vk, unsigned int ctrl_state )
1440 INPUT_RECORD records[8];
1441 unsigned int count = 0, ctrl = 0;
1443 if (ctrl_state & SHIFT_PRESSED)
1445 ctrl |= SHIFT_PRESSED;
1446 set_key_input_record( &records[count++], 0, VK_SHIFT, TRUE, ctrl );
1448 if (ctrl_state & LEFT_ALT_PRESSED)
1450 ctrl |= LEFT_ALT_PRESSED;
1451 set_key_input_record( &records[count++], 0, VK_MENU, TRUE, ctrl );
1453 if (ctrl_state & LEFT_CTRL_PRESSED)
1455 ctrl |= LEFT_CTRL_PRESSED;
1456 set_key_input_record( &records[count++], 0, VK_CONTROL, TRUE, ctrl );
1459 set_key_input_record( &records[count++], ch, vk, TRUE, ctrl );
1460 set_key_input_record( &records[count++], ch, vk, FALSE, ctrl );
1462 if (ctrl & LEFT_CTRL_PRESSED)
1464 ctrl &= ~LEFT_CTRL_PRESSED;
1465 set_key_input_record( &records[count++], 0, VK_CONTROL, FALSE, ctrl );
1467 if (ctrl & LEFT_ALT_PRESSED)
1469 ctrl &= ~LEFT_ALT_PRESSED;
1470 set_key_input_record( &records[count++], 0, VK_MENU, FALSE, ctrl );
1472 if (ctrl & SHIFT_PRESSED)
1474 ctrl &= ~SHIFT_PRESSED;
1475 set_key_input_record( &records[count++], 0, VK_SHIFT, FALSE, ctrl );
1478 return write_console_input( console, records, count, FALSE );
1481 static void char_key_press( struct console *console, WCHAR ch, unsigned int ctrl )
1483 unsigned int vk = VkKeyScanW( ch );
1484 if (vk == ~0) vk = 0;
1485 if (vk & 0x0100) ctrl |= SHIFT_PRESSED;
1486 if (vk & 0x0200) ctrl |= LEFT_CTRL_PRESSED;
1487 if (vk & 0x0400) ctrl |= LEFT_ALT_PRESSED;
1488 vk &= 0xff;
1489 key_press( console, ch, vk, ctrl );
1492 static unsigned int escape_char_to_vk( WCHAR ch )
1494 switch (ch)
1496 case 'A': return VK_UP;
1497 case 'B': return VK_DOWN;
1498 case 'C': return VK_RIGHT;
1499 case 'D': return VK_LEFT;
1500 case 'H': return VK_HOME;
1501 case 'F': return VK_END;
1502 case 'P': return VK_F1;
1503 case 'Q': return VK_F2;
1504 case 'R': return VK_F3;
1505 case 'S': return VK_F4;
1506 default: return 0;
1510 static unsigned int escape_number_to_vk( unsigned int n )
1512 switch(n)
1514 case 2: return VK_INSERT;
1515 case 3: return VK_DELETE;
1516 case 5: return VK_PRIOR;
1517 case 6: return VK_NEXT;
1518 case 15: return VK_F5;
1519 case 17: return VK_F6;
1520 case 18: return VK_F7;
1521 case 19: return VK_F8;
1522 case 20: return VK_F9;
1523 case 21: return VK_F10;
1524 case 23: return VK_F11;
1525 case 24: return VK_F12;
1526 default: return 0;
1530 static unsigned int convert_modifiers( unsigned int n )
1532 unsigned int ctrl = 0;
1533 if (!n || n > 16) return 0;
1534 n--;
1535 if (n & 1) ctrl |= SHIFT_PRESSED;
1536 if (n & 2) ctrl |= LEFT_ALT_PRESSED;
1537 if (n & 4) ctrl |= LEFT_CTRL_PRESSED;
1538 return ctrl;
1541 static unsigned int process_csi_sequence( struct console *console, const WCHAR *buf, size_t size )
1543 unsigned int n, count = 0, params[8], params_cnt = 0, vk;
1545 for (;;)
1547 n = 0;
1548 while (count < size && '0' <= buf[count] && buf[count] <= '9')
1549 n = n * 10 + buf[count++] - '0';
1550 if (params_cnt < ARRAY_SIZE(params)) params[params_cnt++] = n;
1551 else FIXME( "too many params, skipping %u\n", n );
1552 if (count == size) return 0;
1553 if (buf[count] != ';') break;
1554 if (++count == size) return 0;
1557 if ((vk = escape_char_to_vk( buf[count] )))
1559 key_press( console, 0, vk, params_cnt >= 2 ? convert_modifiers( params[1] ) : 0 );
1560 return count + 1;
1563 switch (buf[count])
1565 case '~':
1566 vk = escape_number_to_vk( params[0] );
1567 key_press( console, 0, vk, params_cnt == 2 ? convert_modifiers( params[1] ) : 0 );
1568 return count + 1;
1570 default:
1571 FIXME( "unhandled sequence %s\n", debugstr_wn( buf, size ));
1572 return 0;
1576 static unsigned int process_input_escape( struct console *console, const WCHAR *buf, size_t size )
1578 unsigned int vk = 0, count = 0, nlen;
1580 if (!size)
1582 key_press( console, 0, VK_ESCAPE, 0 );
1583 return 0;
1586 switch(buf[0])
1588 case '[':
1589 if (++count == size) break;
1590 if ((nlen = process_csi_sequence( console, buf + 1, size - 1 ))) return count + nlen;
1591 break;
1593 case 'O':
1594 if (++count == size) break;
1595 vk = escape_char_to_vk( buf[1] );
1596 if (vk)
1598 key_press( console, 0, vk, 0 );
1599 return count + 1;
1603 char_key_press( console, buf[0], LEFT_ALT_PRESSED );
1604 return 1;
1607 static DWORD WINAPI tty_input( void *param )
1609 struct console *console = param;
1610 IO_STATUS_BLOCK io;
1611 HANDLE event;
1612 char read_buf[4096];
1613 WCHAR buf[4096];
1614 DWORD count, i;
1615 BOOL signaled;
1616 NTSTATUS status;
1618 if (console->is_unix)
1620 unsigned int h = condrv_handle( console->tty_input );
1621 status = NtDeviceIoControlFile( console->server, NULL, NULL, NULL, &io, IOCTL_CONDRV_SETUP_INPUT,
1622 &h, sizeof(h), NULL, 0 );
1623 if (status) ERR( "input setup failed: %#x\n", status );
1626 event = CreateEventW( NULL, TRUE, FALSE, NULL );
1628 for (;;)
1630 status = NtReadFile( console->tty_input, event, NULL, NULL, &io, read_buf, sizeof(read_buf), NULL, NULL );
1631 if (status == STATUS_PENDING)
1633 if ((status = NtWaitForSingleObject( event, FALSE, NULL ))) break;
1634 status = io.Status;
1636 if (status) break;
1638 EnterCriticalSection( &console_section );
1639 signaled = console->record_count != 0;
1641 /* FIXME: Handle partial char read */
1642 count = MultiByteToWideChar( get_tty_cp( console ), 0, read_buf, io.Information, buf, ARRAY_SIZE(buf) );
1644 TRACE( "%s\n", debugstr_wn(buf, count) );
1646 for (i = 0; i < count; i++)
1648 WCHAR ch = buf[i];
1649 switch (ch)
1651 case 3: /* end of text */
1652 LeaveCriticalSection( &console_section );
1653 goto done;
1654 case '\n':
1655 key_press( console, '\n', VK_RETURN, LEFT_CTRL_PRESSED );
1656 break;
1657 case '\b':
1658 key_press( console, ch, 'H', LEFT_CTRL_PRESSED );
1659 break;
1660 case 0x1b:
1661 i += process_input_escape( console, buf + i + 1, count - i - 1 );
1662 break;
1663 case 0x7f:
1664 key_press( console, '\b', VK_BACK, 0 );
1665 break;
1666 default:
1667 char_key_press( console, ch, 0 );
1671 process_console_input( console );
1672 if (!signaled && console->record_count)
1674 assert( !console->read_ioctl );
1675 read_complete( console, STATUS_SUCCESS, NULL, 0, TRUE ); /* signal console */
1677 LeaveCriticalSection( &console_section );
1680 TRACE( "NtReadFile failed: %#x\n", status );
1682 done:
1683 EnterCriticalSection( &console_section );
1684 if (console->read_ioctl) read_complete( console, status, NULL, 0, FALSE );
1685 if (console->is_unix)
1687 unsigned int h = 0;
1688 status = NtDeviceIoControlFile( console->server, NULL, NULL, NULL, &io, IOCTL_CONDRV_SETUP_INPUT,
1689 &h, sizeof(h), NULL, 0 );
1690 if (status) ERR( "input restore failed: %#x\n", status );
1692 CloseHandle( console->input_thread );
1693 console->input_thread = NULL;
1694 LeaveCriticalSection( &console_section );
1696 return 0;
1699 static BOOL ensure_tty_input_thread( struct console *console )
1701 if (!console->tty_input) return TRUE;
1702 if (!console->input_thread)
1703 console->input_thread = CreateThread( NULL, 0, tty_input, console, 0, NULL );
1704 return console->input_thread != NULL;
1707 static NTSTATUS screen_buffer_activate( struct screen_buffer *screen_buffer )
1709 RECT update_rect;
1710 TRACE( "%p\n", screen_buffer );
1711 screen_buffer->console->active = screen_buffer;
1712 SetRect( &update_rect, 0, 0, screen_buffer->width - 1, screen_buffer->height - 1 );
1713 update_output( screen_buffer, &update_rect );
1714 tty_sync( screen_buffer->console );
1715 update_window_config( screen_buffer->console, FALSE );
1716 return STATUS_SUCCESS;
1719 static NTSTATUS get_output_info( struct screen_buffer *screen_buffer, size_t *out_size )
1721 struct condrv_output_info *info;
1723 *out_size = min( *out_size, sizeof(*info) + screen_buffer->font.face_len );
1724 if (!(info = alloc_ioctl_buffer( *out_size ))) return STATUS_NO_MEMORY;
1726 info->cursor_size = screen_buffer->cursor_size;
1727 info->cursor_visible = screen_buffer->cursor_visible;
1728 info->cursor_x = get_bounded_cursor_x( screen_buffer );
1729 info->cursor_y = screen_buffer->cursor_y;
1730 info->width = screen_buffer->width;
1731 info->height = screen_buffer->height;
1732 info->attr = screen_buffer->attr;
1733 info->popup_attr = screen_buffer->popup_attr;
1734 info->win_left = screen_buffer->win.left;
1735 info->win_top = screen_buffer->win.top;
1736 info->win_right = screen_buffer->win.right;
1737 info->win_bottom = screen_buffer->win.bottom;
1738 info->max_width = screen_buffer->max_width;
1739 info->max_height = screen_buffer->max_height;
1740 info->font_width = screen_buffer->font.width;
1741 info->font_height = screen_buffer->font.height;
1742 info->font_weight = screen_buffer->font.weight;
1743 info->font_pitch_family = screen_buffer->font.pitch_family;
1744 memcpy( info->color_map, screen_buffer->color_map, sizeof(info->color_map) );
1745 if (*out_size > sizeof(*info)) memcpy( info + 1, screen_buffer->font.face_name, *out_size - sizeof(*info) );
1747 TRACE( "%p cursor_size=%u cursor_visible=%x cursor=(%u,%u) width=%u height=%u win=%s attr=%x popup_attr=%x"
1748 " font_width=%u font_height=%u %s\n", screen_buffer, info->cursor_size, info->cursor_visible,
1749 info->cursor_x, info->cursor_y, info->width, info->height, wine_dbgstr_rect(&screen_buffer->win),
1750 info->attr, info->popup_attr, info->font_width, info->font_height,
1751 debugstr_wn( (const WCHAR *)(info + 1), (*out_size - sizeof(*info)) / sizeof(WCHAR) ) );
1752 return STATUS_SUCCESS;
1755 void notify_screen_buffer_size( struct screen_buffer *screen_buffer )
1757 if (is_active( screen_buffer ) && screen_buffer->console->mode & ENABLE_WINDOW_INPUT)
1759 INPUT_RECORD ir;
1760 ir.EventType = WINDOW_BUFFER_SIZE_EVENT;
1761 ir.Event.WindowBufferSizeEvent.dwSize.X = screen_buffer->width;
1762 ir.Event.WindowBufferSizeEvent.dwSize.Y = screen_buffer->height;
1763 write_console_input( screen_buffer->console, &ir, 1, TRUE );
1767 NTSTATUS change_screen_buffer_size( struct screen_buffer *screen_buffer, int new_width, int new_height )
1769 int i, old_width, old_height, copy_width, copy_height;
1770 char_info_t *new_data;
1772 if (!(new_data = malloc( new_width * new_height * sizeof(*new_data) ))) return STATUS_NO_MEMORY;
1774 old_width = screen_buffer->width;
1775 old_height = screen_buffer->height;
1776 copy_width = min( old_width, new_width );
1777 copy_height = min( old_height, new_height );
1779 /* copy all the rows */
1780 for (i = 0; i < copy_height; i++)
1782 memcpy( &new_data[i * new_width], &screen_buffer->data[i * old_width],
1783 copy_width * sizeof(char_info_t) );
1786 /* clear the end of each row */
1787 if (new_width > old_width)
1789 /* fill first row */
1790 for (i = old_width; i < new_width; i++) new_data[i] = empty_char_info;
1791 /* and blast it to the other rows */
1792 for (i = 1; i < copy_height; i++)
1793 memcpy( &new_data[i * new_width + old_width], &new_data[old_width],
1794 (new_width - old_width) * sizeof(char_info_t) );
1797 /* clear remaining rows */
1798 if (new_height > old_height)
1800 /* fill first row */
1801 for (i = 0; i < new_width; i++) new_data[old_height * new_width + i] = empty_char_info;
1802 /* and blast it to the other rows */
1803 for (i = old_height+1; i < new_height; i++)
1804 memcpy( &new_data[i * new_width], &new_data[old_height * new_width],
1805 new_width * sizeof(char_info_t) );
1807 free( screen_buffer->data );
1808 screen_buffer->data = new_data;
1809 screen_buffer->width = new_width;
1810 screen_buffer->height = new_height;
1811 return STATUS_SUCCESS;
1814 static NTSTATUS set_output_info( struct screen_buffer *screen_buffer,
1815 const struct condrv_output_info_params *params, size_t extra_size )
1817 const struct condrv_output_info *info = &params->info;
1818 NTSTATUS status;
1820 TRACE( "%p\n", screen_buffer );
1822 extra_size -= sizeof(*params);
1824 if (params->mask & SET_CONSOLE_OUTPUT_INFO_CURSOR_GEOM)
1826 if (info->cursor_size < 1 || info->cursor_size > 100) return STATUS_INVALID_PARAMETER;
1828 screen_buffer->cursor_size = info->cursor_size;
1829 screen_buffer->cursor_visible = !!info->cursor_visible;
1831 if (params->mask & SET_CONSOLE_OUTPUT_INFO_CURSOR_POS)
1833 if (info->cursor_x < 0 || info->cursor_x >= screen_buffer->width ||
1834 info->cursor_y < 0 || info->cursor_y >= screen_buffer->height)
1836 return STATUS_INVALID_PARAMETER;
1839 if (screen_buffer->cursor_x != info->cursor_x || screen_buffer->cursor_y != info->cursor_y)
1841 screen_buffer->cursor_x = info->cursor_x;
1842 screen_buffer->cursor_y = info->cursor_y;
1843 scroll_to_cursor( screen_buffer );
1846 if (params->mask & SET_CONSOLE_OUTPUT_INFO_SIZE)
1848 /* new screen-buffer cannot be smaller than actual window */
1849 if (info->width < screen_buffer->win.right - screen_buffer->win.left + 1 ||
1850 info->height < screen_buffer->win.bottom - screen_buffer->win.top + 1)
1852 return STATUS_INVALID_PARAMETER;
1854 /* FIXME: there are also some basic minimum and max size to deal with */
1855 if ((status = change_screen_buffer_size( screen_buffer, info->width, info->height ))) return status;
1857 /* scroll window to display sb */
1858 if (screen_buffer->win.right >= info->width)
1860 screen_buffer->win.right -= screen_buffer->win.left;
1861 screen_buffer->win.left = 0;
1863 if (screen_buffer->win.bottom >= info->height)
1865 screen_buffer->win.bottom -= screen_buffer->win.top;
1866 screen_buffer->win.top = 0;
1868 if (screen_buffer->cursor_x >= info->width) screen_buffer->cursor_x = info->width - 1;
1869 if (screen_buffer->cursor_y >= info->height) screen_buffer->cursor_y = info->height - 1;
1871 notify_screen_buffer_size( screen_buffer );
1873 if (params->mask & SET_CONSOLE_OUTPUT_INFO_ATTR)
1875 screen_buffer->attr = info->attr;
1877 if (params->mask & SET_CONSOLE_OUTPUT_INFO_POPUP_ATTR)
1879 screen_buffer->popup_attr = info->popup_attr;
1881 if (params->mask & SET_CONSOLE_OUTPUT_INFO_DISPLAY_WINDOW)
1883 if (info->win_left < 0 || info->win_left > info->win_right ||
1884 info->win_right >= screen_buffer->width ||
1885 info->win_top < 0 || info->win_top > info->win_bottom ||
1886 info->win_bottom >= screen_buffer->height)
1888 return STATUS_INVALID_PARAMETER;
1890 if (screen_buffer->win.left != info->win_left || screen_buffer->win.top != info->win_top ||
1891 screen_buffer->win.right != info->win_right || screen_buffer->win.bottom != info->win_bottom)
1893 screen_buffer->win.left = info->win_left;
1894 screen_buffer->win.top = info->win_top;
1895 screen_buffer->win.right = info->win_right;
1896 screen_buffer->win.bottom = info->win_bottom;
1899 if (params->mask & SET_CONSOLE_OUTPUT_INFO_MAX_SIZE)
1901 screen_buffer->max_width = info->max_width;
1902 screen_buffer->max_height = info->max_height;
1905 if (is_active( screen_buffer ))
1907 tty_sync( screen_buffer->console );
1908 update_window_config( screen_buffer->console, FALSE );
1910 return STATUS_SUCCESS;
1913 static NTSTATUS write_console( struct screen_buffer *screen_buffer, const WCHAR *buffer, size_t len )
1915 RECT update_rect;
1916 size_t i, j;
1918 TRACE( "%s\n", debugstr_wn(buffer, len) );
1920 empty_update_rect( screen_buffer, &update_rect );
1922 for (i = 0; i < len; i++)
1924 if (screen_buffer->mode & ENABLE_PROCESSED_OUTPUT)
1926 switch (buffer[i])
1928 case '\b':
1929 screen_buffer->cursor_x = get_bounded_cursor_x( screen_buffer );
1930 if (screen_buffer->cursor_x) screen_buffer->cursor_x--;
1931 continue;
1932 case '\t':
1933 j = min( screen_buffer->width - screen_buffer->cursor_x, 8 - (screen_buffer->cursor_x % 8) );
1934 if (!j) j = 8;
1935 while (j--) write_char( screen_buffer, ' ', &update_rect, NULL );
1936 continue;
1937 case '\n':
1938 screen_buffer->cursor_x = 0;
1939 if (++screen_buffer->cursor_y == screen_buffer->height)
1940 new_line( screen_buffer, &update_rect );
1941 else if (screen_buffer->mode & ENABLE_WRAP_AT_EOL_OUTPUT)
1943 update_output( screen_buffer, &update_rect );
1944 set_tty_cursor( screen_buffer->console, screen_buffer->cursor_x, screen_buffer->cursor_y );
1946 continue;
1947 case '\a':
1948 FIXME( "beep\n" );
1949 continue;
1950 case '\r':
1951 screen_buffer->cursor_x = 0;
1952 continue;
1955 if (screen_buffer->cursor_x == screen_buffer->width && !(screen_buffer->mode & ENABLE_WRAP_AT_EOL_OUTPUT))
1956 screen_buffer->cursor_x = update_rect.left;
1957 write_char( screen_buffer, buffer[i], &update_rect, NULL );
1960 if (screen_buffer->cursor_x == screen_buffer->width)
1962 if (screen_buffer->mode & ENABLE_WRAP_AT_EOL_OUTPUT)
1964 if (!(screen_buffer->mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING))
1966 screen_buffer->cursor_x = 0;
1967 if (++screen_buffer->cursor_y == screen_buffer->height)
1968 new_line( screen_buffer, &update_rect );
1971 else screen_buffer->cursor_x = update_rect.left;
1974 scroll_to_cursor( screen_buffer );
1975 update_output( screen_buffer, &update_rect );
1976 tty_sync( screen_buffer->console );
1977 update_window_config( screen_buffer->console, TRUE );
1978 return STATUS_SUCCESS;
1981 static NTSTATUS write_output( struct screen_buffer *screen_buffer, const struct condrv_output_params *params,
1982 size_t in_size, size_t *out_size )
1984 unsigned int i, entry_size, entry_cnt, x, y;
1985 char_info_t *dest;
1986 char *src;
1988 if (*out_size == sizeof(SMALL_RECT) && !params->width) return STATUS_INVALID_PARAMETER;
1990 entry_size = params->mode == CHAR_INFO_MODE_TEXTATTR ? sizeof(char_info_t) : sizeof(WCHAR);
1991 entry_cnt = (in_size - sizeof(*params)) / entry_size;
1993 TRACE( "(%u,%u) cnt %u\n", params->x, params->y, entry_cnt );
1995 if (params->x >= screen_buffer->width)
1997 *out_size = 0;
1998 return STATUS_SUCCESS;
2001 for (i = 0, src = (char *)(params + 1); i < entry_cnt; i++, src += entry_size)
2003 if (params->width)
2005 x = params->x + i % params->width;
2006 y = params->y + i / params->width;
2007 if (x >= screen_buffer->width) continue;
2009 else
2011 x = (params->x + i) % screen_buffer->width;
2012 y = params->y + (params->x + i) / screen_buffer->width;
2014 if (y >= screen_buffer->height) break;
2016 dest = &screen_buffer->data[y * screen_buffer->width + x];
2017 switch(params->mode)
2019 case CHAR_INFO_MODE_TEXT:
2020 dest->ch = *(const WCHAR *)src;
2021 break;
2022 case CHAR_INFO_MODE_ATTR:
2023 dest->attr = *(const unsigned short *)src;
2024 break;
2025 case CHAR_INFO_MODE_TEXTATTR:
2026 *dest = *(const char_info_t *)src;
2027 break;
2028 default:
2029 return STATUS_INVALID_PARAMETER;
2033 if (i && is_active( screen_buffer ))
2035 RECT update_rect;
2037 update_rect.left = params->x;
2038 update_rect.top = params->y;
2039 if (params->width)
2041 update_rect.bottom = min( params->y + entry_cnt / params->width, screen_buffer->height ) - 1;
2042 update_rect.right = min( params->x + params->width, screen_buffer->width ) - 1;
2044 else
2046 update_rect.bottom = params->y + (params->x + i - 1) / screen_buffer->width;
2047 if (update_rect.bottom != params->y)
2049 update_rect.left = 0;
2050 update_rect.right = screen_buffer->width - 1;
2052 else
2054 update_rect.right = params->x + i - 1;
2057 update_output( screen_buffer, &update_rect );
2058 tty_sync( screen_buffer->console );
2061 if (*out_size == sizeof(SMALL_RECT))
2063 SMALL_RECT *region;
2064 unsigned int width = params->width;
2065 x = params->x;
2066 y = params->y;
2067 if (!(region = alloc_ioctl_buffer( sizeof(*region )))) return STATUS_NO_MEMORY;
2068 region->Left = x;
2069 region->Top = y;
2070 region->Right = min( x + width, screen_buffer->width ) - 1;
2071 region->Bottom = min( y + entry_cnt / width, screen_buffer->height ) - 1;
2073 else
2075 DWORD *result;
2076 if (!(result = alloc_ioctl_buffer( sizeof(*result )))) return STATUS_NO_MEMORY;
2077 *result = i;
2080 return STATUS_SUCCESS;
2083 static NTSTATUS read_output( struct screen_buffer *screen_buffer, const struct condrv_output_params *params,
2084 size_t *out_size )
2086 enum char_info_mode mode;
2087 unsigned int x, y, width;
2088 unsigned int i, count;
2090 x = params->x;
2091 y = params->y;
2092 mode = params->mode;
2093 width = params->width;
2094 TRACE( "(%u %u) mode %u width %u\n", x, y, mode, width );
2096 switch(mode)
2098 case CHAR_INFO_MODE_TEXT:
2100 WCHAR *data;
2101 char_info_t *src;
2102 if (x >= screen_buffer->width || y >= screen_buffer->height)
2104 *out_size = 0;
2105 return STATUS_SUCCESS;
2107 src = screen_buffer->data + y * screen_buffer->width + x;
2108 count = min( screen_buffer->data + screen_buffer->height * screen_buffer->width - src,
2109 *out_size / sizeof(*data) );
2110 *out_size = count * sizeof(*data);
2111 if (!(data = alloc_ioctl_buffer( *out_size ))) return STATUS_NO_MEMORY;
2112 for (i = 0; i < count; i++) data[i] = src[i].ch;
2114 break;
2115 case CHAR_INFO_MODE_ATTR:
2117 unsigned short *data;
2118 char_info_t *src;
2119 if (x >= screen_buffer->width || y >= screen_buffer->height)
2121 *out_size = 0;
2122 return STATUS_SUCCESS;
2124 src = screen_buffer->data + y * screen_buffer->width + x;
2125 count = min( screen_buffer->data + screen_buffer->height * screen_buffer->width - src,
2126 *out_size / sizeof(*data) );
2127 *out_size = count * sizeof(*data);
2128 if (!(data = alloc_ioctl_buffer( *out_size ))) return STATUS_NO_MEMORY;
2129 for (i = 0; i < count; i++) data[i] = src[i].attr;
2131 break;
2132 case CHAR_INFO_MODE_TEXTATTR:
2134 SMALL_RECT *region;
2135 char_info_t *data;
2136 if (!width || *out_size < sizeof(*region) || x >= screen_buffer->width || y >= screen_buffer->height)
2137 return STATUS_INVALID_PARAMETER;
2138 count = min( (*out_size - sizeof(*region)) / (width * sizeof(*data)), screen_buffer->height - y );
2139 width = min( width, screen_buffer->width - x );
2140 *out_size = sizeof(*region) + width * count * sizeof(*data);
2141 if (!(region = alloc_ioctl_buffer( *out_size ))) return STATUS_NO_MEMORY;
2142 region->Left = x;
2143 region->Top = y;
2144 region->Right = x + width - 1;
2145 region->Bottom = y + count - 1;
2146 data = (char_info_t *)(region + 1);
2147 for (i = 0; i < count; i++)
2149 memcpy( &data[i * width], &screen_buffer->data[(y + i) * screen_buffer->width + x],
2150 width * sizeof(*data) );
2153 break;
2154 default:
2155 return STATUS_INVALID_PARAMETER;
2158 return STATUS_SUCCESS;
2161 static NTSTATUS fill_output( struct screen_buffer *screen_buffer, const struct condrv_fill_output_params *params )
2163 char_info_t *end, *dest;
2164 DWORD i, count, *result;
2166 TRACE( "(%u %u) mode %u\n", params->x, params->y, params->mode );
2168 if (params->y >= screen_buffer->height) return STATUS_SUCCESS;
2169 dest = screen_buffer->data + min( params->y * screen_buffer->width + params->x,
2170 screen_buffer->height * screen_buffer->width );
2172 end = screen_buffer->data + screen_buffer->height * screen_buffer->width;
2174 count = params->count;
2175 if (count > end - dest) count = end - dest;
2177 switch(params->mode)
2179 case CHAR_INFO_MODE_TEXT:
2180 for (i = 0; i < count; i++) dest[i].ch = params->ch;
2181 break;
2182 case CHAR_INFO_MODE_ATTR:
2183 for (i = 0; i < count; i++) dest[i].attr = params->attr;
2184 break;
2185 case CHAR_INFO_MODE_TEXTATTR:
2186 for (i = 0; i < count; i++)
2188 dest[i].ch = params->ch;
2189 dest[i].attr = params->attr;
2191 break;
2192 default:
2193 return STATUS_INVALID_PARAMETER;
2196 if (count && is_active(screen_buffer))
2198 RECT update_rect;
2199 SetRect( &update_rect,
2200 params->x % screen_buffer->width,
2201 params->y + params->x / screen_buffer->width,
2202 (params->x + i - 1) % screen_buffer->width,
2203 params->y + (params->x + i - 1) / screen_buffer->width );
2204 update_output( screen_buffer, &update_rect );
2205 tty_sync( screen_buffer->console );
2208 if (!(result = alloc_ioctl_buffer( sizeof(*result) ))) return STATUS_NO_MEMORY;
2209 *result = count;
2210 return STATUS_SUCCESS;
2213 static NTSTATUS scroll_output( struct screen_buffer *screen_buffer, const struct condrv_scroll_params *params )
2215 int x, y, xsrc, ysrc, w, h;
2216 char_info_t *psrc, *pdst;
2217 SMALL_RECT src, dst;
2218 RECT update_rect;
2219 SMALL_RECT clip;
2221 xsrc = params->scroll.Left;
2222 ysrc = params->scroll.Top;
2223 w = params->scroll.Right - params->scroll.Left + 1;
2224 h = params->scroll.Bottom - params->scroll.Top + 1;
2226 TRACE( "(%d %d) -> (%u %u) w %u h %u\n", xsrc, ysrc, params->origin.X, params->origin.Y, w, h );
2228 clip.Left = max( params->clip.Left, 0 );
2229 clip.Top = max( params->clip.Top, 0 );
2230 clip.Right = min( params->clip.Right, screen_buffer->width - 1 );
2231 clip.Bottom = min( params->clip.Bottom, screen_buffer->height - 1 );
2232 if (clip.Left > clip.Right || clip.Top > clip.Bottom || params->scroll.Left < 0 || params->scroll.Top < 0 ||
2233 params->scroll.Right >= screen_buffer->width || params->scroll.Bottom >= screen_buffer->height ||
2234 params->scroll.Right < params->scroll.Left || params->scroll.Top > params->scroll.Bottom ||
2235 params->origin.X < 0 || params->origin.X >= screen_buffer->width || params->origin.Y < 0 ||
2236 params->origin.Y >= screen_buffer->height)
2237 return STATUS_INVALID_PARAMETER;
2239 src.Left = max( xsrc, clip.Left );
2240 src.Top = max( ysrc, clip.Top );
2241 src.Right = min( xsrc + w - 1, clip.Right );
2242 src.Bottom = min( ysrc + h - 1, clip.Bottom );
2244 dst.Left = params->origin.X;
2245 dst.Top = params->origin.Y;
2246 dst.Right = params->origin.X + w - 1;
2247 dst.Bottom = params->origin.Y + h - 1;
2249 if (dst.Left < clip.Left)
2251 xsrc += clip.Left - dst.Left;
2252 w -= clip.Left - dst.Left;
2253 dst.Left = clip.Left;
2255 if (dst.Top < clip.Top)
2257 ysrc += clip.Top - dst.Top;
2258 h -= clip.Top - dst.Top;
2259 dst.Top = clip.Top;
2261 if (dst.Right > clip.Right) w -= dst.Right - clip.Right;
2262 if (dst.Bottom > clip.Bottom) h -= dst.Bottom - clip.Bottom;
2264 if (w > 0 && h > 0)
2266 if (ysrc < dst.Top)
2268 psrc = &screen_buffer->data[(ysrc + h - 1) * screen_buffer->width + xsrc];
2269 pdst = &screen_buffer->data[(dst.Top + h - 1) * screen_buffer->width + dst.Left];
2271 for (y = h; y > 0; y--)
2273 memcpy( pdst, psrc, w * sizeof(*pdst) );
2274 pdst -= screen_buffer->width;
2275 psrc -= screen_buffer->width;
2278 else
2280 psrc = &screen_buffer->data[ysrc * screen_buffer->width + xsrc];
2281 pdst = &screen_buffer->data[dst.Top * screen_buffer->width + dst.Left];
2283 for (y = 0; y < h; y++)
2285 /* we use memmove here because when psrc and pdst are the same,
2286 * copies are done on the same row, so the dst and src blocks
2287 * can overlap */
2288 memmove( pdst, psrc, w * sizeof(*pdst) );
2289 pdst += screen_buffer->width;
2290 psrc += screen_buffer->width;
2295 for (y = src.Top; y <= src.Bottom; y++)
2297 int left = src.Left;
2298 int right = src.Right;
2299 if (dst.Top <= y && y <= dst.Bottom)
2301 if (dst.Left <= src.Left) left = max( left, dst.Right + 1 );
2302 if (dst.Left >= src.Left) right = min( right, dst.Left - 1 );
2304 for (x = left; x <= right; x++) screen_buffer->data[y * screen_buffer->width + x] = params->fill;
2307 SetRect( &update_rect, min( src.Left, dst.Left ), min( src.Top, dst.Top ),
2308 max( src.Right, dst.Right ), max( src.Bottom, dst.Bottom ));
2309 update_output( screen_buffer, &update_rect );
2310 tty_sync( screen_buffer->console );
2311 return STATUS_SUCCESS;
2314 static NTSTATUS set_console_title( struct console *console, const WCHAR *in_title, size_t size )
2316 WCHAR *title = NULL;
2318 TRACE( "%s\n", debugstr_wn(in_title, size / sizeof(WCHAR)) );
2320 if (size)
2322 if (!(title = malloc( size + sizeof(WCHAR) ))) return STATUS_NO_MEMORY;
2323 memcpy( title, in_title, size );
2324 title[size / sizeof(WCHAR)] = 0;
2326 free( console->title );
2327 console->title = title;
2329 if (console->tty_output)
2331 size_t len;
2332 char *vt;
2334 tty_write( console, "\x1b]0;", 4 );
2335 len = WideCharToMultiByte( get_tty_cp( console ), 0, console->title, size / sizeof(WCHAR),
2336 NULL, 0, NULL, NULL);
2337 if ((vt = tty_alloc_buffer( console, len )))
2338 WideCharToMultiByte( get_tty_cp( console ), 0, console->title, size / sizeof(WCHAR),
2339 vt, len, NULL, NULL );
2340 tty_write( console, "\x07", 1 );
2341 tty_sync( console );
2343 if (console->win)
2344 SetWindowTextW( console->win, console->title );
2345 return STATUS_SUCCESS;
2348 static NTSTATUS screen_buffer_ioctl( struct screen_buffer *screen_buffer, unsigned int code,
2349 const void *in_data, size_t in_size, size_t *out_size )
2351 switch (code)
2353 case IOCTL_CONDRV_CLOSE_OUTPUT:
2354 if (in_size || *out_size) return STATUS_INVALID_PARAMETER;
2355 destroy_screen_buffer( screen_buffer );
2356 return STATUS_SUCCESS;
2358 case IOCTL_CONDRV_ACTIVATE:
2359 if (in_size || *out_size) return STATUS_INVALID_PARAMETER;
2360 return screen_buffer_activate( screen_buffer );
2362 case IOCTL_CONDRV_GET_MODE:
2364 DWORD *mode;
2365 TRACE( "returning mode %x\n", screen_buffer->mode );
2366 if (in_size || *out_size != sizeof(*mode)) return STATUS_INVALID_PARAMETER;
2367 if (!(mode = alloc_ioctl_buffer( *out_size ))) return STATUS_NO_MEMORY;
2368 *mode = screen_buffer->mode;
2369 return STATUS_SUCCESS;
2372 case IOCTL_CONDRV_SET_MODE:
2373 if (in_size != sizeof(unsigned int) || *out_size) return STATUS_INVALID_PARAMETER;
2374 screen_buffer->mode = *(unsigned int *)in_data;
2375 TRACE( "set %x mode\n", screen_buffer->mode );
2376 return STATUS_SUCCESS;
2378 case IOCTL_CONDRV_WRITE_CONSOLE:
2379 if (in_size % sizeof(WCHAR) || *out_size) return STATUS_INVALID_PARAMETER;
2380 return write_console( screen_buffer, in_data, in_size / sizeof(WCHAR) );
2382 case IOCTL_CONDRV_WRITE_FILE:
2384 unsigned int len;
2385 WCHAR *buf;
2386 NTSTATUS status;
2388 len = MultiByteToWideChar( screen_buffer->console->output_cp, 0, in_data, in_size,
2389 NULL, 0 );
2390 if (!len) return STATUS_SUCCESS;
2391 if (!(buf = malloc( len * sizeof(WCHAR) ))) return STATUS_NO_MEMORY;
2392 MultiByteToWideChar( screen_buffer->console->output_cp, 0, in_data, in_size, buf, len );
2393 status = write_console( screen_buffer, buf, len );
2394 free( buf );
2395 return status;
2398 case IOCTL_CONDRV_WRITE_OUTPUT:
2399 if ((*out_size != sizeof(DWORD) && *out_size != sizeof(SMALL_RECT)) ||
2400 in_size < sizeof(struct condrv_output_params))
2401 return STATUS_INVALID_PARAMETER;
2402 return write_output( screen_buffer, in_data, in_size, out_size );
2404 case IOCTL_CONDRV_READ_OUTPUT:
2405 if (in_size != sizeof(struct condrv_output_params)) return STATUS_INVALID_PARAMETER;
2406 return read_output( screen_buffer, in_data, out_size );
2408 case IOCTL_CONDRV_GET_OUTPUT_INFO:
2409 if (in_size || *out_size < sizeof(struct condrv_output_info)) return STATUS_INVALID_PARAMETER;
2410 return get_output_info( screen_buffer, out_size );
2412 case IOCTL_CONDRV_SET_OUTPUT_INFO:
2413 if (in_size < sizeof(struct condrv_output_info) || *out_size) return STATUS_INVALID_PARAMETER;
2414 return set_output_info( screen_buffer, in_data, in_size );
2416 case IOCTL_CONDRV_FILL_OUTPUT:
2417 if (in_size != sizeof(struct condrv_fill_output_params) || *out_size != sizeof(DWORD))
2418 return STATUS_INVALID_PARAMETER;
2419 return fill_output( screen_buffer, in_data );
2421 case IOCTL_CONDRV_SCROLL:
2422 if (in_size != sizeof(struct condrv_scroll_params) || *out_size)
2423 return STATUS_INVALID_PARAMETER;
2424 return scroll_output( screen_buffer, in_data );
2426 default:
2427 WARN( "invalid ioctl %x\n", code );
2428 return STATUS_INVALID_HANDLE;
2432 static NTSTATUS console_input_ioctl( struct console *console, unsigned int code, const void *in_data,
2433 size_t in_size, size_t *out_size )
2435 NTSTATUS status;
2437 switch (code)
2439 case IOCTL_CONDRV_GET_MODE:
2441 DWORD *mode;
2442 TRACE( "returning mode %x\n", console->mode );
2443 if (in_size || *out_size != sizeof(*mode)) return STATUS_INVALID_PARAMETER;
2444 if (!(mode = alloc_ioctl_buffer( *out_size ))) return STATUS_NO_MEMORY;
2445 *mode = console->mode;
2446 return STATUS_SUCCESS;
2449 case IOCTL_CONDRV_SET_MODE:
2450 if (in_size != sizeof(unsigned int) || *out_size) return STATUS_INVALID_PARAMETER;
2451 console->mode = *(unsigned int *)in_data;
2452 TRACE( "set %x mode\n", console->mode );
2453 return STATUS_SUCCESS;
2455 case IOCTL_CONDRV_READ_CONSOLE:
2456 if (in_size || *out_size % sizeof(WCHAR)) return STATUS_INVALID_PARAMETER;
2457 ensure_tty_input_thread( console );
2458 status = read_console( console, code, *out_size );
2459 *out_size = 0;
2460 return status;
2462 case IOCTL_CONDRV_READ_FILE:
2463 ensure_tty_input_thread( console );
2464 status = read_console( console, code, *out_size );
2465 *out_size = 0;
2466 return status;
2468 case IOCTL_CONDRV_READ_INPUT:
2470 if (in_size) return STATUS_INVALID_PARAMETER;
2471 ensure_tty_input_thread( console );
2472 if (!console->record_count && *out_size)
2474 TRACE( "pending read\n" );
2475 console->read_ioctl = IOCTL_CONDRV_READ_INPUT;
2476 console->pending_read = *out_size;
2477 return STATUS_PENDING;
2479 status = read_console_input( console, *out_size );
2480 *out_size = 0;
2481 return status;
2484 case IOCTL_CONDRV_WRITE_INPUT:
2485 if (in_size % sizeof(INPUT_RECORD) || *out_size) return STATUS_INVALID_PARAMETER;
2486 return write_console_input( console, in_data, in_size / sizeof(INPUT_RECORD), TRUE );
2488 case IOCTL_CONDRV_PEEK:
2490 void *result;
2491 TRACE( "peek\n" );
2492 if (in_size) return STATUS_INVALID_PARAMETER;
2493 ensure_tty_input_thread( console );
2494 *out_size = min( *out_size, console->record_count * sizeof(INPUT_RECORD) );
2495 if (!(result = alloc_ioctl_buffer( *out_size ))) return STATUS_NO_MEMORY;
2496 if (*out_size) memcpy( result, console->records, *out_size );
2497 return STATUS_SUCCESS;
2500 case IOCTL_CONDRV_GET_INPUT_INFO:
2502 struct condrv_input_info *info;
2503 TRACE( "get info\n" );
2504 if (in_size || *out_size != sizeof(*info)) return STATUS_INVALID_PARAMETER;
2505 if (!(info = alloc_ioctl_buffer( sizeof(*info )))) return STATUS_NO_MEMORY;
2506 info->input_cp = console->input_cp;
2507 info->output_cp = console->output_cp;
2508 info->win = condrv_handle( console->win );
2509 info->input_count = console->record_count;
2510 return STATUS_SUCCESS;
2513 case IOCTL_CONDRV_SET_INPUT_INFO:
2515 const struct condrv_input_info_params *params = in_data;
2516 TRACE( "set info\n" );
2517 if (in_size != sizeof(*params) || *out_size) return STATUS_INVALID_PARAMETER;
2518 if (params->mask & SET_CONSOLE_INPUT_INFO_INPUT_CODEPAGE)
2520 if (!IsValidCodePage( params->info.input_cp )) return STATUS_INVALID_PARAMETER;
2521 console->input_cp = params->info.input_cp;
2523 if (params->mask & SET_CONSOLE_INPUT_INFO_OUTPUT_CODEPAGE)
2525 if (!IsValidCodePage( params->info.output_cp )) return STATUS_INVALID_PARAMETER;
2526 console->output_cp = params->info.output_cp;
2528 return STATUS_SUCCESS;
2531 case IOCTL_CONDRV_GET_TITLE:
2533 WCHAR *result;
2534 if (in_size) return STATUS_INVALID_PARAMETER;
2535 TRACE( "returning title %s\n", debugstr_w(console->title) );
2536 *out_size = min( *out_size, console->title ? wcslen( console->title ) * sizeof(WCHAR) : 0 );
2537 if (!(result = alloc_ioctl_buffer( *out_size ))) return STATUS_NO_MEMORY;
2538 if (*out_size) memcpy( result, console->title, *out_size );
2539 return STATUS_SUCCESS;
2542 case IOCTL_CONDRV_SET_TITLE:
2543 if (in_size % sizeof(WCHAR) || *out_size) return STATUS_INVALID_PARAMETER;
2544 return set_console_title( console, in_data, in_size );
2546 case IOCTL_CONDRV_BEEP:
2547 if (in_size || *out_size) return STATUS_INVALID_PARAMETER;
2548 if (console->is_unix)
2550 tty_write( console, "\a", 1 );
2551 tty_sync( console );
2553 return STATUS_SUCCESS;
2555 case IOCTL_CONDRV_FLUSH:
2556 if (in_size || *out_size) return STATUS_INVALID_PARAMETER;
2557 TRACE( "flush\n" );
2558 console->record_count = 0;
2559 return STATUS_SUCCESS;
2561 default:
2562 FIXME( "unsupported ioctl %x\n", code );
2563 return STATUS_NOT_SUPPORTED;
2567 static NTSTATUS process_console_ioctls( struct console *console )
2569 size_t out_size = 0, in_size;
2570 unsigned int code;
2571 int output;
2572 NTSTATUS status = STATUS_SUCCESS;
2574 for (;;)
2576 if (status) out_size = 0;
2578 console->signaled = console->record_count != 0;
2579 SERVER_START_REQ( get_next_console_request )
2581 req->handle = wine_server_obj_handle( console->server );
2582 req->status = status;
2583 req->signal = console->signaled;
2584 wine_server_add_data( req, ioctl_buffer, out_size );
2585 wine_server_set_reply( req, ioctl_buffer, ioctl_buffer_size );
2586 status = wine_server_call( req );
2587 code = reply->code;
2588 output = reply->output;
2589 out_size = reply->out_size;
2590 in_size = wine_server_reply_size( reply );
2592 SERVER_END_REQ;
2594 if (status == STATUS_PENDING) return STATUS_SUCCESS;
2595 if (status == STATUS_BUFFER_OVERFLOW)
2597 if (!alloc_ioctl_buffer( out_size )) return STATUS_NO_MEMORY;
2598 status = STATUS_SUCCESS;
2599 continue;
2601 if (status)
2603 TRACE( "failed to get next request: %#x\n", status );
2604 return status;
2607 if (code == IOCTL_CONDRV_INIT_OUTPUT)
2609 TRACE( "initializing output %x\n", output );
2610 if (console->active)
2611 create_screen_buffer( console, output, console->active->width, console->active->height );
2612 else
2613 create_screen_buffer( console, output, 80, 150 );
2615 else if (!output)
2617 status = console_input_ioctl( console, code, ioctl_buffer, in_size, &out_size );
2619 else
2621 struct wine_rb_entry *entry;
2622 if (!(entry = wine_rb_get( &screen_buffer_map, LongToPtr(output) )))
2624 ERR( "invalid screen buffer id %x\n", output );
2625 status = STATUS_INVALID_HANDLE;
2627 else
2629 status = screen_buffer_ioctl( WINE_RB_ENTRY_VALUE( entry, struct screen_buffer, entry ), code,
2630 ioctl_buffer, in_size, &out_size );
2636 static int main_loop( struct console *console, HANDLE signal )
2638 HANDLE signal_event = NULL;
2639 HANDLE wait_handles[3];
2640 unsigned int wait_cnt = 0;
2641 unsigned short signal_id;
2642 IO_STATUS_BLOCK signal_io;
2643 NTSTATUS status;
2644 BOOL pump_msgs;
2645 DWORD res;
2647 if (signal)
2649 if (!(signal_event = CreateEventW( NULL, TRUE, FALSE, NULL ))) return 1;
2650 status = NtReadFile( signal, signal_event, NULL, NULL, &signal_io, &signal_id,
2651 sizeof(signal_id), NULL, NULL );
2652 if (status && status != STATUS_PENDING) return 1;
2655 if (!alloc_ioctl_buffer( 4096 )) return 1;
2657 wait_handles[wait_cnt++] = console->server;
2658 if (signal) wait_handles[wait_cnt++] = signal_event;
2659 if (console->input_thread) wait_handles[wait_cnt++] = console->input_thread;
2660 pump_msgs = console->win != NULL;
2662 for (;;)
2664 if (pump_msgs)
2665 res = MsgWaitForMultipleObjects( wait_cnt, wait_handles, FALSE, INFINITE, QS_ALLINPUT );
2666 else
2667 res = WaitForMultipleObjects( wait_cnt, wait_handles, FALSE, INFINITE );
2669 if (res == WAIT_OBJECT_0 + wait_cnt)
2671 MSG msg;
2672 while (PeekMessageW( &msg, 0, 0, 0, PM_REMOVE ))
2674 if (msg.message == WM_QUIT) return 0;
2675 DispatchMessageW(&msg);
2677 continue;
2680 switch (res)
2682 case WAIT_OBJECT_0:
2683 EnterCriticalSection( &console_section );
2684 status = process_console_ioctls( console );
2685 LeaveCriticalSection( &console_section );
2686 if (status) return 0;
2687 break;
2689 case WAIT_OBJECT_0 + 1:
2690 if (signal_io.Status || signal_io.Information != sizeof(signal_id))
2692 TRACE( "signaled quit\n" );
2693 return 0;
2695 FIXME( "unimplemented signal %x\n", signal_id );
2696 status = NtReadFile( signal, signal_event, NULL, NULL, &signal_io, &signal_id,
2697 sizeof(signal_id), NULL, NULL );
2698 if (status && status != STATUS_PENDING) return 1;
2699 break;
2701 default:
2702 TRACE( "wait failed, quit\n");
2703 return 0;
2707 return 0;
2710 static LONG WINAPI handle_ctrl_c( EXCEPTION_POINTERS *eptr )
2712 if (eptr->ExceptionRecord->ExceptionCode != CONTROL_C_EXIT) return EXCEPTION_CONTINUE_SEARCH;
2713 /* In Unix mode, ignore ctrl c exceptions. Signals are sent it to clients as well and we will
2714 * terminate the usual way if they don't handle it. */
2715 return EXCEPTION_CONTINUE_EXECUTION;
2718 int __cdecl wmain(int argc, WCHAR *argv[])
2720 int headless = 0, i, width = 0, height = 0;
2721 HANDLE signal = NULL;
2722 WCHAR *end;
2724 static struct console console;
2726 for (i = 0; i < argc; i++) TRACE("%s ", wine_dbgstr_w(argv[i]));
2727 TRACE("\n");
2729 console.mode = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT |
2730 ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT | ENABLE_INSERT_MODE |
2731 ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS | ENABLE_AUTO_POSITION;
2732 console.input_cp = console.output_cp = GetOEMCP();
2733 console.history_size = 50;
2734 if (!(console.history = calloc( console.history_size, sizeof(*console.history) ))) return 1;
2736 for (i = 1; i < argc; i++)
2738 if (!wcscmp( argv[i], L"--headless"))
2740 headless = 1;
2741 continue;
2743 if (!wcscmp( argv[i], L"--unix"))
2745 console.is_unix = 1;
2746 headless = 1;
2747 continue;
2749 if (!wcscmp( argv[i], L"--width" ))
2751 if (++i == argc) return 1;
2752 width = wcstol( argv[i], &end, 0 );
2753 if ((!width && !console.is_unix) || width > 0xffff || *end) return 1;
2754 continue;
2756 if (!wcscmp( argv[i], L"--height" ))
2758 if (++i == argc) return 1;
2759 height = wcstol( argv[i], &end, 0 );
2760 if ((!height && !console.is_unix) || height > 0xffff || *end) return 1;
2761 continue;
2763 if (!wcscmp( argv[i], L"--signal" ))
2765 if (++i == argc) return 1;
2766 signal = ULongToHandle( wcstol( argv[i], &end, 0 ));
2767 if (*end) return 1;
2768 continue;
2770 if (!wcscmp( argv[i], L"--server" ))
2772 if (++i == argc) return 1;
2773 console.server = ULongToHandle( wcstol( argv[i], &end, 0 ));
2774 if (*end) return 1;
2775 continue;
2777 FIXME( "unknown option %s\n", debugstr_w(argv[i]) );
2778 return 1;
2781 if (!console.server)
2783 ERR( "no server handle\n" );
2784 return 1;
2787 if (!width) width = 80;
2788 if (!height) height = 150;
2790 if (!(console.active = create_screen_buffer( &console, 1, width, height ))) return 1;
2791 if (headless)
2793 console.tty_input = GetStdHandle( STD_INPUT_HANDLE );
2794 console.tty_output = GetStdHandle( STD_OUTPUT_HANDLE );
2795 init_tty_output( &console );
2796 if (!console.is_unix && !ensure_tty_input_thread( &console )) return 1;
2798 else
2800 STARTUPINFOW si;
2801 if (!init_window( &console )) return 1;
2802 GetStartupInfoW( &si );
2803 set_console_title( &console, si.lpTitle, wcslen( si.lpTitle ) * sizeof(WCHAR) );
2804 ShowWindow( console.win, (si.dwFlags & STARTF_USESHOWWINDOW) ? si.wShowWindow : SW_SHOW );
2807 if (console.is_unix) RtlAddVectoredExceptionHandler( FALSE, handle_ctrl_c );
2809 return main_loop( &console, signal );