conhost: Use correct pointer type in edit_line_transpose_words (scan-build).
[wine.git] / programs / conhost / conhost.c
blob21fc883850277e604a668e08dfdc797dc0a01061
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->font.face_name );
72 free( screen_buffer->data );
73 free( screen_buffer );
76 static struct screen_buffer *create_screen_buffer( struct console *console, int id, int width, int height )
78 struct screen_buffer *screen_buffer;
79 unsigned int i;
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;
90 if (console->active)
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) );
101 if (screen_buffer->font.face_len)
103 screen_buffer->font.face_name = malloc( screen_buffer->font.face_len * sizeof(WCHAR) );
104 if (!screen_buffer->font.face_name)
106 free( screen_buffer );
107 return NULL;
110 memcpy( screen_buffer->font.face_name, console->active->font.face_name,
111 screen_buffer->font.face_len * sizeof(WCHAR) );
114 else
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;
126 if (wine_rb_put( &screen_buffer_map, LongToPtr(id), &screen_buffer->entry ))
128 free( screen_buffer );
129 ERR( "id %x already exists\n", id );
130 return NULL;
133 if (!(screen_buffer->data = malloc( screen_buffer->width * screen_buffer->height *
134 sizeof(*screen_buffer->data) )))
136 destroy_screen_buffer( screen_buffer );
137 return NULL;
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) );
147 return screen_buffer;
150 static BOOL is_active( struct screen_buffer *screen_buffer )
152 return screen_buffer == screen_buffer->console->active;
155 static unsigned int get_tty_cp( struct console *console )
157 return console->is_unix ? CP_UNIXCP : CP_UTF8;
160 static void tty_flush( struct console *console )
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;
170 static void tty_write( struct console *console, const char *buffer, size_t size )
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))
177 memcpy( console->tty_buffer + console->tty_buffer_count, buffer, size );
178 console->tty_buffer_count += size;
180 else
182 assert( !console->tty_buffer_count );
183 if (!WriteFile( console->tty_output, buffer, size, NULL, NULL ))
184 WARN( "write failed: %lu\n", GetLastError() );
188 static void *tty_alloc_buffer( struct console *console, size_t size )
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;
197 static void hide_tty_cursor( struct console *console )
199 if (console->tty_cursor_visible)
201 tty_write( console, "\x1b[?25l", 6 );
202 console->tty_cursor_visible = FALSE;
206 static void set_tty_cursor( struct console *console, unsigned int x, unsigned int y )
208 char buf[64];
210 if (console->tty_cursor_x == x && console->tty_cursor_y == y) return;
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)
216 if (console->tty_cursor_x >= console->active->width)
218 if (console->is_unix)
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;
226 else if (console->active->mode & ENABLE_WRAP_AT_EOL_OUTPUT)
228 console->tty_cursor_x--;
230 if (console->tty_cursor_x == x) return;
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 );
236 else if (x || y)
238 hide_tty_cursor( console );
239 sprintf( buf, "\x1b[%u;%uH", y + 1, x + 1);
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) );
247 static void set_tty_cursor_relative( struct console *console, unsigned int x, unsigned int y )
249 if (y < console->tty_cursor_y)
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;
256 else
258 while (console->tty_cursor_y < y)
260 console->tty_cursor_x = 0;
261 console->tty_cursor_y++;
262 tty_write( console, "\r\n", 2 );
265 set_tty_cursor( console, x, y );
268 static void set_tty_attr( struct console *console, unsigned int attr )
270 char buf[8];
272 if ((attr & 0x0f) != (console->tty_attr & 0x0f))
274 if ((attr & 0x0f) != 7)
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) );
284 else tty_write( console, "\x1b[m", 3 );
287 if ((attr & 0xf0) != (console->tty_attr & 0xf0) && attr != 7)
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) );
298 console->tty_attr = attr;
301 static void tty_sync( struct console *console )
303 if (!console->tty_output) return;
305 if (console->active->cursor_visible)
307 set_tty_cursor( console, get_bounded_cursor_x( console->active ), console->active->cursor_y );
308 if (!console->tty_cursor_visible)
310 tty_write( console, "\x1b[?25h", 6 ); /* show cursor */
311 console->tty_cursor_visible = TRUE;
314 else if (console->tty_cursor_visible)
315 hide_tty_cursor( console );
316 tty_flush( console );
319 static void init_tty_output( struct console *console )
321 if (!console->is_unix)
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) */
328 else console->tty_attr = empty_char_info.attr;
329 console->tty_cursor_visible = TRUE;
332 /* no longer use relative cursor positioning (legacy API have been used) */
333 static void enter_absolute_mode( struct console *console )
335 console->use_relative_cursor = 0;
338 static void scroll_to_cursor( struct screen_buffer *screen_buffer )
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;
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;
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;
357 static void update_output( struct screen_buffer *screen_buffer, RECT *rect )
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');
365 if (!is_active( screen_buffer ) || rect->top > rect->bottom || rect->right < rect->left)
366 return;
368 TRACE( "%s\n", wine_dbgstr_rect( rect ));
370 if (screen_buffer->console->window)
372 update_window_region( screen_buffer->console, rect );
373 return;
375 if (!screen_buffer->console->tty_output) return;
377 hide_tty_cursor( screen_buffer->console );
379 for (y = rect->top; y <= rect->bottom; y++)
381 for (trailing_spaces = 0; trailing_spaces < screen_buffer->width; trailing_spaces++)
383 ch = &screen_buffer->data[(y + 1) * screen_buffer->width - trailing_spaces - 1];
384 if (ch->ch != ' ' || ch->attr != 7) break;
386 if (trailing_spaces < 4) trailing_spaces = 0;
388 for (x = rect->left; x <= rect->right; x++)
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 );
394 if (x + trailing_spaces >= screen_buffer->width)
396 tty_write( screen_buffer->console, "\x1b[K", 3 );
397 break;
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++;
409 empty_update_rect( screen_buffer, rect );
412 static void new_line( struct screen_buffer *screen_buffer, RECT *update_rect )
414 unsigned int i;
416 assert( screen_buffer->cursor_y >= screen_buffer->height );
417 screen_buffer->cursor_y = screen_buffer->height - 1;
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 );
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 ))
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 );
437 static void write_char( struct screen_buffer *screen_buffer, WCHAR ch, RECT *update_rect, unsigned int *home_y )
439 if (screen_buffer->cursor_x == screen_buffer->width)
441 screen_buffer->cursor_x = 0;
442 screen_buffer->cursor_y++;
444 if (screen_buffer->cursor_y == screen_buffer->height)
446 if (home_y)
448 if (!*home_y) return;
449 (*home_y)--;
451 new_line( screen_buffer, update_rect );
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++;
463 static NTSTATUS read_complete( struct console *console, NTSTATUS status, const void *buf, size_t size, int signal )
465 SERVER_START_REQ( get_next_console_request )
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 );
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;
484 static NTSTATUS read_console_input( struct console *console, size_t out_size )
486 size_t count = min( out_size / sizeof(INPUT_RECORD), console->record_count );
488 TRACE("count %Iu\n", count);
490 read_complete( console, STATUS_SUCCESS, console->records, count * sizeof(*console->records),
491 console->record_count > count );
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;
500 static void read_from_buffer( struct console *console, size_t out_size )
502 size_t len, read_len = 0;
503 char *buf = NULL;
505 switch( console->read_ioctl )
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)
517 len += WideCharToMultiByte( console->input_cp, 0, console->read_buffer + read_len, 1, NULL, 0, NULL, NULL );
518 read_len++;
520 if (len)
522 if (!(buf = malloc( len )))
524 read_complete( console, STATUS_NO_MEMORY, NULL, 0, console->record_count != 0 );
525 return;
527 WideCharToMultiByte( console->input_cp, 0, console->read_buffer, read_len, buf, len, NULL, NULL );
529 len = min( out_size, len );
530 read_complete( console, STATUS_SUCCESS, buf, len, console->record_count != 0 );
531 free( buf );
532 break;
535 if (read_len < console->read_buffer_count)
537 memmove( console->read_buffer, console->read_buffer + read_len,
538 (console->read_buffer_count - read_len) * sizeof(WCHAR) );
540 if (!(console->read_buffer_count -= read_len))
541 free( console->read_buffer );
544 static void append_input_history( struct console *console, const WCHAR *str, size_t len )
546 struct history_line *ptr;
548 if (!console->history_size) return;
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;
556 if (!(ptr = malloc( offsetof( struct history_line, text[len / sizeof(WCHAR)] )))) return;
557 ptr->len = len;
558 memcpy( ptr->text, str, len );
560 if (console->history_index < console->history_size)
562 console->history[console->history_index++] = ptr;
564 else
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;
573 static void edit_line_update( struct console *console, unsigned int begin, unsigned int length )
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 );
581 static BOOL edit_line_grow( struct console *console, size_t length )
583 struct edit_line *ctx = &console->edit_line;
584 WCHAR *new_buf;
585 size_t new_size;
587 if (ctx->len + length < ctx->size) return TRUE;
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 )))
593 ctx->status = STATUS_NO_MEMORY;
594 return FALSE;
596 ctx->buf = new_buf;
597 ctx->size = new_size;
598 return TRUE;
601 static void edit_line_delete( struct console *console, int begin, int end )
603 struct edit_line *ctx = &console->edit_line;
604 unsigned int len = end - begin;
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;
614 static void edit_line_insert( struct console *console, const WCHAR *str, unsigned int len )
616 struct edit_line *ctx = &console->edit_line;
617 unsigned int update_len;
619 if (!len) return;
620 if (ctx->insert_mode)
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;
629 else
631 if (ctx->cursor + len > ctx->len)
633 if (!edit_line_grow( console, (ctx->cursor + len) - ctx->len) )
634 return;
635 ctx->len = ctx->cursor + len;
637 update_len = len;
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;
645 static void edit_line_save_yank( struct console *console, unsigned int begin, unsigned int end )
647 struct edit_line *ctx = &console->edit_line;
648 unsigned int len = end - begin;
649 if (len <= 0) return;
651 free(ctx->yanked);
652 ctx->yanked = malloc( (len + 1) * sizeof(WCHAR) );
653 if (!ctx->yanked)
655 ctx->status = STATUS_NO_MEMORY;
656 return;
658 memcpy( ctx->yanked, &ctx->buf[begin], len * sizeof(WCHAR) );
659 ctx->yanked[len] = 0;
662 static int edit_line_left_word_transition( struct console *console, int offset )
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 );
671 static int edit_line_right_word_transition( struct console *console, int offset )
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);
681 static WCHAR *edit_line_history( struct console *console, unsigned int index )
683 WCHAR *ptr = NULL;
685 if (index < console->history_index)
687 if ((ptr = malloc( console->history[index]->len + sizeof(WCHAR) )))
689 memcpy( ptr, console->history[index]->text, console->history[index]->len );
690 ptr[console->history[index]->len / sizeof(WCHAR)] = 0;
693 else if(console->edit_line.current_history)
695 if ((ptr = malloc( (lstrlenW(console->edit_line.current_history) + 1) * sizeof(WCHAR) )))
696 lstrcpyW( ptr, console->edit_line.current_history );
698 return ptr;
701 static void edit_line_move_to_history( struct console *console, int index )
703 struct edit_line *ctx = &console->edit_line;
704 WCHAR *line = edit_line_history(console, index);
705 size_t len = line ? lstrlenW(line) : 0;
707 /* save current line edition for recall when needed */
708 if (ctx->history_index == console->history_index)
710 free( ctx->current_history );
711 ctx->current_history = malloc( (ctx->len + 1) * sizeof(WCHAR) );
712 if (ctx->current_history)
714 memcpy( ctx->current_history, ctx->buf, (ctx->len + 1) * sizeof(WCHAR) );
716 else
718 free( line );
719 ctx->status = STATUS_NO_MEMORY;
720 return;
724 /* need to clean also the screen if new string is shorter than old one */
725 edit_line_delete(console, 0, ctx->len);
726 ctx->cursor = 0;
727 /* insert new string */
728 if (edit_line_grow(console, len + 1))
730 edit_line_insert( console, line, len );
731 ctx->history_index = index;
733 free(line);
736 static void edit_line_find_in_history( struct console *console )
738 struct edit_line *ctx = &console->edit_line;
739 int start_pos = ctx->history_index;
740 unsigned int len, oldoffset;
741 WCHAR *line;
743 if (!console->history_index) return;
744 if (ctx->history_index && ctx->history_index == console->history_index)
746 start_pos--;
747 ctx->history_index--;
752 line = edit_line_history(console, ctx->history_index);
754 if (ctx->history_index) ctx->history_index--;
755 else ctx->history_index = console->history_index - 1;
757 len = lstrlenW(line) + 1;
758 if (len >= ctx->cursor && !memcmp( ctx->buf, line, ctx->cursor * sizeof(WCHAR) ))
760 /* need to clean also the screen if new string is shorter than old one */
761 edit_line_delete(console, 0, ctx->len);
763 if (edit_line_grow(console, len))
765 oldoffset = ctx->cursor;
766 ctx->cursor = 0;
767 edit_line_insert( console, line, len - 1 );
768 ctx->cursor = oldoffset;
769 free(line);
770 return;
773 free(line);
775 while (ctx->history_index != start_pos);
778 static void edit_line_move_left( struct console *console )
780 if (console->edit_line.cursor > 0) console->edit_line.cursor--;
783 static void edit_line_move_right( struct console *console )
785 struct edit_line *ctx = &console->edit_line;
786 if (ctx->cursor < ctx->len) ctx->cursor++;
789 static void edit_line_move_left_word( struct console *console )
791 console->edit_line.cursor = edit_line_left_word_transition( console, console->edit_line.cursor );
794 static void edit_line_move_right_word( struct console *console )
796 console->edit_line.cursor = edit_line_right_word_transition( console, console->edit_line.cursor );
799 static void edit_line_move_home( struct console *console )
801 console->edit_line.cursor = 0;
804 static void edit_line_move_end( struct console *console )
806 console->edit_line.cursor = console->edit_line.len;
809 static void edit_line_set_mark( struct console *console )
811 console->edit_line.mark = console->edit_line.cursor;
814 static void edit_line_exchange_mark( struct console *console )
816 struct edit_line *ctx = &console->edit_line;
817 unsigned int cursor;
819 if (ctx->mark > ctx->len) return;
820 cursor = ctx->cursor;
821 ctx->cursor = ctx->mark;
822 ctx->mark = cursor;
825 static void edit_line_copy_marked_zone( struct console *console )
827 struct edit_line *ctx = &console->edit_line;
828 unsigned int begin, end;
830 if (ctx->mark > ctx->len || ctx->mark == ctx->cursor) return;
831 if (ctx->mark > ctx->cursor)
833 begin = ctx->cursor;
834 end = ctx->mark;
836 else
838 begin = ctx->mark;
839 end = ctx->cursor;
841 edit_line_save_yank( console, begin, end );
844 static void edit_line_transpose_char( struct console *console )
846 struct edit_line *ctx = &console->edit_line;
847 WCHAR c;
849 if (!ctx->cursor || ctx->cursor == ctx->len) return;
851 c = ctx->buf[ctx->cursor];
852 ctx->buf[ctx->cursor] = ctx->buf[ctx->cursor - 1];
853 ctx->buf[ctx->cursor - 1] = c;
855 edit_line_update( console, ctx->cursor - 1, 2 );
856 ctx->cursor++;
859 static void edit_line_transpose_words( struct console *console )
861 struct edit_line *ctx = &console->edit_line;
862 unsigned int left_offset = edit_line_left_word_transition( console, ctx->cursor );
863 unsigned int right_offset = edit_line_right_word_transition( console, ctx->cursor );
864 if (left_offset < ctx->cursor && right_offset > ctx->cursor)
866 unsigned int len_r = right_offset - ctx->cursor;
867 unsigned int len_l = ctx->cursor - left_offset;
868 WCHAR *tmp = malloc( len_r * sizeof(WCHAR) );
869 if (!tmp)
871 ctx->status = STATUS_NO_MEMORY;
872 return;
875 memcpy( tmp, &ctx->buf[ctx->cursor], len_r * sizeof(WCHAR) );
876 memmove( &ctx->buf[left_offset + len_r], &ctx->buf[left_offset],
877 len_l * sizeof(WCHAR) );
878 memcpy( &ctx->buf[left_offset], tmp, len_r * sizeof(WCHAR) );
879 free(tmp);
881 edit_line_update( console, left_offset, len_l + len_r );
882 ctx->cursor = right_offset;
886 static void edit_line_lower_case_word( struct console *console )
888 struct edit_line *ctx = &console->edit_line;
889 unsigned int new_offset = edit_line_right_word_transition( console, ctx->cursor );
890 if (new_offset != ctx->cursor)
892 CharLowerBuffW( ctx->buf + ctx->cursor, new_offset - ctx->cursor + 1 );
893 edit_line_update( console, ctx->cursor, new_offset - ctx->cursor + 1 );
894 ctx->cursor = new_offset;
898 static void edit_line_upper_case_word( struct console *console )
900 struct edit_line *ctx = &console->edit_line;
901 unsigned int new_offset = edit_line_right_word_transition( console, ctx->cursor );
902 if (new_offset != ctx->cursor)
904 CharUpperBuffW( ctx->buf + ctx->cursor, new_offset - ctx->cursor + 1 );
905 edit_line_update( console, ctx->cursor, new_offset - ctx->cursor + 1 );
906 ctx->cursor = new_offset;
910 static void edit_line_capitalize_word( struct console *console )
912 struct edit_line *ctx = &console->edit_line;
913 unsigned int new_offset = edit_line_right_word_transition( console, ctx->cursor );
914 if (new_offset != ctx->cursor)
916 CharUpperBuffW( ctx->buf + ctx->cursor, 1 );
917 CharLowerBuffW( ctx->buf + ctx->cursor + 1, new_offset - ctx->cursor );
918 edit_line_update( console, ctx->cursor, new_offset - ctx->cursor + 1 );
919 ctx->cursor = new_offset;
923 static void edit_line_yank( struct console *console )
925 struct edit_line *ctx = &console->edit_line;
926 if (ctx->yanked) edit_line_insert( console, ctx->yanked, wcslen(ctx->yanked) );
929 static void edit_line_kill_suffix( struct console *console )
931 struct edit_line *ctx = &console->edit_line;
932 edit_line_save_yank( console, ctx->cursor, ctx->len );
933 edit_line_delete( console, ctx->cursor, ctx->len );
936 static void edit_line_kill_prefix( struct console *console )
938 struct edit_line *ctx = &console->edit_line;
939 if (ctx->cursor)
941 edit_line_save_yank( console, 0, ctx->cursor );
942 edit_line_delete( console, 0, ctx->cursor );
943 ctx->cursor = 0;
947 static void edit_line_kill_marked_zone( struct console *console )
949 struct edit_line *ctx = &console->edit_line;
950 unsigned int begin, end;
952 if (ctx->mark > ctx->len || ctx->mark == ctx->cursor)
953 return;
954 if (ctx->mark > ctx->cursor)
956 begin = ctx->cursor;
957 end = ctx->mark;
959 else
961 begin = ctx->mark;
962 end = ctx->cursor;
964 edit_line_save_yank( console, begin, end );
965 edit_line_delete( console, begin, end );
966 ctx->cursor = begin;
969 static void edit_line_delete_prev( struct console *console )
971 struct edit_line *ctx = &console->edit_line;
972 if (ctx->cursor)
974 edit_line_delete( console, ctx->cursor - 1, ctx->cursor );
975 ctx->cursor--;
979 static void edit_line_delete_char( struct console *console )
981 struct edit_line *ctx = &console->edit_line;
982 if (ctx->cursor < ctx->len)
983 edit_line_delete( console, ctx->cursor, ctx->cursor + 1 );
986 static void edit_line_delete_left_word( struct console *console )
988 struct edit_line *ctx = &console->edit_line;
989 unsigned int new_offset = edit_line_left_word_transition( console, ctx->cursor );
990 if (new_offset != ctx->cursor)
992 edit_line_delete( console, new_offset, ctx->cursor );
993 ctx->cursor = new_offset;
997 static void edit_line_delete_right_word( struct console *console )
999 struct edit_line *ctx = &console->edit_line;
1000 unsigned int new_offset = edit_line_right_word_transition( console, ctx->cursor );
1001 if (new_offset != ctx->cursor)
1003 edit_line_delete( console, ctx->cursor, new_offset );
1007 static void edit_line_move_to_prev_hist( struct console *console )
1009 if (console->edit_line.history_index)
1010 edit_line_move_to_history( console, console->edit_line.history_index - 1 );
1013 static void edit_line_move_to_next_hist( struct console *console )
1015 if (console->edit_line.history_index < console->history_index)
1016 edit_line_move_to_history( console, console->edit_line.history_index + 1 );
1019 static void edit_line_move_to_first_hist( struct console *console )
1021 if (console->edit_line.history_index)
1022 edit_line_move_to_history( console, 0 );
1025 static void edit_line_move_to_last_hist( struct console *console )
1027 if (console->edit_line.history_index != console->history_index)
1028 edit_line_move_to_history( console, console->history_index );
1031 static void edit_line_redraw( struct console *console )
1033 if (console->mode & ENABLE_ECHO_INPUT)
1034 edit_line_update( console, 0, console->edit_line.len );
1037 static void edit_line_toggle_insert( struct console *console )
1039 struct edit_line *ctx = &console->edit_line;
1040 ctx->insert_key = !ctx->insert_key;
1041 console->active->cursor_size = ctx->insert_key ? 100 : 25;
1044 static void edit_line_done( struct console *console )
1046 console->edit_line.status = STATUS_SUCCESS;
1049 struct edit_line_key_entry
1051 WCHAR val; /* vk or unicode char */
1052 void (*func)( struct console *console );
1055 struct edit_line_key_map
1057 DWORD key_state; /* keyState (from INPUT_RECORD) to match */
1058 BOOL is_char; /* check vk or char */
1059 const struct edit_line_key_entry *entries;
1062 #define CTRL(x) ((x) - '@')
1063 static const struct edit_line_key_entry std_key_map[] =
1065 { VK_BACK, edit_line_delete_prev },
1066 { VK_RETURN, edit_line_done },
1067 { VK_DELETE, edit_line_delete_char },
1068 { 0 }
1071 static const struct edit_line_key_entry emacs_key_map_ctrl[] =
1073 { CTRL('@'), edit_line_set_mark },
1074 { CTRL('A'), edit_line_move_home },
1075 { CTRL('B'), edit_line_move_left },
1076 { CTRL('D'), edit_line_delete_char },
1077 { CTRL('E'), edit_line_move_end },
1078 { CTRL('F'), edit_line_move_right },
1079 { CTRL('H'), edit_line_delete_prev },
1080 { CTRL('J'), edit_line_done },
1081 { CTRL('K'), edit_line_kill_suffix },
1082 { CTRL('L'), edit_line_redraw },
1083 { CTRL('M'), edit_line_done },
1084 { CTRL('N'), edit_line_move_to_next_hist },
1085 { CTRL('P'), edit_line_move_to_prev_hist },
1086 { CTRL('T'), edit_line_transpose_char },
1087 { CTRL('W'), edit_line_kill_marked_zone },
1088 { CTRL('X'), edit_line_exchange_mark },
1089 { CTRL('Y'), edit_line_yank },
1090 { 0 }
1093 static const struct edit_line_key_entry emacs_key_map_alt[] =
1095 { 0x7f, edit_line_delete_left_word },
1096 { '<', edit_line_move_to_first_hist },
1097 { '>', edit_line_move_to_last_hist },
1098 { 'b', edit_line_move_left_word },
1099 { 'c', edit_line_capitalize_word },
1100 { 'd', edit_line_delete_right_word },
1101 { 'f', edit_line_move_right_word },
1102 { 'l', edit_line_lower_case_word },
1103 { 't', edit_line_transpose_words },
1104 { 'u', edit_line_upper_case_word },
1105 { 'w', edit_line_copy_marked_zone },
1106 { 0 }
1109 static const struct edit_line_key_entry emacs_std_key_map[] =
1111 { VK_PRIOR, edit_line_move_to_prev_hist },
1112 { VK_NEXT, edit_line_move_to_next_hist },
1113 { VK_END, edit_line_move_end },
1114 { VK_HOME, edit_line_move_home },
1115 { VK_RIGHT, edit_line_move_right },
1116 { VK_LEFT, edit_line_move_left },
1117 { VK_INSERT, edit_line_toggle_insert },
1118 { 0 }
1121 static const struct edit_line_key_map emacs_key_map[] =
1123 { 0, 0, std_key_map },
1124 { 0, 0, emacs_std_key_map },
1125 { RIGHT_ALT_PRESSED, 1, emacs_key_map_alt },
1126 { LEFT_ALT_PRESSED, 1, emacs_key_map_alt },
1127 { RIGHT_CTRL_PRESSED, 1, emacs_key_map_ctrl },
1128 { LEFT_CTRL_PRESSED, 1, emacs_key_map_ctrl },
1129 { 0 }
1132 static const struct edit_line_key_entry win32_std_key_map[] =
1134 { VK_LEFT, edit_line_move_left },
1135 { VK_RIGHT, edit_line_move_right },
1136 { VK_HOME, edit_line_move_home },
1137 { VK_END, edit_line_move_end },
1138 { VK_UP, edit_line_move_to_prev_hist },
1139 { VK_DOWN, edit_line_move_to_next_hist },
1140 { VK_INSERT, edit_line_toggle_insert },
1141 { VK_F8, edit_line_find_in_history },
1142 { 0 }
1145 static const struct edit_line_key_entry win32_key_map_ctrl[] =
1147 { VK_LEFT, edit_line_move_left_word },
1148 { VK_RIGHT, edit_line_move_right_word },
1149 { VK_END, edit_line_kill_suffix },
1150 { VK_HOME, edit_line_kill_prefix },
1151 { 'M', edit_line_done },
1152 { 0 }
1155 static const struct edit_line_key_map win32_key_map[] =
1157 { 0, 0, std_key_map },
1158 { SHIFT_PRESSED, 0, std_key_map },
1159 { 0, 0, win32_std_key_map },
1160 { RIGHT_CTRL_PRESSED, 0, win32_key_map_ctrl },
1161 { LEFT_CTRL_PRESSED, 0, win32_key_map_ctrl },
1162 { 0 }
1164 #undef CTRL
1166 static unsigned int edit_line_string_width( const WCHAR *str, unsigned int len)
1168 unsigned int i, offset = 0;
1169 for (i = 0; i < len; i++) offset += str[i] < ' ' ? 2 : 1;
1170 return offset;
1173 static void update_read_output( struct console *console, BOOL newline )
1175 struct screen_buffer *screen_buffer = console->active;
1176 struct edit_line *ctx = &console->edit_line;
1177 int offset = 0, j, end_offset;
1178 RECT update_rect;
1180 empty_update_rect( screen_buffer, &update_rect );
1182 if (ctx->update_end >= ctx->update_begin)
1184 TRACE( "update %d-%d %s\n", ctx->update_begin, ctx->update_end,
1185 debugstr_wn( ctx->buf + ctx->update_begin, ctx->update_end - ctx->update_begin + 1 ));
1187 hide_tty_cursor( screen_buffer->console );
1189 offset = edit_line_string_width( ctx->buf, ctx->update_begin );
1190 screen_buffer->cursor_x = (ctx->home_x + offset) % screen_buffer->width;
1191 screen_buffer->cursor_y = ctx->home_y + (ctx->home_x + offset) / screen_buffer->width;
1192 for (j = ctx->update_begin; j <= ctx->update_end; j++)
1194 if (screen_buffer->cursor_y >= screen_buffer->height && !ctx->home_y) break;
1195 if (j >= ctx->len) break;
1196 if (ctx->buf[j] < ' ')
1198 write_char( screen_buffer, '^', &update_rect, &ctx->home_y );
1199 write_char( screen_buffer, '@' + ctx->buf[j], &update_rect, &ctx->home_y );
1200 offset += 2;
1202 else
1204 write_char( screen_buffer, ctx->buf[j], &update_rect, &ctx->home_y );
1205 offset++;
1208 end_offset = ctx->end_offset;
1209 ctx->end_offset = offset;
1210 if (j >= ctx->len)
1212 /* clear trailing characters if buffer was shortened */
1213 while (offset < end_offset && screen_buffer->cursor_y < screen_buffer->height)
1215 write_char( screen_buffer, ' ', &update_rect, &ctx->home_y );
1216 offset++;
1221 if (newline)
1223 offset = edit_line_string_width( ctx->buf, ctx->len );
1224 screen_buffer->cursor_x = 0;
1225 screen_buffer->cursor_y = ctx->home_y + (ctx->home_x + offset) / screen_buffer->width;
1226 if (++screen_buffer->cursor_y >= screen_buffer->height)
1227 new_line( screen_buffer, &update_rect );
1229 else
1231 offset = edit_line_string_width( ctx->buf, ctx->cursor );
1232 screen_buffer->cursor_y = ctx->home_y + (ctx->home_x + offset) / screen_buffer->width;
1233 if (screen_buffer->cursor_y < screen_buffer->height)
1235 screen_buffer->cursor_x = (ctx->home_x + offset) % screen_buffer->width;
1237 else
1239 screen_buffer->cursor_x = screen_buffer->width - 1;
1240 screen_buffer->cursor_y = screen_buffer->height - 1;
1244 /* always try to use relative cursor positions in UNIX mode so that it works even if cursor
1245 * position is out of sync */
1246 if (update_rect.left <= update_rect.right && update_rect.top <= update_rect.bottom)
1248 if (console->is_unix)
1249 set_tty_cursor_relative( screen_buffer->console, update_rect.left, update_rect.top );
1250 update_output( screen_buffer, &update_rect );
1251 scroll_to_cursor( screen_buffer );
1253 if (console->is_unix)
1254 set_tty_cursor_relative( screen_buffer->console, screen_buffer->cursor_x, screen_buffer->cursor_y );
1255 tty_sync( screen_buffer->console );
1256 update_window_config( screen_buffer->console, TRUE );
1259 /* can end on any ctrl-character: from 0x00 up to 0x1F) */
1260 #define FIRST_NON_CONTROL_CHAR (L' ')
1262 static NTSTATUS process_console_input( struct console *console )
1264 struct edit_line *ctx = &console->edit_line;
1265 unsigned int i;
1266 WCHAR ctrl_value = FIRST_NON_CONTROL_CHAR;
1267 unsigned int ctrl_keyvalue = 0;
1269 switch (console->read_ioctl)
1271 case IOCTL_CONDRV_READ_INPUT:
1272 if (console->record_count) read_console_input( console, console->pending_read );
1273 return STATUS_SUCCESS;
1274 case IOCTL_CONDRV_READ_CONSOLE:
1275 case IOCTL_CONDRV_READ_CONSOLE_CONTROL:
1276 case IOCTL_CONDRV_READ_FILE:
1277 break;
1278 default:
1279 assert( !console->read_ioctl );
1280 if (console->record_count && !console->signaled)
1281 read_complete( console, STATUS_PENDING, NULL, 0, TRUE ); /* signal server */
1282 return STATUS_SUCCESS;
1285 ctx->update_begin = ctx->len + 1;
1286 ctx->update_end = 0;
1288 for (i = 0; i < console->record_count && ctx->status == STATUS_PENDING; i++)
1290 void (*func)( struct console *console ) = NULL;
1291 INPUT_RECORD ir = console->records[i];
1293 if (ir.EventType != KEY_EVENT || !ir.Event.KeyEvent.bKeyDown) continue;
1295 TRACE( "key code=%02x scan=%02x char=%02x state=%08lx\n",
1296 ir.Event.KeyEvent.wVirtualKeyCode, ir.Event.KeyEvent.wVirtualScanCode,
1297 ir.Event.KeyEvent.uChar.UnicodeChar, ir.Event.KeyEvent.dwControlKeyState );
1299 if (console->mode & ENABLE_LINE_INPUT)
1301 const struct edit_line_key_entry *entry;
1302 const struct edit_line_key_map *map;
1303 unsigned int state;
1305 /* mask out some bits which don't interest us */
1306 state = ir.Event.KeyEvent.dwControlKeyState & ~(NUMLOCK_ON|SCROLLLOCK_ON|CAPSLOCK_ON|ENHANCED_KEY);
1308 if (ctx->ctrl_mask &&
1309 ir.Event.KeyEvent.uChar.UnicodeChar &&
1310 ir.Event.KeyEvent.uChar.UnicodeChar < FIRST_NON_CONTROL_CHAR)
1312 if (ctx->ctrl_mask & (1u << ir.Event.KeyEvent.uChar.UnicodeChar))
1314 ctrl_value = ir.Event.KeyEvent.uChar.UnicodeChar;
1315 ctrl_keyvalue = ir.Event.KeyEvent.dwControlKeyState;
1316 ctx->status = STATUS_SUCCESS;
1317 TRACE("Found ctrl char in mask: ^%lc %x\n", ir.Event.KeyEvent.uChar.UnicodeChar + '@', ctx->ctrl_mask);
1318 continue;
1320 if (ir.Event.KeyEvent.uChar.UnicodeChar == 10) continue;
1322 func = NULL;
1323 for (map = console->edition_mode ? emacs_key_map : win32_key_map; map->entries != NULL; map++)
1325 if (map->key_state != state)
1326 continue;
1327 if (map->is_char)
1329 for (entry = &map->entries[0]; entry->func != 0; entry++)
1330 if (entry->val == ir.Event.KeyEvent.uChar.UnicodeChar) break;
1332 else
1334 for (entry = &map->entries[0]; entry->func != 0; entry++)
1335 if (entry->val == ir.Event.KeyEvent.wVirtualKeyCode) break;
1338 if (entry->func)
1340 func = entry->func;
1341 break;
1346 ctx->insert_mode = ((console->mode & (ENABLE_INSERT_MODE | ENABLE_EXTENDED_FLAGS)) ==
1347 (ENABLE_INSERT_MODE | ENABLE_EXTENDED_FLAGS))
1348 ^ ctx->insert_key;
1350 if (func) func( console );
1351 else if (ir.Event.KeyEvent.uChar.UnicodeChar)
1352 edit_line_insert( console, &ir.Event.KeyEvent.uChar.UnicodeChar, 1 );
1354 if (!(console->mode & ENABLE_LINE_INPUT) && ctx->status == STATUS_PENDING)
1356 if (console->read_ioctl == IOCTL_CONDRV_READ_FILE)
1358 if (WideCharToMultiByte(console->input_cp, 0, ctx->buf, ctx->len, NULL, 0, NULL, NULL)
1359 >= console->pending_read)
1360 ctx->status = STATUS_SUCCESS;
1362 else if (ctx->len >= console->pending_read / sizeof(WCHAR))
1363 ctx->status = STATUS_SUCCESS;
1367 if (console->record_count > i) memmove( console->records, console->records + i,
1368 (console->record_count - i) * sizeof(*console->records) );
1369 console->record_count -= i;
1371 if (ctx->status == STATUS_PENDING && !(console->mode & ENABLE_LINE_INPUT) && ctx->len)
1372 ctx->status = STATUS_SUCCESS;
1374 if (console->mode & ENABLE_ECHO_INPUT) update_read_output( console, !ctx->status && ctrl_value == FIRST_NON_CONTROL_CHAR );
1375 if (ctx->status == STATUS_PENDING) return STATUS_SUCCESS;
1377 if (!ctx->status && (console->mode & ENABLE_LINE_INPUT))
1379 if (ctrl_value < FIRST_NON_CONTROL_CHAR)
1381 edit_line_insert( console, &ctrl_value, 1 );
1382 console->key_state = ctrl_keyvalue;
1384 else
1386 if (ctx->len) append_input_history( console, ctx->buf, ctx->len * sizeof(WCHAR) );
1387 if (edit_line_grow(console, 2))
1389 ctx->buf[ctx->len++] = '\r';
1390 ctx->buf[ctx->len++] = '\n';
1391 ctx->buf[ctx->len] = 0;
1394 TRACE( "return %s\n", debugstr_wn( ctx->buf, ctx->len ));
1397 console->read_buffer = ctx->buf;
1398 console->read_buffer_count = ctx->len;
1399 console->read_buffer_size = ctx->size;
1401 if (ctx->status) read_complete( console, ctx->status, NULL, 0, console->record_count );
1402 else read_from_buffer( console, console->pending_read );
1404 /* reset context */
1405 free( ctx->yanked );
1406 free( ctx->current_history );
1407 memset( &console->edit_line, 0, sizeof(console->edit_line) );
1408 return STATUS_SUCCESS;
1411 static NTSTATUS read_console( struct console *console, unsigned int ioctl, size_t out_size,
1412 const WCHAR *initial, unsigned int initial_len, unsigned int ctrl_mask )
1414 struct edit_line *ctx = &console->edit_line;
1415 TRACE("\n");
1417 if (out_size > INT_MAX)
1419 read_complete( console, STATUS_NO_MEMORY, NULL, 0, console->record_count );
1420 return STATUS_NO_MEMORY;
1423 console->read_ioctl = ioctl;
1424 console->key_state = 0;
1425 if (!out_size || console->read_buffer_count)
1427 read_from_buffer( console, out_size );
1428 return STATUS_SUCCESS;
1431 ctx->history_index = console->history_index;
1432 ctx->home_x = console->active->cursor_x;
1433 ctx->home_y = console->active->cursor_y;
1434 ctx->status = STATUS_PENDING;
1435 if (initial_len && edit_line_grow( console, initial_len + 1 ))
1437 unsigned offset = edit_line_string_width( initial, initial_len );
1438 if (offset > ctx->home_x)
1440 int deltay;
1441 offset -= ctx->home_x + 1;
1442 deltay = offset / console->active->width + 1;
1443 if (ctx->home_y >= deltay)
1444 ctx->home_y -= deltay;
1445 else
1447 ctx->home_y = 0;
1448 FIXME("Support for negative ordinates is missing\n");
1450 ctx->home_x = console->active->width - 1 - (offset % console->active->width);
1452 else
1453 ctx->home_x -= offset;
1454 ctx->cursor = initial_len;
1455 memcpy( ctx->buf, initial, initial_len * sizeof(WCHAR) );
1456 ctx->buf[initial_len] = 0;
1457 ctx->len = initial_len;
1458 ctx->end_offset = initial_len;
1460 else if (edit_line_grow( console, 1 )) ctx->buf[0] = 0;
1461 ctx->ctrl_mask = ctrl_mask;
1463 console->pending_read = out_size;
1464 return process_console_input( console );
1467 static BOOL map_to_ctrlevent( struct console *console, const INPUT_RECORD *record,
1468 unsigned int* event)
1470 if (record->EventType == KEY_EVENT)
1472 if (record->Event.KeyEvent.uChar.UnicodeChar == 'C' - 64 &&
1473 !(record->Event.KeyEvent.dwControlKeyState & ENHANCED_KEY))
1475 *event = CTRL_C_EVENT;
1476 return TRUE;
1478 /* we want to get ctrl-pause/break, but it's already translated by user32 into VK_CANCEL */
1479 if (record->Event.KeyEvent.uChar.UnicodeChar == 0 &&
1480 record->Event.KeyEvent.wVirtualKeyCode == VK_CANCEL &&
1481 record->Event.KeyEvent.dwControlKeyState == LEFT_CTRL_PRESSED)
1483 *event = CTRL_BREAK_EVENT;
1484 return TRUE;
1487 return FALSE;
1490 /* add input events to a console input queue */
1491 NTSTATUS write_console_input( struct console *console, const INPUT_RECORD *records,
1492 unsigned int count, BOOL flush )
1494 TRACE( "%u\n", count );
1496 if (!count) return STATUS_SUCCESS;
1497 if (console->record_count + count > console->record_size)
1499 INPUT_RECORD *new_rec;
1500 if (!(new_rec = realloc( console->records, (console->record_size * 2 + count) * sizeof(INPUT_RECORD) )))
1501 return STATUS_NO_MEMORY;
1502 console->records = new_rec;
1503 console->record_size = console->record_size * 2 + count;
1505 memcpy( console->records + console->record_count, records, count * sizeof(INPUT_RECORD) );
1507 if (console->mode & ENABLE_PROCESSED_INPUT)
1509 unsigned int i = 0;
1510 while (i < count)
1512 unsigned int event;
1514 if (map_to_ctrlevent(console, &records[i], &event))
1516 if (i != count - 1)
1517 memcpy( &console->records[console->record_count + i],
1518 &console->records[console->record_count + i + 1],
1519 (count - i - 1) * sizeof(INPUT_RECORD) );
1520 count--;
1521 if (records[i].Event.KeyEvent.bKeyDown)
1523 struct condrv_ctrl_event ctrl_event;
1524 IO_STATUS_BLOCK io;
1526 ctrl_event.event = event;
1527 ctrl_event.group_id = 0;
1528 NtDeviceIoControlFile( console->server, NULL, NULL, NULL, &io, IOCTL_CONDRV_CTRL_EVENT,
1529 &ctrl_event, sizeof(ctrl_event), NULL, 0 );
1533 else i++;
1536 console->record_count += count;
1537 return flush ? process_console_input( console ) : STATUS_SUCCESS;
1540 static void set_key_input_record( INPUT_RECORD *record, WCHAR ch, unsigned int vk, BOOL is_down, unsigned int ctrl_state )
1542 record->EventType = KEY_EVENT;
1543 record->Event.KeyEvent.bKeyDown = is_down;
1544 record->Event.KeyEvent.wRepeatCount = 1;
1545 record->Event.KeyEvent.uChar.UnicodeChar = ch;
1546 record->Event.KeyEvent.wVirtualKeyCode = vk;
1547 record->Event.KeyEvent.wVirtualScanCode = MapVirtualKeyW( vk, MAPVK_VK_TO_VSC );
1548 record->Event.KeyEvent.dwControlKeyState = ctrl_state;
1551 static NTSTATUS key_press( struct console *console, WCHAR ch, unsigned int vk, unsigned int ctrl_state )
1553 INPUT_RECORD records[8];
1554 unsigned int count = 0, ctrl = 0;
1556 if (ctrl_state & SHIFT_PRESSED)
1558 ctrl |= SHIFT_PRESSED;
1559 set_key_input_record( &records[count++], 0, VK_SHIFT, TRUE, ctrl );
1561 if (ctrl_state & LEFT_ALT_PRESSED)
1563 ctrl |= LEFT_ALT_PRESSED;
1564 set_key_input_record( &records[count++], 0, VK_MENU, TRUE, ctrl );
1566 if (ctrl_state & LEFT_CTRL_PRESSED)
1568 ctrl |= LEFT_CTRL_PRESSED;
1569 set_key_input_record( &records[count++], 0, VK_CONTROL, TRUE, ctrl );
1572 set_key_input_record( &records[count++], ch, vk, TRUE, ctrl );
1573 set_key_input_record( &records[count++], ch, vk, FALSE, ctrl );
1575 if (ctrl & LEFT_CTRL_PRESSED)
1577 ctrl &= ~LEFT_CTRL_PRESSED;
1578 set_key_input_record( &records[count++], 0, VK_CONTROL, FALSE, ctrl );
1580 if (ctrl & LEFT_ALT_PRESSED)
1582 ctrl &= ~LEFT_ALT_PRESSED;
1583 set_key_input_record( &records[count++], 0, VK_MENU, FALSE, ctrl );
1585 if (ctrl & SHIFT_PRESSED)
1587 ctrl &= ~SHIFT_PRESSED;
1588 set_key_input_record( &records[count++], 0, VK_SHIFT, FALSE, ctrl );
1591 return write_console_input( console, records, count, FALSE );
1594 static void char_key_press( struct console *console, WCHAR ch, unsigned int ctrl )
1596 unsigned int vk = VkKeyScanW( ch );
1597 if (vk == ~0) vk = 0;
1598 if (vk & 0x0100) ctrl |= SHIFT_PRESSED;
1599 if (vk & 0x0200) ctrl |= LEFT_CTRL_PRESSED;
1600 if (vk & 0x0400) ctrl |= LEFT_ALT_PRESSED;
1601 vk &= 0xff;
1602 key_press( console, ch, vk, ctrl );
1605 static unsigned int escape_char_to_vk( WCHAR ch, unsigned int *ctrl, WCHAR *outuch )
1607 if (ctrl) *ctrl = 0;
1608 if (outuch) *outuch = '\0';
1610 switch (ch)
1612 case 'A': return VK_UP;
1613 case 'B': return VK_DOWN;
1614 case 'C': return VK_RIGHT;
1615 case 'D': return VK_LEFT;
1616 case 'H': return VK_HOME;
1617 case 'F': return VK_END;
1618 case 'P': return VK_F1;
1619 case 'Q': return VK_F2;
1620 case 'R': return VK_F3;
1621 case 'S': return VK_F4;
1622 case 'Z': if (ctrl && outuch) {*ctrl = SHIFT_PRESSED; *outuch = '\t'; return VK_TAB;}
1623 return 0;
1624 default: return 0;
1628 static unsigned int escape_number_to_vk( unsigned int n )
1630 switch(n)
1632 case 2: return VK_INSERT;
1633 case 3: return VK_DELETE;
1634 case 5: return VK_PRIOR;
1635 case 6: return VK_NEXT;
1636 case 15: return VK_F5;
1637 case 17: return VK_F6;
1638 case 18: return VK_F7;
1639 case 19: return VK_F8;
1640 case 20: return VK_F9;
1641 case 21: return VK_F10;
1642 case 23: return VK_F11;
1643 case 24: return VK_F12;
1644 default: return 0;
1648 static unsigned int convert_modifiers( unsigned int n )
1650 unsigned int ctrl = 0;
1651 if (!n || n > 16) return 0;
1652 n--;
1653 if (n & 1) ctrl |= SHIFT_PRESSED;
1654 if (n & 2) ctrl |= LEFT_ALT_PRESSED;
1655 if (n & 4) ctrl |= LEFT_CTRL_PRESSED;
1656 return ctrl;
1659 static unsigned int process_csi_sequence( struct console *console, const WCHAR *buf, size_t size )
1661 unsigned int n, count = 0, params[8], params_cnt = 0, vk, ctrl;
1662 WCHAR outuch;
1664 for (;;)
1666 n = 0;
1667 while (count < size && '0' <= buf[count] && buf[count] <= '9')
1668 n = n * 10 + buf[count++] - '0';
1669 if (params_cnt < ARRAY_SIZE(params)) params[params_cnt++] = n;
1670 else FIXME( "too many params, skipping %u\n", n );
1671 if (count == size) return 0;
1672 if (buf[count] != ';') break;
1673 if (++count == size) return 0;
1676 if ((vk = escape_char_to_vk( buf[count], &ctrl, &outuch )))
1678 key_press( console, outuch, vk, params_cnt >= 2 ? convert_modifiers( params[1] ) : ctrl );
1679 return count + 1;
1682 switch (buf[count])
1684 case '~':
1685 vk = escape_number_to_vk( params[0] );
1686 key_press( console, 0, vk, params_cnt == 2 ? convert_modifiers( params[1] ) : 0 );
1687 return count + 1;
1689 default:
1690 FIXME( "unhandled sequence %s\n", debugstr_wn( buf, size ));
1691 return 0;
1695 static unsigned int process_input_escape( struct console *console, const WCHAR *buf, size_t size )
1697 unsigned int vk = 0, count = 0, nlen;
1699 if (!size)
1701 key_press( console, 0, VK_ESCAPE, 0 );
1702 return 0;
1705 switch(buf[0])
1707 case '[':
1708 if (++count == size) break;
1709 if ((nlen = process_csi_sequence( console, buf + 1, size - 1 ))) return count + nlen;
1710 break;
1712 case 'O':
1713 if (++count == size) break;
1714 vk = escape_char_to_vk( buf[1], NULL, NULL );
1715 if (vk)
1717 key_press( console, 0, vk, 0 );
1718 return count + 1;
1722 char_key_press( console, buf[0], LEFT_ALT_PRESSED );
1723 return 1;
1726 static DWORD WINAPI tty_input( void *param )
1728 struct console *console = param;
1729 IO_STATUS_BLOCK io;
1730 HANDLE event;
1731 char read_buf[4096];
1732 WCHAR buf[4096];
1733 DWORD count, i;
1734 BOOL signaled;
1735 NTSTATUS status;
1737 if (console->is_unix)
1739 unsigned int h = condrv_handle( console->tty_input );
1740 status = NtDeviceIoControlFile( console->server, NULL, NULL, NULL, &io, IOCTL_CONDRV_SETUP_INPUT,
1741 &h, sizeof(h), NULL, 0 );
1742 if (status) ERR( "input setup failed: %#lx\n", status );
1745 event = CreateEventW( NULL, TRUE, FALSE, NULL );
1747 for (;;)
1749 status = NtReadFile( console->tty_input, event, NULL, NULL, &io, read_buf, sizeof(read_buf), NULL, NULL );
1750 if (status == STATUS_PENDING)
1752 if ((status = NtWaitForSingleObject( event, FALSE, NULL ))) break;
1753 status = io.Status;
1755 if (status) break;
1757 EnterCriticalSection( &console_section );
1758 signaled = console->record_count != 0;
1760 /* FIXME: Handle partial char read */
1761 count = MultiByteToWideChar( get_tty_cp( console ), 0, read_buf, io.Information, buf, ARRAY_SIZE(buf) );
1763 TRACE( "%s\n", debugstr_wn(buf, count) );
1765 for (i = 0; i < count; i++)
1767 WCHAR ch = buf[i];
1768 switch (ch)
1770 case 3: /* end of text */
1771 LeaveCriticalSection( &console_section );
1772 goto done;
1773 case '\n':
1774 key_press( console, '\n', VK_RETURN, LEFT_CTRL_PRESSED );
1775 break;
1776 case '\b':
1777 key_press( console, ch, 'H', LEFT_CTRL_PRESSED );
1778 break;
1779 case 0x1b:
1780 i += process_input_escape( console, buf + i + 1, count - i - 1 );
1781 break;
1782 case 0x1c: /* map ctrl-\ unix-ism into ctrl-break/pause windows-ism for unix consoles */
1783 if (console->is_unix)
1784 key_press( console, 0, VK_CANCEL, LEFT_CTRL_PRESSED );
1785 else
1786 char_key_press( console, ch, 0 );
1787 break;
1788 case 0x7f:
1789 key_press( console, '\b', VK_BACK, 0 );
1790 break;
1791 default:
1792 char_key_press( console, ch, 0 );
1796 process_console_input( console );
1797 if (!signaled && console->record_count)
1799 assert( !console->read_ioctl );
1800 read_complete( console, STATUS_SUCCESS, NULL, 0, TRUE ); /* signal console */
1802 LeaveCriticalSection( &console_section );
1805 TRACE( "NtReadFile failed: %#lx\n", status );
1807 done:
1808 EnterCriticalSection( &console_section );
1809 if (console->read_ioctl) read_complete( console, status, NULL, 0, FALSE );
1810 if (console->is_unix)
1812 unsigned int h = 0;
1813 status = NtDeviceIoControlFile( console->server, NULL, NULL, NULL, &io, IOCTL_CONDRV_SETUP_INPUT,
1814 &h, sizeof(h), NULL, 0 );
1815 if (status) ERR( "input restore failed: %#lx\n", status );
1817 CloseHandle( console->input_thread );
1818 console->input_thread = NULL;
1819 LeaveCriticalSection( &console_section );
1821 return 0;
1824 static BOOL ensure_tty_input_thread( struct console *console )
1826 if (!console->tty_input) return TRUE;
1827 if (!console->input_thread)
1828 console->input_thread = CreateThread( NULL, 0, tty_input, console, 0, NULL );
1829 return console->input_thread != NULL;
1832 static NTSTATUS screen_buffer_activate( struct screen_buffer *screen_buffer )
1834 RECT update_rect;
1835 TRACE( "%p\n", screen_buffer );
1836 screen_buffer->console->active = screen_buffer;
1837 SetRect( &update_rect, 0, 0, screen_buffer->width - 1, screen_buffer->height - 1 );
1838 update_output( screen_buffer, &update_rect );
1839 tty_sync( screen_buffer->console );
1840 update_window_config( screen_buffer->console, FALSE );
1841 return STATUS_SUCCESS;
1844 static NTSTATUS get_output_info( struct screen_buffer *screen_buffer, size_t *out_size )
1846 struct condrv_output_info *info;
1848 *out_size = min( *out_size, sizeof(*info) + screen_buffer->font.face_len * sizeof(WCHAR) );
1849 if (!(info = alloc_ioctl_buffer( *out_size ))) return STATUS_NO_MEMORY;
1851 info->cursor_size = screen_buffer->cursor_size;
1852 info->cursor_visible = screen_buffer->cursor_visible;
1853 info->cursor_x = get_bounded_cursor_x( screen_buffer );
1854 info->cursor_y = screen_buffer->cursor_y;
1855 info->width = screen_buffer->width;
1856 info->height = screen_buffer->height;
1857 info->attr = screen_buffer->attr;
1858 info->popup_attr = screen_buffer->popup_attr;
1859 info->win_left = screen_buffer->win.left;
1860 info->win_top = screen_buffer->win.top;
1861 info->win_right = screen_buffer->win.right;
1862 info->win_bottom = screen_buffer->win.bottom;
1863 info->max_width = screen_buffer->max_width;
1864 info->max_height = screen_buffer->max_height;
1865 info->font_width = screen_buffer->font.width;
1866 info->font_height = screen_buffer->font.height;
1867 info->font_weight = screen_buffer->font.weight;
1868 info->font_pitch_family = screen_buffer->font.pitch_family;
1869 memcpy( info->color_map, screen_buffer->color_map, sizeof(info->color_map) );
1870 if (*out_size > sizeof(*info)) memcpy( info + 1, screen_buffer->font.face_name, *out_size - sizeof(*info) );
1872 TRACE( "%p cursor_size=%u cursor_visible=%x cursor=(%u,%u) width=%u height=%u win=%s attr=%x popup_attr=%x"
1873 " font_width=%u font_height=%u %s\n", screen_buffer, info->cursor_size, info->cursor_visible,
1874 info->cursor_x, info->cursor_y, info->width, info->height, wine_dbgstr_rect(&screen_buffer->win),
1875 info->attr, info->popup_attr, info->font_width, info->font_height,
1876 debugstr_wn( (const WCHAR *)(info + 1), (*out_size - sizeof(*info)) / sizeof(WCHAR) ) );
1877 return STATUS_SUCCESS;
1880 void notify_screen_buffer_size( struct screen_buffer *screen_buffer )
1882 if (is_active( screen_buffer ) && screen_buffer->console->mode & ENABLE_WINDOW_INPUT)
1884 INPUT_RECORD ir;
1885 ir.EventType = WINDOW_BUFFER_SIZE_EVENT;
1886 ir.Event.WindowBufferSizeEvent.dwSize.X = screen_buffer->width;
1887 ir.Event.WindowBufferSizeEvent.dwSize.Y = screen_buffer->height;
1888 write_console_input( screen_buffer->console, &ir, 1, TRUE );
1892 NTSTATUS change_screen_buffer_size( struct screen_buffer *screen_buffer, int new_width, int new_height )
1894 int i, old_width, old_height, copy_width, copy_height;
1895 char_info_t *new_data;
1897 if (!(new_data = malloc( new_width * new_height * sizeof(*new_data) ))) return STATUS_NO_MEMORY;
1899 old_width = screen_buffer->width;
1900 old_height = screen_buffer->height;
1901 copy_width = min( old_width, new_width );
1902 copy_height = min( old_height, new_height );
1904 /* copy all the rows */
1905 for (i = 0; i < copy_height; i++)
1907 memcpy( &new_data[i * new_width], &screen_buffer->data[i * old_width],
1908 copy_width * sizeof(char_info_t) );
1911 /* clear the end of each row */
1912 if (new_width > old_width)
1914 /* fill first row */
1915 for (i = old_width; i < new_width; i++) new_data[i] = empty_char_info;
1916 /* and blast it to the other rows */
1917 for (i = 1; i < copy_height; i++)
1918 memcpy( &new_data[i * new_width + old_width], &new_data[old_width],
1919 (new_width - old_width) * sizeof(char_info_t) );
1922 /* clear remaining rows */
1923 if (new_height > old_height)
1925 /* fill first row */
1926 for (i = 0; i < new_width; i++) new_data[old_height * new_width + i] = empty_char_info;
1927 /* and blast it to the other rows */
1928 for (i = old_height+1; i < new_height; i++)
1929 memcpy( &new_data[i * new_width], &new_data[old_height * new_width],
1930 new_width * sizeof(char_info_t) );
1932 free( screen_buffer->data );
1933 screen_buffer->data = new_data;
1934 screen_buffer->width = new_width;
1935 screen_buffer->height = new_height;
1936 return STATUS_SUCCESS;
1939 static NTSTATUS set_output_info( struct screen_buffer *screen_buffer,
1940 const struct condrv_output_info_params *params, size_t in_size )
1942 const struct condrv_output_info *info = &params->info;
1943 NTSTATUS status;
1945 TRACE( "%p\n", screen_buffer );
1947 if (params->mask & SET_CONSOLE_OUTPUT_INFO_CURSOR_GEOM)
1949 if (info->cursor_size < 1 || info->cursor_size > 100) return STATUS_INVALID_PARAMETER;
1951 screen_buffer->cursor_size = info->cursor_size;
1952 screen_buffer->cursor_visible = !!info->cursor_visible;
1954 if (params->mask & SET_CONSOLE_OUTPUT_INFO_CURSOR_POS)
1956 if (info->cursor_x < 0 || info->cursor_x >= screen_buffer->width ||
1957 info->cursor_y < 0 || info->cursor_y >= screen_buffer->height)
1959 return STATUS_INVALID_PARAMETER;
1962 if (screen_buffer->cursor_x != info->cursor_x || screen_buffer->cursor_y != info->cursor_y)
1964 struct console *console = screen_buffer->console;
1965 screen_buffer->cursor_x = info->cursor_x;
1966 screen_buffer->cursor_y = info->cursor_y;
1967 if (console->use_relative_cursor)
1968 set_tty_cursor_relative( console, screen_buffer->cursor_x, screen_buffer->cursor_y );
1969 scroll_to_cursor( screen_buffer );
1972 if (params->mask & SET_CONSOLE_OUTPUT_INFO_SIZE)
1974 enter_absolute_mode( screen_buffer->console );
1975 /* new screen-buffer cannot be smaller than actual window */
1976 if (info->width < screen_buffer->win.right - screen_buffer->win.left + 1 ||
1977 info->height < screen_buffer->win.bottom - screen_buffer->win.top + 1)
1979 return STATUS_INVALID_PARAMETER;
1981 /* FIXME: there are also some basic minimum and max size to deal with */
1982 if ((status = change_screen_buffer_size( screen_buffer, info->width, info->height ))) return status;
1984 /* scroll window to display sb */
1985 if (screen_buffer->win.right >= info->width)
1987 screen_buffer->win.right -= screen_buffer->win.left;
1988 screen_buffer->win.left = 0;
1990 if (screen_buffer->win.bottom >= info->height)
1992 screen_buffer->win.bottom -= screen_buffer->win.top;
1993 screen_buffer->win.top = 0;
1995 if (screen_buffer->cursor_x >= info->width) screen_buffer->cursor_x = info->width - 1;
1996 if (screen_buffer->cursor_y >= info->height) screen_buffer->cursor_y = info->height - 1;
1998 notify_screen_buffer_size( screen_buffer );
2000 if (params->mask & SET_CONSOLE_OUTPUT_INFO_ATTR)
2002 screen_buffer->attr = info->attr;
2004 if (params->mask & SET_CONSOLE_OUTPUT_INFO_POPUP_ATTR)
2006 screen_buffer->popup_attr = info->popup_attr;
2008 if (params->mask & SET_CONSOLE_OUTPUT_INFO_DISPLAY_WINDOW)
2010 enter_absolute_mode( screen_buffer->console );
2011 if (info->win_left < 0 || info->win_left > info->win_right ||
2012 info->win_right >= screen_buffer->width ||
2013 info->win_top < 0 || info->win_top > info->win_bottom ||
2014 info->win_bottom >= screen_buffer->height)
2016 return STATUS_INVALID_PARAMETER;
2018 if (screen_buffer->win.left != info->win_left || screen_buffer->win.top != info->win_top ||
2019 screen_buffer->win.right != info->win_right || screen_buffer->win.bottom != info->win_bottom)
2021 screen_buffer->win.left = info->win_left;
2022 screen_buffer->win.top = info->win_top;
2023 screen_buffer->win.right = info->win_right;
2024 screen_buffer->win.bottom = info->win_bottom;
2027 if (params->mask & SET_CONSOLE_OUTPUT_INFO_MAX_SIZE)
2029 enter_absolute_mode( screen_buffer->console );
2030 screen_buffer->max_width = info->max_width;
2031 screen_buffer->max_height = info->max_height;
2033 if (params->mask & SET_CONSOLE_OUTPUT_INFO_FONT)
2035 WCHAR *face_name = (WCHAR *)(params + 1);
2036 size_t face_name_size = in_size - sizeof(*params);
2037 unsigned int height = info->font_height;
2038 unsigned int weight = FW_NORMAL;
2040 if (!face_name_size)
2042 face_name = screen_buffer->font.face_name;
2043 face_name_size = screen_buffer->font.face_len * sizeof(WCHAR);
2046 if (!height) height = 12;
2047 if (info->font_weight >= FW_SEMIBOLD) weight = FW_BOLD;
2049 update_console_font( screen_buffer->console, face_name, face_name_size, height, weight );
2052 if (is_active( screen_buffer ))
2054 tty_sync( screen_buffer->console );
2055 update_window_config( screen_buffer->console, FALSE );
2057 return STATUS_SUCCESS;
2060 static NTSTATUS write_console( struct screen_buffer *screen_buffer, const WCHAR *buffer, size_t len )
2062 RECT update_rect;
2063 size_t i, j;
2065 TRACE( "%s\n", debugstr_wn(buffer, len) );
2067 empty_update_rect( screen_buffer, &update_rect );
2069 for (i = 0; i < len; i++)
2071 if (screen_buffer->mode & ENABLE_PROCESSED_OUTPUT)
2073 switch (buffer[i])
2075 case '\b':
2076 screen_buffer->cursor_x = get_bounded_cursor_x( screen_buffer );
2077 if (screen_buffer->cursor_x) screen_buffer->cursor_x--;
2078 continue;
2079 case '\t':
2080 j = min( screen_buffer->width - screen_buffer->cursor_x, 8 - (screen_buffer->cursor_x % 8) );
2081 if (!j) j = 8;
2082 while (j--) write_char( screen_buffer, ' ', &update_rect, NULL );
2083 continue;
2084 case '\n':
2085 screen_buffer->cursor_x = 0;
2086 if (++screen_buffer->cursor_y == screen_buffer->height)
2087 new_line( screen_buffer, &update_rect );
2088 else if (screen_buffer->mode & ENABLE_WRAP_AT_EOL_OUTPUT)
2090 update_output( screen_buffer, &update_rect );
2091 set_tty_cursor( screen_buffer->console, screen_buffer->cursor_x, screen_buffer->cursor_y );
2093 continue;
2094 case '\a':
2095 FIXME( "beep\n" );
2096 continue;
2097 case '\r':
2098 screen_buffer->cursor_x = 0;
2099 continue;
2102 if (screen_buffer->cursor_x == screen_buffer->width && !(screen_buffer->mode & ENABLE_WRAP_AT_EOL_OUTPUT))
2103 screen_buffer->cursor_x = update_rect.left;
2104 write_char( screen_buffer, buffer[i], &update_rect, NULL );
2107 if (screen_buffer->cursor_x == screen_buffer->width)
2109 if (screen_buffer->mode & ENABLE_WRAP_AT_EOL_OUTPUT)
2111 if (!(screen_buffer->mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING))
2113 screen_buffer->cursor_x = 0;
2114 if (++screen_buffer->cursor_y == screen_buffer->height)
2115 new_line( screen_buffer, &update_rect );
2118 else screen_buffer->cursor_x = update_rect.left;
2121 scroll_to_cursor( screen_buffer );
2122 update_output( screen_buffer, &update_rect );
2123 tty_sync( screen_buffer->console );
2124 update_window_config( screen_buffer->console, TRUE );
2125 return STATUS_SUCCESS;
2128 static NTSTATUS write_output( struct screen_buffer *screen_buffer, const struct condrv_output_params *params,
2129 size_t in_size, size_t *out_size )
2131 unsigned int i, entry_size, entry_cnt, x, y;
2132 char_info_t *dest;
2133 char *src;
2135 enter_absolute_mode( screen_buffer->console );
2136 if (*out_size == sizeof(SMALL_RECT) && !params->width) return STATUS_INVALID_PARAMETER;
2138 entry_size = params->mode == CHAR_INFO_MODE_TEXTATTR ? sizeof(char_info_t) : sizeof(WCHAR);
2139 entry_cnt = (in_size - sizeof(*params)) / entry_size;
2141 TRACE( "(%u,%u) cnt %u\n", params->x, params->y, entry_cnt );
2143 if (params->x >= screen_buffer->width)
2145 *out_size = 0;
2146 return STATUS_SUCCESS;
2149 for (i = 0, src = (char *)(params + 1); i < entry_cnt; i++, src += entry_size)
2151 if (params->width)
2153 x = params->x + i % params->width;
2154 y = params->y + i / params->width;
2155 if (x >= screen_buffer->width) continue;
2157 else
2159 x = (params->x + i) % screen_buffer->width;
2160 y = params->y + (params->x + i) / screen_buffer->width;
2162 if (y >= screen_buffer->height) break;
2164 dest = &screen_buffer->data[y * screen_buffer->width + x];
2165 switch(params->mode)
2167 case CHAR_INFO_MODE_TEXT:
2168 dest->ch = *(const WCHAR *)src;
2169 break;
2170 case CHAR_INFO_MODE_ATTR:
2171 dest->attr = *(const unsigned short *)src;
2172 break;
2173 case CHAR_INFO_MODE_TEXTATTR:
2174 *dest = *(const char_info_t *)src;
2175 break;
2176 default:
2177 return STATUS_INVALID_PARAMETER;
2181 if (i && is_active( screen_buffer ))
2183 RECT update_rect;
2185 update_rect.left = params->x;
2186 update_rect.top = params->y;
2187 if (params->width)
2189 update_rect.bottom = min( params->y + entry_cnt / params->width, screen_buffer->height ) - 1;
2190 update_rect.right = min( params->x + params->width, screen_buffer->width ) - 1;
2192 else
2194 update_rect.bottom = params->y + (params->x + i - 1) / screen_buffer->width;
2195 if (update_rect.bottom != params->y)
2197 update_rect.left = 0;
2198 update_rect.right = screen_buffer->width - 1;
2200 else
2202 update_rect.right = params->x + i - 1;
2205 update_output( screen_buffer, &update_rect );
2206 tty_sync( screen_buffer->console );
2209 if (*out_size == sizeof(SMALL_RECT))
2211 SMALL_RECT *region;
2212 unsigned int width = params->width;
2213 x = params->x;
2214 y = params->y;
2215 if (!(region = alloc_ioctl_buffer( sizeof(*region )))) return STATUS_NO_MEMORY;
2216 region->Left = x;
2217 region->Top = y;
2218 region->Right = min( x + width, screen_buffer->width ) - 1;
2219 region->Bottom = min( y + entry_cnt / width, screen_buffer->height ) - 1;
2221 else
2223 DWORD *result;
2224 if (!(result = alloc_ioctl_buffer( sizeof(*result )))) return STATUS_NO_MEMORY;
2225 *result = i;
2228 return STATUS_SUCCESS;
2231 static NTSTATUS read_output( struct screen_buffer *screen_buffer, const struct condrv_output_params *params,
2232 size_t *out_size )
2234 enum char_info_mode mode;
2235 unsigned int x, y, width;
2236 unsigned int i, count;
2238 enter_absolute_mode( screen_buffer->console );
2239 x = params->x;
2240 y = params->y;
2241 mode = params->mode;
2242 width = params->width;
2243 TRACE( "(%u %u) mode %u width %u\n", x, y, mode, width );
2245 switch(mode)
2247 case CHAR_INFO_MODE_TEXT:
2249 WCHAR *data;
2250 char_info_t *src;
2251 if (x >= screen_buffer->width || y >= screen_buffer->height)
2253 *out_size = 0;
2254 return STATUS_SUCCESS;
2256 src = screen_buffer->data + y * screen_buffer->width + x;
2257 count = min( screen_buffer->data + screen_buffer->height * screen_buffer->width - src,
2258 *out_size / sizeof(*data) );
2259 *out_size = count * sizeof(*data);
2260 if (!(data = alloc_ioctl_buffer( *out_size ))) return STATUS_NO_MEMORY;
2261 for (i = 0; i < count; i++) data[i] = src[i].ch;
2263 break;
2264 case CHAR_INFO_MODE_ATTR:
2266 unsigned short *data;
2267 char_info_t *src;
2268 if (x >= screen_buffer->width || y >= screen_buffer->height)
2270 *out_size = 0;
2271 return STATUS_SUCCESS;
2273 src = screen_buffer->data + y * screen_buffer->width + x;
2274 count = min( screen_buffer->data + screen_buffer->height * screen_buffer->width - src,
2275 *out_size / sizeof(*data) );
2276 *out_size = count * sizeof(*data);
2277 if (!(data = alloc_ioctl_buffer( *out_size ))) return STATUS_NO_MEMORY;
2278 for (i = 0; i < count; i++) data[i] = src[i].attr;
2280 break;
2281 case CHAR_INFO_MODE_TEXTATTR:
2283 SMALL_RECT *region;
2284 char_info_t *data;
2285 if (!width || *out_size < sizeof(*region) || x >= screen_buffer->width || y >= screen_buffer->height)
2286 return STATUS_INVALID_PARAMETER;
2287 count = min( (*out_size - sizeof(*region)) / (width * sizeof(*data)), screen_buffer->height - y );
2288 width = min( width, screen_buffer->width - x );
2289 *out_size = sizeof(*region) + width * count * sizeof(*data);
2290 if (!(region = alloc_ioctl_buffer( *out_size ))) return STATUS_NO_MEMORY;
2291 region->Left = x;
2292 region->Top = y;
2293 region->Right = x + width - 1;
2294 region->Bottom = y + count - 1;
2295 data = (char_info_t *)(region + 1);
2296 for (i = 0; i < count; i++)
2298 memcpy( &data[i * width], &screen_buffer->data[(y + i) * screen_buffer->width + x],
2299 width * sizeof(*data) );
2302 break;
2303 default:
2304 return STATUS_INVALID_PARAMETER;
2307 return STATUS_SUCCESS;
2310 static NTSTATUS fill_output( struct screen_buffer *screen_buffer, const struct condrv_fill_output_params *params )
2312 char_info_t *end, *dest;
2313 DWORD i, count, *result;
2315 TRACE( "(%u %u) mode %u\n", params->x, params->y, params->mode );
2317 enter_absolute_mode( screen_buffer->console );
2318 if (params->y >= screen_buffer->height) return STATUS_SUCCESS;
2319 dest = screen_buffer->data + min( params->y * screen_buffer->width + params->x,
2320 screen_buffer->height * screen_buffer->width );
2322 end = screen_buffer->data + screen_buffer->height * screen_buffer->width;
2324 count = params->count;
2325 if (count > end - dest) count = end - dest;
2327 switch(params->mode)
2329 case CHAR_INFO_MODE_TEXT:
2330 for (i = 0; i < count; i++) dest[i].ch = params->ch;
2331 break;
2332 case CHAR_INFO_MODE_ATTR:
2333 for (i = 0; i < count; i++) dest[i].attr = params->attr;
2334 break;
2335 case CHAR_INFO_MODE_TEXTATTR:
2336 for (i = 0; i < count; i++)
2338 dest[i].ch = params->ch;
2339 dest[i].attr = params->attr;
2341 break;
2342 default:
2343 return STATUS_INVALID_PARAMETER;
2346 if (count && is_active(screen_buffer))
2348 RECT update_rect;
2349 SetRect( &update_rect,
2350 params->x % screen_buffer->width,
2351 params->y + params->x / screen_buffer->width,
2352 (params->x + i - 1) % screen_buffer->width,
2353 params->y + (params->x + i - 1) / screen_buffer->width );
2354 update_output( screen_buffer, &update_rect );
2355 tty_sync( screen_buffer->console );
2358 if (!(result = alloc_ioctl_buffer( sizeof(*result) ))) return STATUS_NO_MEMORY;
2359 *result = count;
2360 return STATUS_SUCCESS;
2363 static NTSTATUS scroll_output( struct screen_buffer *screen_buffer, const struct condrv_scroll_params *params )
2365 int x, y, xsrc, ysrc, w, h;
2366 char_info_t *psrc, *pdst;
2367 SMALL_RECT src, dst;
2368 RECT update_rect;
2369 SMALL_RECT clip;
2371 enter_absolute_mode( screen_buffer->console );
2372 xsrc = params->scroll.Left;
2373 ysrc = params->scroll.Top;
2374 w = params->scroll.Right - params->scroll.Left + 1;
2375 h = params->scroll.Bottom - params->scroll.Top + 1;
2377 TRACE( "(%d %d) -> (%u %u) w %u h %u\n", xsrc, ysrc, params->origin.X, params->origin.Y, w, h );
2379 clip.Left = max( params->clip.Left, 0 );
2380 clip.Top = max( params->clip.Top, 0 );
2381 clip.Right = min( params->clip.Right, screen_buffer->width - 1 );
2382 clip.Bottom = min( params->clip.Bottom, screen_buffer->height - 1 );
2383 if (clip.Left > clip.Right || clip.Top > clip.Bottom || params->scroll.Left < 0 || params->scroll.Top < 0 ||
2384 params->scroll.Right >= screen_buffer->width || params->scroll.Bottom >= screen_buffer->height ||
2385 params->scroll.Right < params->scroll.Left || params->scroll.Top > params->scroll.Bottom ||
2386 params->origin.X < 0 || params->origin.X >= screen_buffer->width || params->origin.Y < 0 ||
2387 params->origin.Y >= screen_buffer->height)
2388 return STATUS_INVALID_PARAMETER;
2390 src.Left = max( xsrc, clip.Left );
2391 src.Top = max( ysrc, clip.Top );
2392 src.Right = min( xsrc + w - 1, clip.Right );
2393 src.Bottom = min( ysrc + h - 1, clip.Bottom );
2395 dst.Left = params->origin.X;
2396 dst.Top = params->origin.Y;
2397 dst.Right = params->origin.X + w - 1;
2398 dst.Bottom = params->origin.Y + h - 1;
2400 if (dst.Left < clip.Left)
2402 xsrc += clip.Left - dst.Left;
2403 w -= clip.Left - dst.Left;
2404 dst.Left = clip.Left;
2406 if (dst.Top < clip.Top)
2408 ysrc += clip.Top - dst.Top;
2409 h -= clip.Top - dst.Top;
2410 dst.Top = clip.Top;
2412 if (dst.Right > clip.Right) w -= dst.Right - clip.Right;
2413 if (dst.Bottom > clip.Bottom) h -= dst.Bottom - clip.Bottom;
2415 if (w > 0 && h > 0)
2417 if (ysrc < dst.Top)
2419 psrc = &screen_buffer->data[(ysrc + h - 1) * screen_buffer->width + xsrc];
2420 pdst = &screen_buffer->data[(dst.Top + h - 1) * screen_buffer->width + dst.Left];
2422 for (y = h; y > 0; y--)
2424 memcpy( pdst, psrc, w * sizeof(*pdst) );
2425 pdst -= screen_buffer->width;
2426 psrc -= screen_buffer->width;
2429 else
2431 psrc = &screen_buffer->data[ysrc * screen_buffer->width + xsrc];
2432 pdst = &screen_buffer->data[dst.Top * screen_buffer->width + dst.Left];
2434 for (y = 0; y < h; y++)
2436 /* we use memmove here because when psrc and pdst are the same,
2437 * copies are done on the same row, so the dst and src blocks
2438 * can overlap */
2439 memmove( pdst, psrc, w * sizeof(*pdst) );
2440 pdst += screen_buffer->width;
2441 psrc += screen_buffer->width;
2446 for (y = src.Top; y <= src.Bottom; y++)
2448 int left = src.Left;
2449 int right = src.Right;
2450 if (dst.Top <= y && y <= dst.Bottom)
2452 if (dst.Left <= src.Left) left = max( left, dst.Right + 1 );
2453 if (dst.Left >= src.Left) right = min( right, dst.Left - 1 );
2455 for (x = left; x <= right; x++) screen_buffer->data[y * screen_buffer->width + x] = params->fill;
2458 SetRect( &update_rect, min( src.Left, dst.Left ), min( src.Top, dst.Top ),
2459 max( src.Right, dst.Right ), max( src.Bottom, dst.Bottom ));
2460 update_output( screen_buffer, &update_rect );
2461 tty_sync( screen_buffer->console );
2462 return STATUS_SUCCESS;
2465 static WCHAR *set_title( const WCHAR *in_title, size_t size )
2467 WCHAR *title = NULL;
2469 title = malloc( size + sizeof(WCHAR) );
2470 if (!title) return NULL;
2472 memcpy( title, in_title, size );
2473 title[ size / sizeof(WCHAR) ] = 0;
2475 return title;
2478 static NTSTATUS set_console_title( struct console *console, const WCHAR *in_title, size_t size )
2480 WCHAR *title = NULL;
2482 TRACE( "%s\n", debugstr_wn(in_title, size / sizeof(WCHAR)) );
2484 if (!(title = set_title( in_title, size )))
2485 return STATUS_NO_MEMORY;
2487 free( console->title );
2488 console->title = title;
2490 if (!console->title_orig && !(console->title_orig = set_title( in_title, size )))
2492 free( console->title );
2493 console->title = NULL;
2494 return STATUS_NO_MEMORY;
2497 if (console->tty_output)
2499 size_t len;
2500 char *vt;
2502 tty_write( console, "\x1b]0;", 4 );
2503 len = WideCharToMultiByte( get_tty_cp( console ), 0, console->title, size / sizeof(WCHAR),
2504 NULL, 0, NULL, NULL);
2505 if ((vt = tty_alloc_buffer( console, len )))
2506 WideCharToMultiByte( get_tty_cp( console ), 0, console->title, size / sizeof(WCHAR),
2507 vt, len, NULL, NULL );
2508 tty_write( console, "\x07", 1 );
2509 tty_sync( console );
2511 if (console->win)
2512 SetWindowTextW( console->win, console->title );
2513 return STATUS_SUCCESS;
2516 static NTSTATUS screen_buffer_ioctl( struct screen_buffer *screen_buffer, unsigned int code,
2517 const void *in_data, size_t in_size, size_t *out_size )
2519 switch (code)
2521 case IOCTL_CONDRV_CLOSE_OUTPUT:
2522 if (in_size || *out_size) return STATUS_INVALID_PARAMETER;
2523 destroy_screen_buffer( screen_buffer );
2524 return STATUS_SUCCESS;
2526 case IOCTL_CONDRV_ACTIVATE:
2527 if (in_size || *out_size) return STATUS_INVALID_PARAMETER;
2528 return screen_buffer_activate( screen_buffer );
2530 case IOCTL_CONDRV_GET_MODE:
2532 DWORD *mode;
2533 TRACE( "returning mode %x\n", screen_buffer->mode );
2534 if (in_size || *out_size != sizeof(*mode)) return STATUS_INVALID_PARAMETER;
2535 if (!(mode = alloc_ioctl_buffer( *out_size ))) return STATUS_NO_MEMORY;
2536 *mode = screen_buffer->mode;
2537 return STATUS_SUCCESS;
2540 case IOCTL_CONDRV_SET_MODE:
2541 if (in_size != sizeof(unsigned int) || *out_size) return STATUS_INVALID_PARAMETER;
2542 screen_buffer->mode = *(unsigned int *)in_data;
2543 TRACE( "set %x mode\n", screen_buffer->mode );
2544 return STATUS_SUCCESS;
2546 case IOCTL_CONDRV_IS_UNIX:
2547 return screen_buffer->console->is_unix ? STATUS_SUCCESS : STATUS_NOT_SUPPORTED;
2549 case IOCTL_CONDRV_WRITE_CONSOLE:
2550 if (in_size % sizeof(WCHAR) || *out_size) return STATUS_INVALID_PARAMETER;
2551 return write_console( screen_buffer, in_data, in_size / sizeof(WCHAR) );
2553 case IOCTL_CONDRV_WRITE_FILE:
2555 unsigned int len;
2556 WCHAR *buf;
2557 NTSTATUS status;
2559 len = MultiByteToWideChar( screen_buffer->console->output_cp, 0, in_data, in_size,
2560 NULL, 0 );
2561 if (!len) return STATUS_SUCCESS;
2562 if (!(buf = malloc( len * sizeof(WCHAR) ))) return STATUS_NO_MEMORY;
2563 MultiByteToWideChar( screen_buffer->console->output_cp, 0, in_data, in_size, buf, len );
2564 status = write_console( screen_buffer, buf, len );
2565 free( buf );
2566 return status;
2569 case IOCTL_CONDRV_WRITE_OUTPUT:
2570 if ((*out_size != sizeof(DWORD) && *out_size != sizeof(SMALL_RECT)) ||
2571 in_size < sizeof(struct condrv_output_params))
2572 return STATUS_INVALID_PARAMETER;
2573 return write_output( screen_buffer, in_data, in_size, out_size );
2575 case IOCTL_CONDRV_READ_OUTPUT:
2576 if (in_size != sizeof(struct condrv_output_params)) return STATUS_INVALID_PARAMETER;
2577 return read_output( screen_buffer, in_data, out_size );
2579 case IOCTL_CONDRV_GET_OUTPUT_INFO:
2580 if (in_size || *out_size < sizeof(struct condrv_output_info)) return STATUS_INVALID_PARAMETER;
2581 return get_output_info( screen_buffer, out_size );
2583 case IOCTL_CONDRV_SET_OUTPUT_INFO:
2584 if (in_size < sizeof(struct condrv_output_info_params) || *out_size)
2585 return STATUS_INVALID_PARAMETER;
2586 return set_output_info( screen_buffer, in_data, in_size );
2588 case IOCTL_CONDRV_FILL_OUTPUT:
2589 if (in_size != sizeof(struct condrv_fill_output_params) || *out_size != sizeof(DWORD))
2590 return STATUS_INVALID_PARAMETER;
2591 return fill_output( screen_buffer, in_data );
2593 case IOCTL_CONDRV_SCROLL:
2594 if (in_size != sizeof(struct condrv_scroll_params) || *out_size)
2595 return STATUS_INVALID_PARAMETER;
2596 return scroll_output( screen_buffer, in_data );
2598 default:
2599 WARN( "invalid ioctl %x\n", code );
2600 return STATUS_INVALID_HANDLE;
2604 static NTSTATUS console_input_ioctl( struct console *console, unsigned int code, const void *in_data,
2605 size_t in_size, size_t *out_size )
2607 NTSTATUS status;
2609 switch (code)
2611 case IOCTL_CONDRV_GET_MODE:
2613 DWORD *mode;
2614 TRACE( "returning mode %x\n", console->mode );
2615 if (in_size || *out_size != sizeof(*mode)) return STATUS_INVALID_PARAMETER;
2616 if (!(mode = alloc_ioctl_buffer( *out_size ))) return STATUS_NO_MEMORY;
2617 *mode = console->mode;
2618 return STATUS_SUCCESS;
2621 case IOCTL_CONDRV_SET_MODE:
2622 if (in_size != sizeof(unsigned int) || *out_size) return STATUS_INVALID_PARAMETER;
2623 console->mode = *(unsigned int *)in_data;
2624 TRACE( "set %x mode\n", console->mode );
2625 return STATUS_SUCCESS;
2627 case IOCTL_CONDRV_IS_UNIX:
2628 return console->is_unix ? STATUS_SUCCESS : STATUS_NOT_SUPPORTED;
2630 case IOCTL_CONDRV_READ_CONSOLE:
2631 if (in_size || *out_size % sizeof(WCHAR)) return STATUS_INVALID_PARAMETER;
2632 ensure_tty_input_thread( console );
2633 status = read_console( console, code, *out_size, NULL, 0, 0 );
2634 *out_size = 0;
2635 return status;
2637 case IOCTL_CONDRV_READ_CONSOLE_CONTROL:
2638 if ((in_size < sizeof(DWORD)) || ((in_size - sizeof(DWORD)) % sizeof(WCHAR)) ||
2639 (*out_size < sizeof(DWORD)) || ((*out_size - sizeof(DWORD)) % sizeof(WCHAR)))
2640 return STATUS_INVALID_PARAMETER;
2641 ensure_tty_input_thread( console );
2642 status = read_console( console, code, *out_size - sizeof(DWORD),
2643 (const WCHAR*)((const char*)in_data + sizeof(DWORD)),
2644 (in_size - sizeof(DWORD)) / sizeof(WCHAR),
2645 *(DWORD*)in_data );
2646 *out_size = 0;
2647 return status;
2649 case IOCTL_CONDRV_READ_FILE:
2650 ensure_tty_input_thread( console );
2651 status = read_console( console, code, *out_size, NULL, 0, 0 );
2652 *out_size = 0;
2653 return status;
2655 case IOCTL_CONDRV_READ_INPUT:
2657 if (in_size) return STATUS_INVALID_PARAMETER;
2658 ensure_tty_input_thread( console );
2659 if (!console->record_count && *out_size)
2661 TRACE( "pending read\n" );
2662 console->read_ioctl = IOCTL_CONDRV_READ_INPUT;
2663 console->pending_read = *out_size;
2664 return STATUS_PENDING;
2666 status = read_console_input( console, *out_size );
2667 *out_size = 0;
2668 return status;
2671 case IOCTL_CONDRV_WRITE_INPUT:
2672 if (in_size % sizeof(INPUT_RECORD) || *out_size) return STATUS_INVALID_PARAMETER;
2673 return write_console_input( console, in_data, in_size / sizeof(INPUT_RECORD), TRUE );
2675 case IOCTL_CONDRV_PEEK:
2677 void *result;
2678 TRACE( "peek\n" );
2679 if (in_size) return STATUS_INVALID_PARAMETER;
2680 ensure_tty_input_thread( console );
2681 *out_size = min( *out_size, console->record_count * sizeof(INPUT_RECORD) );
2682 if (!(result = alloc_ioctl_buffer( *out_size ))) return STATUS_NO_MEMORY;
2683 if (*out_size) memcpy( result, console->records, *out_size );
2684 return STATUS_SUCCESS;
2687 case IOCTL_CONDRV_GET_INPUT_INFO:
2689 struct condrv_input_info *info;
2690 TRACE( "get info\n" );
2691 if (in_size || *out_size != sizeof(*info)) return STATUS_INVALID_PARAMETER;
2692 if (!(info = alloc_ioctl_buffer( sizeof(*info )))) return STATUS_NO_MEMORY;
2693 info->input_cp = console->input_cp;
2694 info->output_cp = console->output_cp;
2695 info->input_count = console->record_count;
2696 return STATUS_SUCCESS;
2699 case IOCTL_CONDRV_GET_WINDOW:
2701 condrv_handle_t *result;
2702 TRACE( "get window\n" );
2703 if (in_size || *out_size != sizeof(*result)) return STATUS_INVALID_PARAMETER;
2704 if (!(result = alloc_ioctl_buffer( sizeof(*result )))) return STATUS_NO_MEMORY;
2705 if (!console->win && !console->no_window) init_message_window( console );
2706 *result = condrv_handle( console->win );
2707 return STATUS_SUCCESS;
2710 case IOCTL_CONDRV_SET_INPUT_INFO:
2712 const struct condrv_input_info_params *params = in_data;
2713 TRACE( "set info\n" );
2714 if (in_size != sizeof(*params) || *out_size) return STATUS_INVALID_PARAMETER;
2715 if (params->mask & SET_CONSOLE_INPUT_INFO_INPUT_CODEPAGE)
2717 if (!IsValidCodePage( params->info.input_cp )) return STATUS_INVALID_PARAMETER;
2718 console->input_cp = params->info.input_cp;
2720 if (params->mask & SET_CONSOLE_INPUT_INFO_OUTPUT_CODEPAGE)
2722 if (!IsValidCodePage( params->info.output_cp )) return STATUS_INVALID_PARAMETER;
2723 console->output_cp = params->info.output_cp;
2725 return STATUS_SUCCESS;
2728 case IOCTL_CONDRV_GET_TITLE:
2730 BOOL current_title;
2731 WCHAR *title;
2732 size_t title_len, str_size;
2733 struct condrv_title_params *params;
2734 if (in_size != sizeof(BOOL)) return STATUS_INVALID_PARAMETER;
2735 current_title = *(BOOL *)in_data;
2736 title = current_title ? console->title : console->title_orig;
2737 title_len = title ? wcslen( title ) : 0;
2738 str_size = min( *out_size - sizeof(*params), title_len * sizeof(WCHAR) );
2739 *out_size = sizeof(*params) + str_size;
2740 if (!(params = alloc_ioctl_buffer( *out_size ))) return STATUS_NO_MEMORY;
2741 TRACE( "returning %s %s\n", current_title ? "title" : "original title", debugstr_w(title) );
2742 if (str_size) memcpy( params->buffer, title, str_size );
2743 params->title_len = title_len;
2744 return STATUS_SUCCESS;
2747 case IOCTL_CONDRV_SET_TITLE:
2748 if (in_size % sizeof(WCHAR) || *out_size) return STATUS_INVALID_PARAMETER;
2749 return set_console_title( console, in_data, in_size );
2751 case IOCTL_CONDRV_BEEP:
2752 if (in_size || *out_size) return STATUS_INVALID_PARAMETER;
2753 if (console->is_unix)
2755 tty_write( console, "\a", 1 );
2756 tty_sync( console );
2758 return STATUS_SUCCESS;
2760 case IOCTL_CONDRV_FLUSH:
2761 if (in_size || *out_size) return STATUS_INVALID_PARAMETER;
2762 TRACE( "flush\n" );
2763 console->record_count = 0;
2764 return STATUS_SUCCESS;
2766 default:
2767 WARN( "unsupported ioctl %x\n", code );
2768 return STATUS_INVALID_HANDLE;
2772 static NTSTATUS process_console_ioctls( struct console *console )
2774 size_t out_size = 0, in_size;
2775 unsigned int code;
2776 int output;
2777 NTSTATUS status = STATUS_SUCCESS;
2779 for (;;)
2781 if (status) out_size = 0;
2783 console->signaled = console->record_count != 0;
2784 SERVER_START_REQ( get_next_console_request )
2786 req->handle = wine_server_obj_handle( console->server );
2787 req->status = status;
2788 req->signal = console->signaled;
2789 wine_server_add_data( req, ioctl_buffer, out_size );
2790 wine_server_set_reply( req, ioctl_buffer, ioctl_buffer_size );
2791 status = wine_server_call( req );
2792 code = reply->code;
2793 output = reply->output;
2794 out_size = reply->out_size;
2795 in_size = wine_server_reply_size( reply );
2797 SERVER_END_REQ;
2799 if (status == STATUS_PENDING) return STATUS_SUCCESS;
2800 if (status == STATUS_BUFFER_OVERFLOW)
2802 if (!alloc_ioctl_buffer( out_size )) return STATUS_NO_MEMORY;
2803 status = STATUS_SUCCESS;
2804 continue;
2806 if (status)
2808 TRACE( "failed to get next request: %#lx\n", status );
2809 return status;
2812 if (code == IOCTL_CONDRV_INIT_OUTPUT)
2814 TRACE( "initializing output %x\n", output );
2815 enter_absolute_mode( console );
2816 if (console->active)
2817 create_screen_buffer( console, output, console->active->width, console->active->height );
2818 else
2819 create_screen_buffer( console, output, 80, 150 );
2821 else if (!output)
2823 status = console_input_ioctl( console, code, ioctl_buffer, in_size, &out_size );
2825 else
2827 struct wine_rb_entry *entry;
2828 if (!(entry = wine_rb_get( &screen_buffer_map, LongToPtr(output) )))
2830 ERR( "invalid screen buffer id %x\n", output );
2831 status = STATUS_INVALID_HANDLE;
2833 else
2835 status = screen_buffer_ioctl( WINE_RB_ENTRY_VALUE( entry, struct screen_buffer, entry ), code,
2836 ioctl_buffer, in_size, &out_size );
2842 static int main_loop( struct console *console, HANDLE signal )
2844 HANDLE signal_event = NULL;
2845 HANDLE wait_handles[3];
2846 unsigned int wait_cnt = 0;
2847 unsigned short signal_id;
2848 IO_STATUS_BLOCK signal_io;
2849 NTSTATUS status;
2850 DWORD res;
2852 if (signal)
2854 if (!(signal_event = CreateEventW( NULL, TRUE, FALSE, NULL ))) return 1;
2855 status = NtReadFile( signal, signal_event, NULL, NULL, &signal_io, &signal_id,
2856 sizeof(signal_id), NULL, NULL );
2857 if (status && status != STATUS_PENDING) return 1;
2860 if (!alloc_ioctl_buffer( 4096 )) return 1;
2862 wait_handles[wait_cnt++] = console->server;
2863 if (signal) wait_handles[wait_cnt++] = signal_event;
2864 if (console->input_thread) wait_handles[wait_cnt++] = console->input_thread;
2866 for (;;)
2868 if (console->win)
2869 res = MsgWaitForMultipleObjects( wait_cnt, wait_handles, FALSE, INFINITE, QS_ALLINPUT );
2870 else
2871 res = WaitForMultipleObjects( wait_cnt, wait_handles, FALSE, INFINITE );
2873 if (res == WAIT_OBJECT_0 + wait_cnt)
2875 MSG msg;
2876 while (PeekMessageW( &msg, 0, 0, 0, PM_REMOVE ))
2878 if (msg.message == WM_QUIT) return 0;
2879 DispatchMessageW(&msg);
2881 continue;
2884 switch (res)
2886 case WAIT_OBJECT_0:
2887 EnterCriticalSection( &console_section );
2888 status = process_console_ioctls( console );
2889 LeaveCriticalSection( &console_section );
2890 if (status) return 0;
2891 break;
2893 case WAIT_OBJECT_0 + 1:
2894 if (signal_io.Status || signal_io.Information != sizeof(signal_id))
2896 TRACE( "signaled quit\n" );
2897 return 0;
2899 FIXME( "unimplemented signal %x\n", signal_id );
2900 status = NtReadFile( signal, signal_event, NULL, NULL, &signal_io, &signal_id,
2901 sizeof(signal_id), NULL, NULL );
2902 if (status && status != STATUS_PENDING) return 1;
2903 break;
2905 default:
2906 TRACE( "wait failed, quit\n");
2907 return 0;
2911 return 0;
2914 static void teardown( struct console *console )
2916 if (console->is_unix)
2918 set_tty_attr( console, empty_char_info.attr );
2919 tty_flush( console );
2923 int __cdecl wmain(int argc, WCHAR *argv[])
2925 int headless = 0, i, width = 0, height = 0, ret;
2926 HANDLE signal = NULL;
2927 WCHAR *end;
2929 static struct console console;
2931 for (i = 0; i < argc; i++) TRACE("%s ", wine_dbgstr_w(argv[i]));
2932 TRACE("\n");
2934 console.mode = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT |
2935 ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT | ENABLE_INSERT_MODE |
2936 ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS | ENABLE_AUTO_POSITION;
2937 console.input_cp = console.output_cp = GetOEMCP();
2938 console.history_size = 50;
2939 if (!(console.history = calloc( console.history_size, sizeof(*console.history) ))) return 1;
2941 for (i = 1; i < argc; i++)
2943 if (!wcscmp( argv[i], L"--headless"))
2945 headless = 1;
2946 continue;
2948 if (!wcscmp( argv[i], L"--unix"))
2950 console.is_unix = 1;
2951 console.use_relative_cursor = 1;
2952 headless = 1;
2953 continue;
2955 if (!wcscmp( argv[i], L"--width" ))
2957 if (++i == argc) return 1;
2958 width = wcstol( argv[i], &end, 0 );
2959 if ((!width && !console.is_unix) || width > 0xffff || *end) return 1;
2960 continue;
2962 if (!wcscmp( argv[i], L"--height" ))
2964 if (++i == argc) return 1;
2965 height = wcstol( argv[i], &end, 0 );
2966 if ((!height && !console.is_unix) || height > 0xffff || *end) return 1;
2967 continue;
2969 if (!wcscmp( argv[i], L"--signal" ))
2971 if (++i == argc) return 1;
2972 signal = ULongToHandle( wcstol( argv[i], &end, 0 ));
2973 if (*end) return 1;
2974 continue;
2976 if (!wcscmp( argv[i], L"--server" ))
2978 if (++i == argc) return 1;
2979 console.server = ULongToHandle( wcstol( argv[i], &end, 0 ));
2980 if (*end) return 1;
2981 continue;
2983 FIXME( "unknown option %s\n", debugstr_w(argv[i]) );
2984 return 1;
2987 if (!console.server)
2989 ERR( "no server handle\n" );
2990 return 1;
2993 if (!width) width = 80;
2994 if (!height) height = 150;
2996 if (!(console.active = create_screen_buffer( &console, 1, width, height ))) return 1;
2997 if (headless)
2999 console.tty_input = GetStdHandle( STD_INPUT_HANDLE );
3000 console.tty_output = GetStdHandle( STD_OUTPUT_HANDLE );
3002 if (console.tty_input || console.tty_output)
3004 init_tty_output( &console );
3005 if (!console.is_unix && !ensure_tty_input_thread( &console )) return 1;
3007 else console.no_window = TRUE;
3009 else
3011 STARTUPINFOW si;
3012 if (!init_window( &console )) return 1;
3013 GetStartupInfoW( &si );
3014 set_console_title( &console, si.lpTitle, wcslen( si.lpTitle ) * sizeof(WCHAR) );
3015 ShowWindow( console.win, (si.dwFlags & STARTF_USESHOWWINDOW) ? si.wShowWindow : SW_SHOW );
3018 ret = main_loop( &console, signal );
3019 teardown( &console );
3021 return ret;