conhost: Use more standard hide cursor sequence.
[wine.git] / programs / conhost / conhost.c
blob84a1c8ac7df8bd2d89bc3f155bb8ea0c99389284
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 <stdarg.h>
23 #include <stdlib.h>
24 #include <assert.h>
26 #include <ntstatus.h>
27 #define WIN32_NO_STATUS
28 #include <windef.h>
29 #include <winbase.h>
30 #include <winuser.h>
31 #include <winnls.h>
32 #include <winternl.h>
34 #include "wine/condrv.h"
35 #include "wine/server.h"
36 #include "wine/rbtree.h"
37 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(conhost);
41 struct history_line
43 size_t len;
44 WCHAR text[1];
47 struct font_info
49 short int width;
50 short int height;
51 short int weight;
52 short int pitch_family;
53 WCHAR *face_name;
54 size_t face_len;
57 struct console
59 HANDLE server; /* console server handle */
60 unsigned int mode; /* input mode */
61 struct screen_buffer *active; /* active screen buffer */
62 INPUT_RECORD *records; /* input records */
63 unsigned int record_count; /* number of input records */
64 unsigned int record_size; /* size of input records buffer */
65 size_t pending_read; /* size of pending read buffer */
66 WCHAR *title; /* console title */
67 size_t title_len; /* length of console title */
68 struct history_line **history; /* lines history */
69 unsigned int history_size; /* number of entries in history array */
70 unsigned int history_index; /* number of used entries in history array */
71 unsigned int history_mode; /* mode of history (non zero means remove doubled strings */
72 unsigned int edition_mode; /* index to edition mode flavors */
73 unsigned int input_cp; /* console input codepage */
74 unsigned int output_cp; /* console output codepage */
75 unsigned int win; /* window handle if backend supports it */
76 HANDLE tty_input; /* handle to tty input stream */
77 HANDLE tty_output; /* handle to tty output stream */
78 char tty_buffer[4096]; /* tty output buffer */
79 size_t tty_buffer_count; /* tty buffer size */
80 unsigned int tty_cursor_x; /* tty cursor position */
81 unsigned int tty_cursor_y;
82 unsigned int tty_attr; /* current tty char attributes */
83 int tty_cursor_visible; /* tty cursor visibility flag */
86 struct screen_buffer
88 struct console *console; /* console reference */
89 unsigned int id; /* screen buffer id */
90 unsigned int mode; /* output mode */
91 unsigned int width; /* size (w-h) of the screen buffer */
92 unsigned int height;
93 unsigned int cursor_size; /* size of cursor (percentage filled) */
94 unsigned int cursor_visible;/* cursor visibility flag */
95 unsigned int cursor_x; /* position of cursor */
96 unsigned int cursor_y; /* position of cursor */
97 unsigned short attr; /* default fill attributes (screen colors) */
98 unsigned short popup_attr; /* pop-up color attributes */
99 unsigned int max_width; /* size (w-h) of the window given font size */
100 unsigned int max_height;
101 char_info_t *data; /* the data for each cell - a width x height matrix */
102 unsigned int color_map[16]; /* color table */
103 RECT win; /* current visible window on the screen buffer */
104 struct font_info font; /* console font information */
105 struct wine_rb_entry entry; /* map entry */
108 static const char_info_t empty_char_info = { ' ', 0x0007 }; /* white on black space */
110 static CRITICAL_SECTION console_section;
111 static CRITICAL_SECTION_DEBUG critsect_debug =
113 0, 0, &console_section,
114 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
115 0, 0, { (DWORD_PTR)(__FILE__ ": console_section") }
117 static CRITICAL_SECTION console_section = { &critsect_debug, -1, 0, 0, 0, 0 };
119 static void *ioctl_buffer;
120 static size_t ioctl_buffer_size;
122 static void *alloc_ioctl_buffer( size_t size )
124 if (size > ioctl_buffer_size)
126 void *new_buffer;
127 if (!(new_buffer = realloc( ioctl_buffer, size ))) return NULL;
128 ioctl_buffer = new_buffer;
129 ioctl_buffer_size = size;
131 return ioctl_buffer;
134 static int screen_buffer_compare_id( const void *key, const struct wine_rb_entry *entry )
136 struct screen_buffer *screen_buffer = WINE_RB_ENTRY_VALUE( entry, struct screen_buffer, entry );
137 return PtrToLong(key) - screen_buffer->id;
140 static struct wine_rb_tree screen_buffer_map = { screen_buffer_compare_id };
142 static struct screen_buffer *create_screen_buffer( struct console *console, int id, int width, int height )
144 struct screen_buffer *screen_buffer;
145 unsigned int i;
147 if (!(screen_buffer = malloc( sizeof(*screen_buffer) ))) return NULL;
148 screen_buffer->console = console;
149 screen_buffer->id = id;
150 screen_buffer->mode = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT;
151 screen_buffer->cursor_size = 100;
152 screen_buffer->cursor_visible = 1;
153 screen_buffer->cursor_x = 0;
154 screen_buffer->cursor_y = 0;
155 screen_buffer->width = width;
156 screen_buffer->height = height;
157 screen_buffer->attr = 0x07;
158 screen_buffer->popup_attr = 0xf5;
159 screen_buffer->max_width = 80;
160 screen_buffer->max_height = 25;
161 screen_buffer->win.left = 0;
162 screen_buffer->win.right = screen_buffer->max_width - 1;
163 screen_buffer->win.top = 0;
164 screen_buffer->win.bottom = screen_buffer->max_height - 1;
165 screen_buffer->font.width = 0;
166 screen_buffer->font.height = 0;
167 screen_buffer->font.weight = FW_NORMAL;
168 screen_buffer->font.pitch_family = FIXED_PITCH | FF_DONTCARE;
169 screen_buffer->font.face_name = NULL;
170 screen_buffer->font.face_len = 0;
171 memset( screen_buffer->color_map, 0, sizeof(screen_buffer->color_map) );
173 if (!(screen_buffer->data = malloc( screen_buffer->width * screen_buffer->height *
174 sizeof(*screen_buffer->data) )))
176 free( screen_buffer );
177 return NULL;
180 /* clear the first row */
181 for (i = 0; i < screen_buffer->width; i++) screen_buffer->data[i] = empty_char_info;
182 /* and copy it to all other rows */
183 for (i = 1; i < screen_buffer->height; i++)
184 memcpy( &screen_buffer->data[i * screen_buffer->width], screen_buffer->data,
185 screen_buffer->width * sizeof(char_info_t) );
187 if (wine_rb_put( &screen_buffer_map, LongToPtr(id), &screen_buffer->entry ))
189 ERR( "id %x already exists\n", id );
190 return NULL;
193 return screen_buffer;
196 static void destroy_screen_buffer( struct screen_buffer *screen_buffer )
198 if (screen_buffer->console->active == screen_buffer)
199 screen_buffer->console->active = NULL;
200 wine_rb_remove( &screen_buffer_map, &screen_buffer->entry );
201 free( screen_buffer );
204 static BOOL is_active( struct screen_buffer *screen_buffer )
206 return screen_buffer == screen_buffer->console->active;
209 static void tty_flush( struct console *console )
211 if (!console->tty_output || !console->tty_buffer_count) return;
212 TRACE("%s\n", debugstr_an(console->tty_buffer, console->tty_buffer_count));
213 if (!WriteFile( console->tty_output, console->tty_buffer, console->tty_buffer_count,
214 NULL, NULL ))
215 WARN( "write failed: %u\n", GetLastError() );
216 console->tty_buffer_count = 0;
219 static void tty_write( struct console *console, const char *buffer, size_t size )
221 if (!size || !console->tty_output) return;
222 if (console->tty_buffer_count + size > sizeof(console->tty_buffer))
223 tty_flush( console );
224 if (console->tty_buffer_count + size <= sizeof(console->tty_buffer))
226 memcpy( console->tty_buffer + console->tty_buffer_count, buffer, size );
227 console->tty_buffer_count += size;
229 else
231 assert( !console->tty_buffer_count );
232 if (!WriteFile( console->tty_output, buffer, size, NULL, NULL ))
233 WARN( "write failed: %u\n", GetLastError() );
237 static void *tty_alloc_buffer( struct console *console, size_t size )
239 void *ret;
240 if (console->tty_buffer_count + size > sizeof(console->tty_buffer)) return NULL;
241 ret = console->tty_buffer + console->tty_buffer_count;
242 console->tty_buffer_count += size;
243 return ret;
246 static void hide_tty_cursor( struct console *console )
248 if (console->tty_cursor_visible)
250 tty_write( console, "\x1b[?25l", 6 );
251 console->tty_cursor_visible = FALSE;
255 static void set_tty_cursor( struct console *console, unsigned int x, unsigned int y )
257 char buf[64];
259 if (console->tty_cursor_x == x && console->tty_cursor_y == y) return;
261 if (!x && y == console->tty_cursor_y + 1) strcpy( buf, "\r\n" );
262 else if (!x && y == console->tty_cursor_y) strcpy( buf, "\r" );
263 else if (y == console->tty_cursor_y)
265 if (x + 1 == console->tty_cursor_x) strcpy( buf, "\b" );
266 else if (x > console->tty_cursor_x) sprintf( buf, "\x1b[%uC", x - console->tty_cursor_x );
267 else sprintf( buf, "\x1b[%uD", console->tty_cursor_x - x );
269 else if (x || y)
271 hide_tty_cursor( console );
272 sprintf( buf, "\x1b[%u;%uH", y + 1, x + 1);
274 else strcpy( buf, "\x1b[H" );
275 console->tty_cursor_x = x;
276 console->tty_cursor_y = y;
277 tty_write( console, buf, strlen(buf) );
280 static void set_tty_attr( struct console *console, unsigned int attr )
282 char buf[8];
284 if ((attr & 0x0f) != (console->tty_attr & 0x0f))
286 if ((attr & 0x0f) != 7)
288 unsigned int n = 30;
289 if (attr & FOREGROUND_BLUE) n += 4;
290 if (attr & FOREGROUND_GREEN) n += 2;
291 if (attr & FOREGROUND_RED) n += 1;
292 if (attr & FOREGROUND_INTENSITY) n += 60;
293 sprintf(buf, "\x1b[%um", n);
294 tty_write( console, buf, strlen(buf) );
296 else tty_write( console, "\x1b[m", 3 );
299 if ((attr & 0xf0) != (console->tty_attr & 0xf0) && attr != 7)
301 unsigned int n = 40;
302 if (attr & BACKGROUND_BLUE) n += 4;
303 if (attr & BACKGROUND_GREEN) n += 2;
304 if (attr & BACKGROUND_RED) n += 1;
305 if (attr & BACKGROUND_INTENSITY) n += 60;
306 sprintf(buf, "\x1b[%um", n);
307 tty_write( console, buf, strlen(buf) );
310 console->tty_attr = attr;
313 static void tty_sync( struct console *console )
315 if (!console->tty_output) return;
317 if (console->active->cursor_visible)
319 set_tty_cursor( console, console->active->cursor_x, console->active->cursor_y );
320 if (!console->tty_cursor_visible)
322 tty_write( console, "\x1b[?25h", 6 ); /* show cursor */
323 console->tty_cursor_visible = TRUE;
326 else if (console->tty_cursor_visible)
327 hide_tty_cursor( console );
328 tty_flush( console );
331 static void init_tty_output( struct console *console )
333 /* initialize tty output, but don't flush */
334 tty_write( console, "\x1b[2J", 4 ); /* clear screen */
335 set_tty_attr( console, console->active->attr );
336 tty_write( console, "\x1b[H", 3 ); /* move cursor to (0,0) */
337 console->tty_cursor_visible = TRUE;
340 static void update_output( struct screen_buffer *screen_buffer, const RECT *rect )
342 int x, y, size, trailing_spaces;
343 char_info_t *ch;
344 char buf[8];
346 if (!is_active( screen_buffer ) || !screen_buffer->console->tty_output) return;
347 TRACE( "%s\n", wine_dbgstr_rect( rect ));
349 hide_tty_cursor( screen_buffer->console );
351 for (y = rect->top; y <= rect->bottom; y++)
353 for (trailing_spaces = 0; trailing_spaces < screen_buffer->width; trailing_spaces++)
355 ch = &screen_buffer->data[(y + 1) * screen_buffer->width - trailing_spaces - 1];
356 if (ch->ch != ' ' || ch->attr != 7) break;
358 if (trailing_spaces < 4) trailing_spaces = 0;
360 for (x = rect->left; x <= rect->right; x++)
362 ch = &screen_buffer->data[y * screen_buffer->width + x];
363 set_tty_attr( screen_buffer->console, ch->attr );
364 set_tty_cursor( screen_buffer->console, x, y );
366 if (x + trailing_spaces >= screen_buffer->width)
368 tty_write( screen_buffer->console, "\x1b[K", 3 );
369 break;
372 size = WideCharToMultiByte( CP_UTF8, 0, &ch->ch, 1, buf, sizeof(buf), NULL, NULL );
373 tty_write( screen_buffer->console, buf, size );
374 screen_buffer->console->tty_cursor_x++;
379 static NTSTATUS read_console_input( struct console *console, size_t out_size )
381 size_t count = min( out_size / sizeof(INPUT_RECORD), console->record_count );
382 NTSTATUS status;
384 TRACE("count %u\n", count);
386 SERVER_START_REQ( get_next_console_request )
388 req->handle = wine_server_obj_handle( console->server );
389 req->signal = count < console->record_count;
390 req->read = 1;
391 req->status = STATUS_SUCCESS;
392 wine_server_add_data( req, console->records, count * sizeof(*console->records) );
393 status = wine_server_call( req );
395 SERVER_END_REQ;
396 if (status)
398 ERR( "failed: %#x\n", status );
399 return status;
402 if (count < console->record_count)
403 memmove( console->records, console->records + count,
404 (console->record_count - count) * sizeof(*console->records) );
405 console->record_count -= count;
406 return STATUS_SUCCESS;
409 static NTSTATUS process_console_input( struct console *console )
411 if (console->record_count && console->pending_read)
413 read_console_input( console, console->pending_read );
414 console->pending_read = 0;
416 return STATUS_SUCCESS;
419 /* add input events to a console input queue */
420 static NTSTATUS write_console_input( struct console *console, const INPUT_RECORD *records,
421 unsigned int count, BOOL flush )
423 TRACE( "%u\n", count );
425 if (!count) return STATUS_SUCCESS;
426 if (console->record_count + count > console->record_size)
428 INPUT_RECORD *new_rec;
429 if (!(new_rec = realloc( console->records, (console->record_size * 2 + count) * sizeof(INPUT_RECORD) )))
430 return STATUS_NO_MEMORY;
431 console->records = new_rec;
432 console->record_size = console->record_size * 2 + count;
434 memcpy( console->records + console->record_count, records, count * sizeof(INPUT_RECORD) );
436 if (console->mode & ENABLE_PROCESSED_INPUT)
438 unsigned int i = 0;
439 while (i < count)
441 if (records[i].EventType == KEY_EVENT &&
442 records[i].Event.KeyEvent.uChar.UnicodeChar == 'C' - 64 &&
443 !(records[i].Event.KeyEvent.dwControlKeyState & ENHANCED_KEY))
445 if (i != count - 1)
446 memcpy( &console->records[console->record_count + i],
447 &console->records[console->record_count + i + 1],
448 (count - i - 1) * sizeof(INPUT_RECORD) );
449 count--;
450 if (records[i].Event.KeyEvent.bKeyDown)
452 struct condrv_ctrl_event ctrl_event;
453 IO_STATUS_BLOCK io;
455 ctrl_event.event = CTRL_C_EVENT;
456 ctrl_event.group_id = 0;
457 NtDeviceIoControlFile( console->server, NULL, NULL, NULL, &io, IOCTL_CONDRV_CTRL_EVENT,
458 &ctrl_event, sizeof(ctrl_event), NULL, 0 );
462 else i++;
465 console->record_count += count;
466 return flush ? process_console_input( console ) : STATUS_SUCCESS;
469 static void set_key_input_record( INPUT_RECORD *record, WCHAR ch, unsigned int vk, BOOL is_down, unsigned int ctrl_state )
471 record->EventType = KEY_EVENT;
472 record->Event.KeyEvent.bKeyDown = is_down;
473 record->Event.KeyEvent.wRepeatCount = 1;
474 record->Event.KeyEvent.uChar.UnicodeChar = ch;
475 record->Event.KeyEvent.wVirtualKeyCode = vk;
476 record->Event.KeyEvent.wVirtualScanCode = MapVirtualKeyW( vk, MAPVK_VK_TO_VSC );
477 record->Event.KeyEvent.dwControlKeyState = ctrl_state;
480 static NTSTATUS key_press( struct console *console, WCHAR ch, unsigned int vk, unsigned int ctrl_state )
482 INPUT_RECORD records[8];
483 unsigned int count = 0, ctrl = 0;
485 if (ctrl_state & SHIFT_PRESSED)
487 ctrl |= SHIFT_PRESSED;
488 set_key_input_record( &records[count++], 0, VK_SHIFT, TRUE, ctrl );
490 if (ctrl_state & LEFT_ALT_PRESSED)
492 ctrl |= LEFT_ALT_PRESSED;
493 set_key_input_record( &records[count++], 0, VK_MENU, TRUE, ctrl );
495 if (ctrl_state & LEFT_CTRL_PRESSED)
497 ctrl |= LEFT_CTRL_PRESSED;
498 set_key_input_record( &records[count++], 0, VK_CONTROL, TRUE, ctrl );
501 set_key_input_record( &records[count++], ch, vk, TRUE, ctrl );
502 set_key_input_record( &records[count++], ch, vk, FALSE, ctrl );
504 if (ctrl & LEFT_CTRL_PRESSED)
506 ctrl &= ~LEFT_CTRL_PRESSED;
507 set_key_input_record( &records[count++], 0, VK_CONTROL, FALSE, ctrl );
509 if (ctrl & LEFT_ALT_PRESSED)
511 ctrl &= ~LEFT_ALT_PRESSED;
512 set_key_input_record( &records[count++], 0, VK_MENU, FALSE, ctrl );
514 if (ctrl & SHIFT_PRESSED)
516 ctrl &= ~SHIFT_PRESSED;
517 set_key_input_record( &records[count++], 0, VK_SHIFT, FALSE, ctrl );
520 return write_console_input( console, records, count, FALSE );
523 static void char_key_press( struct console *console, WCHAR ch, unsigned int ctrl )
525 unsigned int vk = VkKeyScanW( ch );
526 if (vk == ~0) vk = 0;
527 if (vk & 0x0100) ctrl |= SHIFT_PRESSED;
528 if (vk & 0x0200) ctrl |= LEFT_CTRL_PRESSED;
529 if (vk & 0x0400) ctrl |= LEFT_ALT_PRESSED;
530 vk &= 0xff;
531 key_press( console, ch, vk, ctrl );
534 static unsigned int escape_char_to_vk( WCHAR ch )
536 switch (ch)
538 case 'A': return VK_UP;
539 case 'B': return VK_DOWN;
540 case 'C': return VK_RIGHT;
541 case 'D': return VK_LEFT;
542 case 'H': return VK_HOME;
543 case 'F': return VK_END;
544 case 'P': return VK_F1;
545 case 'Q': return VK_F2;
546 case 'R': return VK_F3;
547 case 'S': return VK_F4;
548 default: return 0;
552 static unsigned int escape_number_to_vk( unsigned int n )
554 switch(n)
556 case 2: return VK_INSERT;
557 case 3: return VK_DELETE;
558 case 5: return VK_PRIOR;
559 case 6: return VK_NEXT;
560 case 15: return VK_F5;
561 case 17: return VK_F6;
562 case 18: return VK_F7;
563 case 19: return VK_F8;
564 case 20: return VK_F9;
565 case 21: return VK_F10;
566 case 23: return VK_F11;
567 case 24: return VK_F12;
568 default: return 0;
572 static unsigned int convert_modifiers( unsigned int n )
574 unsigned int ctrl = 0;
575 if (!n || n > 16) return 0;
576 n--;
577 if (n & 1) ctrl |= SHIFT_PRESSED;
578 if (n & 2) ctrl |= LEFT_ALT_PRESSED;
579 if (n & 4) ctrl |= LEFT_CTRL_PRESSED;
580 return ctrl;
583 static unsigned int process_csi_sequence( struct console *console, const WCHAR *buf, size_t size )
585 unsigned int n, count = 0, params[8], params_cnt = 0, vk;
587 for (;;)
589 n = 0;
590 while (count < size && '0' <= buf[count] && buf[count] <= '9')
591 n = n * 10 + buf[count++] - '0';
592 if (params_cnt < ARRAY_SIZE(params)) params[params_cnt++] = n;
593 else FIXME( "too many params, skipping %u\n", n );
594 if (count == size) return 0;
595 if (buf[count] != ';') break;
596 if (++count == size) return 0;
599 if ((vk = escape_char_to_vk( buf[count] )))
601 key_press( console, 0, vk, params_cnt >= 2 ? convert_modifiers( params[1] ) : 0 );
602 return count + 1;
605 switch (buf[count])
607 case '~':
608 vk = escape_number_to_vk( params[0] );
609 key_press( console, 0, vk, params_cnt == 2 ? convert_modifiers( params[1] ) : 0 );
610 return count + 1;
612 default:
613 FIXME( "unhandled sequence %s\n", debugstr_wn( buf, size ));
614 return 0;
618 static unsigned int process_input_escape( struct console *console, const WCHAR *buf, size_t size )
620 unsigned int vk = 0, count = 0, nlen;
622 if (!size)
624 key_press( console, 0, VK_ESCAPE, 0 );
625 return 0;
628 switch(buf[0])
630 case '[':
631 if (++count == size) break;
632 if ((nlen = process_csi_sequence( console, buf + 1, size - 1 ))) return count + nlen;
633 break;
635 case 'O':
636 if (++count == size) break;
637 vk = escape_char_to_vk( buf[1] );
638 if (vk)
640 key_press( console, 0, vk, 0 );
641 return count + 1;
645 char_key_press( console, buf[0], LEFT_ALT_PRESSED );
646 return 1;
649 static DWORD WINAPI tty_input( void *param )
651 struct console *console = param;
652 IO_STATUS_BLOCK io;
653 HANDLE event;
654 char read_buf[4096];
655 WCHAR buf[4096];
656 DWORD count, i;
657 NTSTATUS status;
659 event = CreateEventW( NULL, TRUE, FALSE, NULL );
661 for (;;)
663 status = NtReadFile( console->tty_input, event, NULL, NULL, &io, read_buf, sizeof(read_buf), NULL, NULL );
664 if (status == STATUS_PENDING)
666 if ((status = NtWaitForSingleObject( event, FALSE, NULL ))) break;
667 status = io.Status;
669 if (status) break;
671 EnterCriticalSection( &console_section );
673 /* FIXME: Handle partial char read */
674 count = MultiByteToWideChar(CP_UTF8, 0, read_buf, io.Information, buf, ARRAY_SIZE(buf));
676 TRACE( "%s\n", debugstr_wn(buf, count) );
678 for (i = 0; i < count; i++)
680 WCHAR ch = buf[i];
681 switch (ch)
683 case 3: /* end of text */
684 return 0;
685 case '\n':
686 key_press( console, '\n', VK_RETURN, LEFT_CTRL_PRESSED );
687 break;
688 case '\b':
689 key_press( console, ch, 'H', LEFT_CTRL_PRESSED );
690 break;
691 case 0x1b:
692 i += process_input_escape( console, buf + i + 1, count - i - 1 );
693 break;
694 case 0x7f:
695 key_press( console, '\b', VK_BACK, 0 );
696 break;
697 default:
698 char_key_press( console, ch, 0 );
702 process_console_input( console );
703 LeaveCriticalSection( &console_section );
706 TRACE( "NtReadFile failed: %#x\n", status );
707 return 0;
710 static NTSTATUS screen_buffer_activate( struct screen_buffer *screen_buffer )
712 RECT update_rect;
713 TRACE( "%p\n", screen_buffer );
714 screen_buffer->console->active = screen_buffer;
715 SetRect( &update_rect, 0, 0, screen_buffer->width - 1, screen_buffer->height - 1);
716 update_output( screen_buffer, &update_rect );
717 tty_sync( screen_buffer->console );
718 return STATUS_SUCCESS;
721 static NTSTATUS get_output_info( struct screen_buffer *screen_buffer, size_t *out_size )
723 struct condrv_output_info *info;
725 TRACE( "%p\n", screen_buffer );
727 *out_size = min( *out_size, sizeof(*info) + screen_buffer->font.face_len );
728 if (!(info = alloc_ioctl_buffer( *out_size ))) return STATUS_NO_MEMORY;
730 info->cursor_size = screen_buffer->cursor_size;
731 info->cursor_visible = screen_buffer->cursor_visible;
732 info->cursor_x = screen_buffer->cursor_x;
733 info->cursor_y = screen_buffer->cursor_y;
734 info->width = screen_buffer->width;
735 info->height = screen_buffer->height;
736 info->attr = screen_buffer->attr;
737 info->popup_attr = screen_buffer->popup_attr;
738 info->win_left = screen_buffer->win.left;
739 info->win_top = screen_buffer->win.top;
740 info->win_right = screen_buffer->win.right;
741 info->win_bottom = screen_buffer->win.bottom;
742 info->max_width = screen_buffer->max_width;
743 info->max_height = screen_buffer->max_height;
744 info->font_width = screen_buffer->font.width;
745 info->font_height = screen_buffer->font.height;
746 info->font_weight = screen_buffer->font.weight;
747 info->font_pitch_family = screen_buffer->font.pitch_family;
748 memcpy( info->color_map, screen_buffer->color_map, sizeof(info->color_map) );
749 if (*out_size > sizeof(*info)) memcpy( info + 1, screen_buffer->font.face_name, *out_size - sizeof(*info) );
750 return STATUS_SUCCESS;
753 static NTSTATUS change_screen_buffer_size( struct screen_buffer *screen_buffer, int new_width, int new_height )
755 int i, old_width, old_height, copy_width, copy_height;
756 char_info_t *new_data;
758 if (!(new_data = malloc( new_width * new_height * sizeof(*new_data) ))) return STATUS_NO_MEMORY;
760 old_width = screen_buffer->width;
761 old_height = screen_buffer->height;
762 copy_width = min( old_width, new_width );
763 copy_height = min( old_height, new_height );
765 /* copy all the rows */
766 for (i = 0; i < copy_height; i++)
768 memcpy( &new_data[i * new_width], &screen_buffer->data[i * old_width],
769 copy_width * sizeof(char_info_t) );
772 /* clear the end of each row */
773 if (new_width > old_width)
775 /* fill first row */
776 for (i = old_width; i < new_width; i++) new_data[i] = empty_char_info;
777 /* and blast it to the other rows */
778 for (i = 1; i < copy_height; i++)
779 memcpy( &new_data[i * new_width + old_width], &new_data[old_width],
780 (new_width - old_width) * sizeof(char_info_t) );
783 /* clear remaining rows */
784 if (new_height > old_height)
786 /* fill first row */
787 for (i = 0; i < new_width; i++) new_data[old_height * new_width + i] = empty_char_info;
788 /* and blast it to the other rows */
789 for (i = old_height+1; i < new_height; i++)
790 memcpy( &new_data[i * new_width], &new_data[old_height * new_width],
791 new_width * sizeof(char_info_t) );
793 free( screen_buffer->data );
794 screen_buffer->data = new_data;
795 screen_buffer->width = new_width;
796 screen_buffer->height = new_height;
797 return STATUS_SUCCESS;
800 static NTSTATUS set_output_info( struct screen_buffer *screen_buffer,
801 const struct condrv_output_info_params *params, size_t extra_size )
803 const struct condrv_output_info *info = &params->info;
804 WCHAR *font_name;
805 NTSTATUS status;
807 TRACE( "%p\n", screen_buffer );
809 extra_size -= sizeof(*params);
811 if (params->mask & SET_CONSOLE_OUTPUT_INFO_CURSOR_GEOM)
813 if (info->cursor_size < 1 || info->cursor_size > 100) return STATUS_INVALID_PARAMETER;
815 screen_buffer->cursor_size = info->cursor_size;
816 screen_buffer->cursor_visible = !!info->cursor_visible;
818 if (params->mask & SET_CONSOLE_OUTPUT_INFO_CURSOR_POS)
820 if (info->cursor_x < 0 || info->cursor_x >= screen_buffer->width ||
821 info->cursor_y < 0 || info->cursor_y >= screen_buffer->height)
823 return STATUS_INVALID_PARAMETER;
826 if (screen_buffer->cursor_x != info->cursor_x || screen_buffer->cursor_y != info->cursor_y)
828 screen_buffer->cursor_x = info->cursor_x;
829 screen_buffer->cursor_y = info->cursor_y;
832 if (params->mask & SET_CONSOLE_OUTPUT_INFO_SIZE)
834 /* new screen-buffer cannot be smaller than actual window */
835 if (info->width < screen_buffer->win.right - screen_buffer->win.left + 1 ||
836 info->height < screen_buffer->win.bottom - screen_buffer->win.top + 1)
838 return STATUS_INVALID_PARAMETER;
840 /* FIXME: there are also some basic minimum and max size to deal with */
841 if ((status = change_screen_buffer_size( screen_buffer, info->width, info->height ))) return status;
843 /* scroll window to display sb */
844 if (screen_buffer->win.right >= info->width)
846 screen_buffer->win.right -= screen_buffer->win.left;
847 screen_buffer->win.left = 0;
849 if (screen_buffer->win.bottom >= info->height)
851 screen_buffer->win.bottom -= screen_buffer->win.top;
852 screen_buffer->win.top = 0;
854 if (screen_buffer->cursor_x >= info->width) screen_buffer->cursor_x = info->width - 1;
855 if (screen_buffer->cursor_y >= info->height) screen_buffer->cursor_y = info->height - 1;
857 if (is_active( screen_buffer ) && screen_buffer->console->mode & ENABLE_WINDOW_INPUT)
859 INPUT_RECORD ir;
860 ir.EventType = WINDOW_BUFFER_SIZE_EVENT;
861 ir.Event.WindowBufferSizeEvent.dwSize.X = info->width;
862 ir.Event.WindowBufferSizeEvent.dwSize.Y = info->height;
863 write_console_input( screen_buffer->console, &ir, 1, TRUE );
866 if (params->mask & SET_CONSOLE_OUTPUT_INFO_ATTR)
868 screen_buffer->attr = info->attr;
870 if (params->mask & SET_CONSOLE_OUTPUT_INFO_POPUP_ATTR)
872 screen_buffer->popup_attr = info->popup_attr;
874 if (params->mask & SET_CONSOLE_OUTPUT_INFO_DISPLAY_WINDOW)
876 if (info->win_left < 0 || info->win_left > info->win_right ||
877 info->win_right >= screen_buffer->width ||
878 info->win_top < 0 || info->win_top > info->win_bottom ||
879 info->win_bottom >= screen_buffer->height)
881 return STATUS_INVALID_PARAMETER;
883 if (screen_buffer->win.left != info->win_left || screen_buffer->win.top != info->win_top ||
884 screen_buffer->win.right != info->win_right || screen_buffer->win.bottom != info->win_bottom)
886 screen_buffer->win.left = info->win_left;
887 screen_buffer->win.top = info->win_top;
888 screen_buffer->win.right = info->win_right;
889 screen_buffer->win.bottom = info->win_bottom;
892 if (params->mask & SET_CONSOLE_OUTPUT_INFO_MAX_SIZE)
894 screen_buffer->max_width = info->max_width;
895 screen_buffer->max_height = info->max_height;
897 if (params->mask & SET_CONSOLE_OUTPUT_INFO_COLORTABLE)
899 memcpy( screen_buffer->color_map, info->color_map, sizeof(screen_buffer->color_map) );
901 if (params->mask & SET_CONSOLE_OUTPUT_INFO_FONT)
903 screen_buffer->font.width = info->font_width;
904 screen_buffer->font.height = info->font_height;
905 screen_buffer->font.weight = info->font_weight;
906 screen_buffer->font.pitch_family = info->font_pitch_family;
907 if (extra_size)
909 const WCHAR *params_font = (const WCHAR *)(params + 1);
910 extra_size = extra_size / sizeof(WCHAR) * sizeof(WCHAR);
911 font_name = malloc( extra_size );
912 if (font_name)
914 memcpy( font_name, params_font, extra_size );
915 free( screen_buffer->font.face_name );
916 screen_buffer->font.face_name = font_name;
917 screen_buffer->font.face_len = extra_size;
922 if (is_active( screen_buffer )) tty_sync( screen_buffer->console );
923 return STATUS_SUCCESS;
926 static NTSTATUS write_output( struct screen_buffer *screen_buffer, const struct condrv_output_params *params,
927 size_t in_size, size_t *out_size )
929 unsigned int i, entry_size, entry_cnt, x, y;
930 char_info_t *dest;
931 char *src;
933 entry_size = params->mode == CHAR_INFO_MODE_TEXTATTR ? sizeof(char_info_t) : sizeof(WCHAR);
934 entry_cnt = (in_size - sizeof(*params)) / entry_size;
936 TRACE( "(%u,%u) cnt %u\n", params->x, params->y, entry_cnt );
938 if (params->x >= screen_buffer->width)
940 *out_size = 0;
941 return STATUS_SUCCESS;
944 for (i = 0, src = (char *)(params + 1); i < entry_cnt; i++, src += entry_size)
946 if (params->width)
948 x = params->x + i % params->width;
949 y = params->y + i / params->width;
950 if (x >= screen_buffer->width) continue;
952 else
954 x = (params->x + i) % screen_buffer->width;
955 y = params->y + (params->x + i) / screen_buffer->width;
957 if (y >= screen_buffer->height) break;
959 dest = &screen_buffer->data[y * screen_buffer->width + x];
960 switch(params->mode)
962 case CHAR_INFO_MODE_TEXT:
963 dest->ch = *(const WCHAR *)src;
964 break;
965 case CHAR_INFO_MODE_ATTR:
966 dest->attr = *(const unsigned short *)src;
967 break;
968 case CHAR_INFO_MODE_TEXTATTR:
969 *dest = *(const char_info_t *)src;
970 break;
971 case CHAR_INFO_MODE_TEXTSTDATTR:
972 dest->ch = *(const WCHAR *)src;
973 dest->attr = screen_buffer->attr;
974 break;
975 default:
976 return STATUS_INVALID_PARAMETER;
980 if (i && is_active( screen_buffer ))
982 RECT update_rect;
984 update_rect.left = params->x;
985 update_rect.top = params->y;
986 if (params->width)
988 update_rect.bottom = min( params->y + entry_cnt / params->width, screen_buffer->height ) - 1;
989 update_rect.right = min( params->x + params->width, screen_buffer->width ) - 1;
991 else
993 update_rect.bottom = params->y + (params->x + i - 1) / screen_buffer->width;
994 if (update_rect.bottom != params->y)
996 update_rect.left = 0;
997 update_rect.right = screen_buffer->width - 1;
999 else
1001 update_rect.right = params->x + i - 1;
1004 update_output( screen_buffer, &update_rect );
1005 tty_sync( screen_buffer->console );
1008 if (*out_size == sizeof(SMALL_RECT))
1010 SMALL_RECT *region;
1011 unsigned int width = params->width;
1012 x = params->x;
1013 y = params->y;
1014 if (!(region = alloc_ioctl_buffer( sizeof(*region )))) return STATUS_NO_MEMORY;
1015 region->Left = x;
1016 region->Top = y;
1017 region->Right = min( x + width, screen_buffer->width ) - 1;
1018 region->Bottom = min( y + entry_cnt / width, screen_buffer->height ) - 1;
1020 else
1022 DWORD *result;
1023 if (!(result = alloc_ioctl_buffer( sizeof(*result )))) return STATUS_NO_MEMORY;
1024 *result = i;
1027 return STATUS_SUCCESS;
1030 static NTSTATUS read_output( struct screen_buffer *screen_buffer, const struct condrv_output_params *params,
1031 size_t *out_size )
1033 enum char_info_mode mode;
1034 unsigned int x, y, width;
1035 unsigned int i, count;
1037 x = params->x;
1038 y = params->y;
1039 mode = params->mode;
1040 width = params->width;
1041 TRACE( "(%u %u) mode %u width %u\n", x, y, mode, width );
1043 switch(mode)
1045 case CHAR_INFO_MODE_TEXT:
1047 WCHAR *data;
1048 char_info_t *src;
1049 if (x >= screen_buffer->width || y >= screen_buffer->height)
1051 *out_size = 0;
1052 return STATUS_SUCCESS;
1054 src = screen_buffer->data + y * screen_buffer->width + x;
1055 count = min( screen_buffer->data + screen_buffer->height * screen_buffer->width - src,
1056 *out_size / sizeof(*data) );
1057 *out_size = count * sizeof(*data);
1058 if (!(data = alloc_ioctl_buffer( *out_size ))) return STATUS_NO_MEMORY;
1059 for (i = 0; i < count; i++) data[i] = src[i].ch;
1061 break;
1062 case CHAR_INFO_MODE_ATTR:
1064 unsigned short *data;
1065 char_info_t *src;
1066 if (x >= screen_buffer->width || y >= screen_buffer->height)
1068 *out_size = 0;
1069 return STATUS_SUCCESS;
1071 src = screen_buffer->data + y * screen_buffer->width + x;
1072 count = min( screen_buffer->data + screen_buffer->height * screen_buffer->width - src,
1073 *out_size / sizeof(*data) );
1074 *out_size = count * sizeof(*data);
1075 if (!(data = alloc_ioctl_buffer( *out_size ))) return STATUS_NO_MEMORY;
1076 for (i = 0; i < count; i++) data[i] = src[i].attr;
1078 break;
1079 case CHAR_INFO_MODE_TEXTATTR:
1081 SMALL_RECT *region;
1082 char_info_t *data;
1083 if (!width || *out_size < sizeof(*region) || x >= screen_buffer->width || y >= screen_buffer->height)
1084 return STATUS_INVALID_PARAMETER;
1085 count = min( (*out_size - sizeof(*region)) / (width * sizeof(*data)), screen_buffer->height - y );
1086 width = min( width, screen_buffer->width - x );
1087 *out_size = sizeof(*region) + width * count * sizeof(*data);
1088 if (!(region = alloc_ioctl_buffer( *out_size ))) return STATUS_NO_MEMORY;
1089 region->Left = x;
1090 region->Top = y;
1091 region->Right = x + width - 1;
1092 region->Bottom = y + count - 1;
1093 data = (char_info_t *)(region + 1);
1094 for (i = 0; i < count; i++)
1096 memcpy( &data[i * width], &screen_buffer->data[(y + i) * screen_buffer->width + x],
1097 width * sizeof(*data) );
1100 break;
1101 default:
1102 return STATUS_INVALID_PARAMETER;
1105 return STATUS_SUCCESS;
1108 static NTSTATUS fill_output( struct screen_buffer *screen_buffer, const struct condrv_fill_output_params *params )
1110 char_info_t *end, *dest;
1111 DWORD i, count, *result;
1113 TRACE( "(%u %u) mode %u\n", params->x, params->y, params->mode );
1115 dest = screen_buffer->data + params->y * screen_buffer->width + params->x;
1117 if (params->y >= screen_buffer->height) return STATUS_SUCCESS;
1119 if (params->wrap)
1120 end = screen_buffer->data + screen_buffer->height * screen_buffer->width;
1121 else
1122 end = screen_buffer->data + (params->y + 1) * screen_buffer->width;
1124 count = params->count;
1125 if (count > end - dest) count = end - dest;
1127 switch(params->mode)
1129 case CHAR_INFO_MODE_TEXT:
1130 for (i = 0; i < count; i++) dest[i].ch = params->ch;
1131 break;
1132 case CHAR_INFO_MODE_ATTR:
1133 for (i = 0; i < count; i++) dest[i].attr = params->attr;
1134 break;
1135 case CHAR_INFO_MODE_TEXTATTR:
1136 for (i = 0; i < count; i++)
1138 dest[i].ch = params->ch;
1139 dest[i].attr = params->attr;
1141 break;
1142 case CHAR_INFO_MODE_TEXTSTDATTR:
1143 for (i = 0; i < count; i++)
1145 dest[i].ch = params->ch;
1146 dest[i].attr = screen_buffer->attr;
1148 break;
1149 default:
1150 return STATUS_INVALID_PARAMETER;
1153 if (count && is_active(screen_buffer))
1155 RECT update_rect;
1156 SetRect( &update_rect,
1157 params->x % screen_buffer->width,
1158 params->y + params->x / screen_buffer->width,
1159 (params->x + i - 1) % screen_buffer->width,
1160 params->y + (params->x + i - 1) / screen_buffer->width );
1161 update_output( screen_buffer, &update_rect );
1162 tty_sync( screen_buffer->console );
1165 if (!(result = alloc_ioctl_buffer( sizeof(*result) ))) return STATUS_NO_MEMORY;
1166 *result = count;
1167 return STATUS_SUCCESS;
1170 static NTSTATUS scroll_output( struct screen_buffer *screen_buffer, const struct condrv_scroll_params *params )
1172 int x, y, xsrc, ysrc, w, h;
1173 char_info_t *psrc, *pdst;
1174 SMALL_RECT src, dst;
1175 RECT update_rect;
1176 SMALL_RECT clip;
1178 xsrc = params->scroll.Left;
1179 ysrc = params->scroll.Top;
1180 w = params->scroll.Right - params->scroll.Left + 1;
1181 h = params->scroll.Bottom - params->scroll.Top + 1;
1183 TRACE( "(%d %d) -> (%u %u) w %u h %u\n", xsrc, ysrc, params->origin.X, params->origin.Y, w, h );
1185 clip.Left = max( params->clip.Left, 0 );
1186 clip.Top = max( params->clip.Top, 0 );
1187 clip.Right = min( params->clip.Right, screen_buffer->width - 1 );
1188 clip.Bottom = min( params->clip.Bottom, screen_buffer->height - 1 );
1189 if (clip.Left > clip.Right || clip.Top > clip.Bottom || params->scroll.Left < 0 || params->scroll.Top < 0 ||
1190 params->scroll.Right >= screen_buffer->width || params->scroll.Bottom >= screen_buffer->height ||
1191 params->scroll.Right < params->scroll.Left || params->scroll.Top > params->scroll.Bottom ||
1192 params->origin.X < 0 || params->origin.X >= screen_buffer->width || params->origin.Y < 0 ||
1193 params->origin.Y >= screen_buffer->height)
1194 return STATUS_INVALID_PARAMETER;
1196 src.Left = max( xsrc, clip.Left );
1197 src.Top = max( ysrc, clip.Top );
1198 src.Right = min( xsrc + w - 1, clip.Right );
1199 src.Bottom = min( ysrc + h - 1, clip.Bottom );
1201 dst.Left = params->origin.X;
1202 dst.Top = params->origin.Y;
1203 dst.Right = params->origin.X + w - 1;
1204 dst.Bottom = params->origin.Y + h - 1;
1206 if (dst.Left < clip.Left)
1208 xsrc += clip.Left - dst.Left;
1209 w -= clip.Left - dst.Left;
1210 dst.Left = clip.Left;
1212 if (dst.Top < clip.Top)
1214 ysrc += clip.Top - dst.Top;
1215 h -= clip.Top - dst.Top;
1216 dst.Top = clip.Top;
1218 if (dst.Right > clip.Right) w -= dst.Right - clip.Right;
1219 if (dst.Bottom > clip.Bottom) h -= dst.Bottom - clip.Bottom;
1221 if (w > 0 && h > 0)
1223 if (ysrc < dst.Top)
1225 psrc = &screen_buffer->data[(ysrc + h - 1) * screen_buffer->width + xsrc];
1226 pdst = &screen_buffer->data[(dst.Top + h - 1) * screen_buffer->width + dst.Left];
1228 for (y = h; y > 0; y--)
1230 memcpy( pdst, psrc, w * sizeof(*pdst) );
1231 pdst -= screen_buffer->width;
1232 psrc -= screen_buffer->width;
1235 else
1237 psrc = &screen_buffer->data[ysrc * screen_buffer->width + xsrc];
1238 pdst = &screen_buffer->data[dst.Top * screen_buffer->width + dst.Left];
1240 for (y = 0; y < h; y++)
1242 /* we use memmove here because when psrc and pdst are the same,
1243 * copies are done on the same row, so the dst and src blocks
1244 * can overlap */
1245 memmove( pdst, psrc, w * sizeof(*pdst) );
1246 pdst += screen_buffer->width;
1247 psrc += screen_buffer->width;
1252 for (y = src.Top; y <= src.Bottom; y++)
1254 int left = src.Left;
1255 int right = src.Right;
1256 if (dst.Top <= y && y <= dst.Bottom)
1258 if (dst.Left <= src.Left) left = max( left, dst.Right + 1 );
1259 if (dst.Left >= src.Left) right = min( right, dst.Left - 1 );
1261 for (x = left; x <= right; x++) screen_buffer->data[y * screen_buffer->width + x] = params->fill;
1264 SetRect( &update_rect, min( src.Left, dst.Left ), min( src.Top, dst.Top ),
1265 max( src.Right, dst.Right ), max( src.Bottom, dst.Bottom ));
1266 update_output( screen_buffer, &update_rect );
1267 tty_sync( screen_buffer->console );
1268 return STATUS_SUCCESS;
1271 static NTSTATUS set_console_title( struct console *console, const WCHAR *in_title, size_t size )
1273 WCHAR *title = NULL;
1275 TRACE( "%s\n", debugstr_wn(in_title, size / sizeof(WCHAR)) );
1277 if (size)
1279 if (!(title = malloc( size ))) return STATUS_NO_MEMORY;
1280 memcpy( title, in_title, size );
1282 free( console->title );
1283 console->title = title;
1284 console->title_len = size;
1286 if (console->tty_output)
1288 size_t len;
1289 char *vt;
1291 tty_write( console, "\x1b]0;", 4 );
1292 len = WideCharToMultiByte( CP_UTF8, 0, console->title, size / sizeof(WCHAR), NULL, 0, NULL, NULL);
1293 if ((vt = tty_alloc_buffer( console, len )))
1294 WideCharToMultiByte( CP_UTF8, 0, console->title, size / sizeof(WCHAR), vt, len, NULL, NULL );
1295 tty_write( console, "\x07", 1 );
1296 tty_sync( console );
1298 return STATUS_SUCCESS;
1301 static NTSTATUS screen_buffer_ioctl( struct screen_buffer *screen_buffer, unsigned int code,
1302 const void *in_data, size_t in_size, size_t *out_size )
1304 switch (code)
1306 case IOCTL_CONDRV_CLOSE_OUTPUT:
1307 if (in_size || *out_size) return STATUS_INVALID_PARAMETER;
1308 destroy_screen_buffer( screen_buffer );
1309 return STATUS_SUCCESS;
1311 case IOCTL_CONDRV_ACTIVATE:
1312 if (in_size || *out_size) return STATUS_INVALID_PARAMETER;
1313 return screen_buffer_activate( screen_buffer );
1315 case IOCTL_CONDRV_GET_MODE:
1317 DWORD *mode;
1318 TRACE( "returning mode %x\n", screen_buffer->mode );
1319 if (in_size || *out_size != sizeof(*mode)) return STATUS_INVALID_PARAMETER;
1320 if (!(mode = alloc_ioctl_buffer( *out_size ))) return STATUS_NO_MEMORY;
1321 *mode = screen_buffer->mode;
1322 return STATUS_SUCCESS;
1325 case IOCTL_CONDRV_SET_MODE:
1326 if (in_size != sizeof(unsigned int) || *out_size) return STATUS_INVALID_PARAMETER;
1327 screen_buffer->mode = *(unsigned int *)in_data;
1328 TRACE( "set %x mode\n", screen_buffer->mode );
1329 return STATUS_SUCCESS;
1331 case IOCTL_CONDRV_WRITE_OUTPUT:
1332 if ((*out_size != sizeof(DWORD) && *out_size != sizeof(SMALL_RECT)) ||
1333 in_size < sizeof(struct condrv_output_params))
1334 return STATUS_INVALID_PARAMETER;
1335 return write_output( screen_buffer, in_data, in_size, out_size );
1337 case IOCTL_CONDRV_READ_OUTPUT:
1338 if (in_size != sizeof(struct condrv_output_params)) return STATUS_INVALID_PARAMETER;
1339 return read_output( screen_buffer, in_data, out_size );
1341 case IOCTL_CONDRV_GET_OUTPUT_INFO:
1342 if (in_size || *out_size < sizeof(struct condrv_output_info)) return STATUS_INVALID_PARAMETER;
1343 return get_output_info( screen_buffer, out_size );
1345 case IOCTL_CONDRV_SET_OUTPUT_INFO:
1346 if (in_size < sizeof(struct condrv_output_info) || *out_size) return STATUS_INVALID_PARAMETER;
1347 return set_output_info( screen_buffer, in_data, in_size );
1349 case IOCTL_CONDRV_FILL_OUTPUT:
1350 if (in_size != sizeof(struct condrv_fill_output_params) || *out_size != sizeof(DWORD))
1351 return STATUS_INVALID_PARAMETER;
1352 return fill_output( screen_buffer, in_data );
1354 case IOCTL_CONDRV_SCROLL:
1355 if (in_size != sizeof(struct condrv_scroll_params) || *out_size)
1356 return STATUS_INVALID_PARAMETER;
1357 return scroll_output( screen_buffer, in_data );
1359 default:
1360 FIXME( "unsupported ioctl %x\n", code );
1361 return STATUS_NOT_SUPPORTED;
1365 static NTSTATUS console_input_ioctl( struct console *console, unsigned int code, const void *in_data,
1366 size_t in_size, size_t *out_size )
1368 switch (code)
1370 case IOCTL_CONDRV_GET_MODE:
1372 DWORD *mode;
1373 TRACE( "returning mode %x\n", console->mode );
1374 if (in_size || *out_size != sizeof(*mode)) return STATUS_INVALID_PARAMETER;
1375 if (!(mode = alloc_ioctl_buffer( *out_size ))) return STATUS_NO_MEMORY;
1376 *mode = console->mode;
1377 return STATUS_SUCCESS;
1380 case IOCTL_CONDRV_SET_MODE:
1381 if (in_size != sizeof(unsigned int) || *out_size) return STATUS_INVALID_PARAMETER;
1382 console->mode = *(unsigned int *)in_data;
1383 TRACE( "set %x mode\n", console->mode );
1384 return STATUS_SUCCESS;
1386 case IOCTL_CONDRV_READ_INPUT:
1388 unsigned int blocking;
1389 NTSTATUS status;
1390 if (in_size && in_size != sizeof(blocking)) return STATUS_INVALID_PARAMETER;
1391 blocking = in_size && *(unsigned int *)in_data;
1392 if (blocking && !console->record_count && *out_size)
1394 TRACE( "pending read" );
1395 console->pending_read = *out_size;
1396 return STATUS_PENDING;
1398 status = read_console_input( console, *out_size );
1399 *out_size = 0;
1400 return status;
1403 case IOCTL_CONDRV_WRITE_INPUT:
1404 if (in_size % sizeof(INPUT_RECORD) || *out_size) return STATUS_INVALID_PARAMETER;
1405 return write_console_input( console, in_data, in_size / sizeof(INPUT_RECORD), TRUE );
1407 case IOCTL_CONDRV_PEEK:
1409 void *result;
1410 TRACE( "peek\n ");
1411 if (in_size) return STATUS_INVALID_PARAMETER;
1412 *out_size = min( *out_size, console->record_count * sizeof(INPUT_RECORD) );
1413 if (!(result = alloc_ioctl_buffer( *out_size ))) return STATUS_NO_MEMORY;
1414 if (*out_size) memcpy( result, console->records, *out_size );
1415 return STATUS_SUCCESS;
1418 case IOCTL_CONDRV_GET_INPUT_INFO:
1420 struct condrv_input_info *info;
1421 TRACE( "get info\n" );
1422 if (in_size || *out_size != sizeof(*info)) return STATUS_INVALID_PARAMETER;
1423 if (!(info = alloc_ioctl_buffer( sizeof(*info )))) return STATUS_NO_MEMORY;
1424 info->history_mode = console->history_mode;
1425 info->history_size = console->history_size;
1426 info->history_index = console->history_index;
1427 info->edition_mode = console->edition_mode;
1428 info->input_cp = console->input_cp;
1429 info->output_cp = console->output_cp;
1430 info->win = console->win;
1431 info->input_count = console->record_count;
1432 return STATUS_SUCCESS;
1435 case IOCTL_CONDRV_SET_INPUT_INFO:
1437 const struct condrv_input_info_params *params = in_data;
1438 TRACE( "set info\n" );
1439 if (in_size != sizeof(*params) || *out_size) return STATUS_INVALID_PARAMETER;
1440 if (params->mask & SET_CONSOLE_INPUT_INFO_HISTORY_MODE)
1442 console->history_mode = params->info.history_mode;
1444 if ((params->mask & SET_CONSOLE_INPUT_INFO_HISTORY_SIZE) &&
1445 console->history_size != params->info.history_size)
1447 struct history_line **mem = NULL;
1448 int i, delta;
1450 if (params->info.history_size)
1452 if (!(mem = malloc( params->info.history_size * sizeof(*mem) )))
1453 return STATUS_NO_MEMORY;
1454 memset( mem, 0, params->info.history_size * sizeof(*mem) );
1457 delta = (console->history_index > params->info.history_size)
1458 ? (console->history_index - params->info.history_size) : 0;
1460 for (i = delta; i < console->history_index; i++)
1462 mem[i - delta] = console->history[i];
1463 console->history[i] = NULL;
1465 console->history_index -= delta;
1467 for (i = 0; i < console->history_size; i++)
1468 free( console->history[i] );
1469 free( console->history );
1470 console->history = mem;
1471 console->history_size = params->info.history_size;
1473 if (params->mask & SET_CONSOLE_INPUT_INFO_EDITION_MODE)
1475 console->edition_mode = params->info.edition_mode;
1477 if (params->mask & SET_CONSOLE_INPUT_INFO_INPUT_CODEPAGE)
1479 console->input_cp = params->info.input_cp;
1481 if (params->mask & SET_CONSOLE_INPUT_INFO_OUTPUT_CODEPAGE)
1483 console->output_cp = params->info.output_cp;
1485 if (params->mask & SET_CONSOLE_INPUT_INFO_WIN)
1487 console->win = params->info.win;
1489 return STATUS_SUCCESS;
1492 case IOCTL_CONDRV_GET_TITLE:
1494 WCHAR *result;
1495 if (in_size) return STATUS_INVALID_PARAMETER;
1496 TRACE( "returning title %s\n", debugstr_wn(console->title,
1497 console->title_len / sizeof(WCHAR)) );
1498 if (!(result = alloc_ioctl_buffer( *out_size = min( *out_size, console->title_len ))))
1499 return STATUS_NO_MEMORY;
1500 if (*out_size) memcpy( result, console->title, *out_size );
1501 return STATUS_SUCCESS;
1504 case IOCTL_CONDRV_SET_TITLE:
1505 if (in_size % sizeof(WCHAR) || *out_size) return STATUS_INVALID_PARAMETER;
1506 return set_console_title( console, in_data, in_size );
1508 default:
1509 FIXME( "unsupported ioctl %x\n", code );
1510 return STATUS_NOT_SUPPORTED;
1514 static NTSTATUS process_console_ioctls( struct console *console )
1516 size_t out_size = 0, in_size;
1517 unsigned int code;
1518 int output;
1519 NTSTATUS status = STATUS_SUCCESS;
1521 for (;;)
1523 if (status) out_size = 0;
1525 SERVER_START_REQ( get_next_console_request )
1527 req->handle = wine_server_obj_handle( console->server );
1528 req->status = status;
1529 req->signal = console->record_count != 0;
1530 wine_server_add_data( req, ioctl_buffer, out_size );
1531 wine_server_set_reply( req, ioctl_buffer, ioctl_buffer_size );
1532 status = wine_server_call( req );
1533 code = reply->code;
1534 output = reply->output;
1535 out_size = reply->out_size;
1536 in_size = wine_server_reply_size( reply );
1538 SERVER_END_REQ;
1540 if (status == STATUS_PENDING) return STATUS_SUCCESS;
1541 if (status == STATUS_BUFFER_OVERFLOW)
1543 if (!alloc_ioctl_buffer( out_size )) return STATUS_NO_MEMORY;
1544 status = STATUS_SUCCESS;
1545 continue;
1547 if (status)
1549 TRACE( "failed to get next request: %#x\n", status );
1550 return status;
1553 if (code == IOCTL_CONDRV_INIT_OUTPUT)
1555 TRACE( "initializing output %x\n", output );
1556 if (console->active)
1557 create_screen_buffer( console, output, console->active->width, console->active->height );
1558 else
1559 create_screen_buffer( console, output, 80, 150 );
1561 else if (!output)
1563 status = console_input_ioctl( console, code, ioctl_buffer, in_size, &out_size );
1565 else
1567 struct wine_rb_entry *entry;
1568 if (!(entry = wine_rb_get( &screen_buffer_map, LongToPtr(output) )))
1570 ERR( "invalid screen buffer id %x\n", output );
1571 status = STATUS_INVALID_HANDLE;
1573 else
1575 status = screen_buffer_ioctl( WINE_RB_ENTRY_VALUE( entry, struct screen_buffer, entry ), code,
1576 ioctl_buffer, in_size, &out_size );
1582 static int main_loop( struct console *console, HANDLE signal )
1584 HANDLE signal_event = NULL;
1585 HANDLE wait_handles[2];
1586 unsigned int wait_cnt = 0;
1587 unsigned short signal_id;
1588 IO_STATUS_BLOCK signal_io;
1589 NTSTATUS status;
1590 DWORD res;
1592 if (signal)
1594 if (!(signal_event = CreateEventW( NULL, TRUE, FALSE, NULL ))) return 1;
1595 status = NtReadFile( signal, signal_event, NULL, NULL, &signal_io, &signal_id,
1596 sizeof(signal_id), NULL, NULL );
1597 if (status && status != STATUS_PENDING) return 1;
1600 if (!alloc_ioctl_buffer( 4096 )) return 1;
1602 wait_handles[wait_cnt++] = console->server;
1603 if (signal) wait_handles[wait_cnt++] = signal_event;
1605 for (;;)
1607 res = WaitForMultipleObjects( wait_cnt, wait_handles, FALSE, INFINITE );
1609 switch (res)
1611 case WAIT_OBJECT_0:
1612 EnterCriticalSection( &console_section );
1613 status = process_console_ioctls( console );
1614 LeaveCriticalSection( &console_section );
1615 if (status) return 0;
1616 break;
1618 case WAIT_OBJECT_0 + 1:
1619 if (signal_io.Status || signal_io.Information != sizeof(signal_id))
1621 TRACE( "signaled quit\n" );
1622 return 0;
1624 FIXME( "unimplemented signal %x\n", signal_id );
1625 status = NtReadFile( signal, signal_event, NULL, NULL, &signal_io, &signal_id,
1626 sizeof(signal_id), NULL, NULL );
1627 if (status && status != STATUS_PENDING) return 1;
1628 break;
1630 default:
1631 TRACE( "wait failed, quit\n");
1632 return 0;
1636 return 0;
1639 int __cdecl wmain(int argc, WCHAR *argv[])
1641 int headless = 0, i, width = 80, height = 150;
1642 HANDLE signal = NULL, input_thread;
1643 WCHAR *end;
1645 static struct console console;
1647 for (i = 0; i < argc; i++) TRACE("%s ", wine_dbgstr_w(argv[i]));
1648 TRACE("\n");
1650 console.mode = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT |
1651 ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT | ENABLE_INSERT_MODE |
1652 ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS | ENABLE_AUTO_POSITION;
1653 console.input_cp = console.output_cp = GetOEMCP();
1654 console.history_size = 50;
1655 if (!(console.history = calloc( console.history_size, sizeof(*console.history) ))) return 1;
1657 for (i = 1; i < argc; i++)
1659 if (!wcscmp( argv[i], L"--headless"))
1661 headless = 1;
1662 continue;
1664 if (!wcscmp( argv[i], L"--width" ))
1666 if (++i == argc) return 1;
1667 width = wcstol( argv[i], &end, 0 );
1668 if (!width || width > 0xffff || *end) return 1;
1669 continue;
1671 if (!wcscmp( argv[i], L"--height" ))
1673 if (++i == argc) return 1;
1674 height = wcstol( argv[i], &end, 0 );
1675 if (!height || height > 0xffff || *end) return 1;
1676 continue;
1678 if (!wcscmp( argv[i], L"--signal" ))
1680 if (++i == argc) return 1;
1681 signal = ULongToHandle( wcstol( argv[i], &end, 0 ));
1682 if (*end) return 1;
1683 continue;
1685 if (!wcscmp( argv[i], L"--server" ))
1687 if (++i == argc) return 1;
1688 console.server = ULongToHandle( wcstol( argv[i], &end, 0 ));
1689 if (*end) return 1;
1690 continue;
1692 FIXME( "unknown option %s\n", debugstr_w(argv[i]) );
1693 return 1;
1696 if (!headless)
1698 FIXME( "windowed mode not supported\n" );
1699 return 0;
1702 if (!console.server)
1704 ERR( "no server handle\n" );
1705 return 1;
1708 if (!(console.active = create_screen_buffer( &console, 1, width, height ))) return 1;
1709 if (headless)
1711 console.tty_input = GetStdHandle( STD_INPUT_HANDLE );
1712 console.tty_output = GetStdHandle( STD_OUTPUT_HANDLE );
1713 init_tty_output( &console );
1715 if (!(input_thread = CreateThread( NULL, 0, tty_input, &console, 0, NULL )))
1716 return 1;
1717 CloseHandle( input_thread );
1720 return main_loop( &console, signal );