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
27 #define WIN32_NO_STATUS
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
);
52 short int pitch_family
;
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 */
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 */
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
)
127 if (!(new_buffer
= realloc( ioctl_buffer
, size
))) return NULL
;
128 ioctl_buffer
= new_buffer
;
129 ioctl_buffer_size
= size
;
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
;
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
);
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
);
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
,
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
;
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
)
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
;
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
)
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
);
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
)
284 if ((attr
& 0x0f) != (console
->tty_attr
& 0x0f))
286 if ((attr
& 0x0f) != 7)
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)
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
;
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 );
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
);
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
;
391 req
->status
= STATUS_SUCCESS
;
392 wine_server_add_data( req
, console
->records
, count
* sizeof(*console
->records
) );
393 status
= wine_server_call( req
);
398 ERR( "failed: %#x\n", 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
)
441 if (records
[i
].EventType
== KEY_EVENT
&&
442 records
[i
].Event
.KeyEvent
.uChar
.UnicodeChar
== 'C' - 64 &&
443 !(records
[i
].Event
.KeyEvent
.dwControlKeyState
& ENHANCED_KEY
))
446 memcpy( &console
->records
[console
->record_count
+ i
],
447 &console
->records
[console
->record_count
+ i
+ 1],
448 (count
- i
- 1) * sizeof(INPUT_RECORD
) );
450 if (records
[i
].Event
.KeyEvent
.bKeyDown
)
452 struct condrv_ctrl_event ctrl_event
;
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 );
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
;
531 key_press( console
, ch
, vk
, ctrl
);
534 static unsigned int escape_char_to_vk( WCHAR 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
;
552 static unsigned int escape_number_to_vk( unsigned int 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
;
572 static unsigned int convert_modifiers( unsigned int n
)
574 unsigned int ctrl
= 0;
575 if (!n
|| n
> 16) return 0;
577 if (n
& 1) ctrl
|= SHIFT_PRESSED
;
578 if (n
& 2) ctrl
|= LEFT_ALT_PRESSED
;
579 if (n
& 4) ctrl
|= LEFT_CTRL_PRESSED
;
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
;
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 );
608 vk
= escape_number_to_vk( params
[0] );
609 key_press( console
, 0, vk
, params_cnt
== 2 ? convert_modifiers( params
[1] ) : 0 );
613 FIXME( "unhandled sequence %s\n", debugstr_wn( buf
, size
));
618 static unsigned int process_input_escape( struct console
*console
, const WCHAR
*buf
, size_t size
)
620 unsigned int vk
= 0, count
= 0, nlen
;
624 key_press( console
, 0, VK_ESCAPE
, 0 );
631 if (++count
== size
) break;
632 if ((nlen
= process_csi_sequence( console
, buf
+ 1, size
- 1 ))) return count
+ nlen
;
636 if (++count
== size
) break;
637 vk
= escape_char_to_vk( buf
[1] );
640 key_press( console
, 0, vk
, 0 );
645 char_key_press( console
, buf
[0], LEFT_ALT_PRESSED
);
649 static DWORD WINAPI
tty_input( void *param
)
651 struct console
*console
= param
;
659 event
= CreateEventW( NULL
, TRUE
, FALSE
, NULL
);
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;
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
++)
683 case 3: /* end of text */
686 key_press( console
, '\n', VK_RETURN
, LEFT_CTRL_PRESSED
);
689 key_press( console
, ch
, 'H', LEFT_CTRL_PRESSED
);
692 i
+= process_input_escape( console
, buf
+ i
+ 1, count
- i
- 1 );
695 key_press( console
, '\b', VK_BACK
, 0 );
698 char_key_press( console
, ch
, 0 );
702 process_console_input( console
);
703 LeaveCriticalSection( &console_section
);
706 TRACE( "NtReadFile failed: %#x\n", status
);
710 static NTSTATUS
screen_buffer_activate( struct screen_buffer
*screen_buffer
)
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
)
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
)
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
= ¶ms
->info
;
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
)
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
;
909 const WCHAR
*params_font
= (const WCHAR
*)(params
+ 1);
910 extra_size
= extra_size
/ sizeof(WCHAR
) * sizeof(WCHAR
);
911 font_name
= malloc( extra_size
);
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
;
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
)
941 return STATUS_SUCCESS
;
944 for (i
= 0, src
= (char *)(params
+ 1); i
< entry_cnt
; i
++, src
+= entry_size
)
948 x
= params
->x
+ i
% params
->width
;
949 y
= params
->y
+ i
/ params
->width
;
950 if (x
>= screen_buffer
->width
) continue;
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
];
962 case CHAR_INFO_MODE_TEXT
:
963 dest
->ch
= *(const WCHAR
*)src
;
965 case CHAR_INFO_MODE_ATTR
:
966 dest
->attr
= *(const unsigned short *)src
;
968 case CHAR_INFO_MODE_TEXTATTR
:
969 *dest
= *(const char_info_t
*)src
;
971 case CHAR_INFO_MODE_TEXTSTDATTR
:
972 dest
->ch
= *(const WCHAR
*)src
;
973 dest
->attr
= screen_buffer
->attr
;
976 return STATUS_INVALID_PARAMETER
;
980 if (i
&& is_active( screen_buffer
))
984 update_rect
.left
= params
->x
;
985 update_rect
.top
= params
->y
;
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;
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;
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
))
1011 unsigned int width
= params
->width
;
1014 if (!(region
= alloc_ioctl_buffer( sizeof(*region
)))) return STATUS_NO_MEMORY
;
1017 region
->Right
= min( x
+ width
, screen_buffer
->width
) - 1;
1018 region
->Bottom
= min( y
+ entry_cnt
/ width
, screen_buffer
->height
) - 1;
1023 if (!(result
= alloc_ioctl_buffer( sizeof(*result
)))) return STATUS_NO_MEMORY
;
1027 return STATUS_SUCCESS
;
1030 static NTSTATUS
read_output( struct screen_buffer
*screen_buffer
, const struct condrv_output_params
*params
,
1033 enum char_info_mode mode
;
1034 unsigned int x
, y
, width
;
1035 unsigned int i
, count
;
1039 mode
= params
->mode
;
1040 width
= params
->width
;
1041 TRACE( "(%u %u) mode %u width %u\n", x
, y
, mode
, width
);
1045 case CHAR_INFO_MODE_TEXT
:
1049 if (x
>= screen_buffer
->width
|| y
>= screen_buffer
->height
)
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
;
1062 case CHAR_INFO_MODE_ATTR
:
1064 unsigned short *data
;
1066 if (x
>= screen_buffer
->width
|| y
>= screen_buffer
->height
)
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
;
1079 case CHAR_INFO_MODE_TEXTATTR
:
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
;
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
) );
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
;
1120 end
= screen_buffer
->data
+ screen_buffer
->height
* screen_buffer
->width
;
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
;
1132 case CHAR_INFO_MODE_ATTR
:
1133 for (i
= 0; i
< count
; i
++) dest
[i
].attr
= params
->attr
;
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
;
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
;
1150 return STATUS_INVALID_PARAMETER
;
1153 if (count
&& is_active(screen_buffer
))
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
;
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
;
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
;
1218 if (dst
.Right
> clip
.Right
) w
-= dst
.Right
- clip
.Right
;
1219 if (dst
.Bottom
> clip
.Bottom
) h
-= dst
.Bottom
- clip
.Bottom
;
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
;
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
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
)) );
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
)
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
)
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
:
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
);
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
)
1370 case IOCTL_CONDRV_GET_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
;
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
);
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
:
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
;
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
:
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
);
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
;
1519 NTSTATUS status
= STATUS_SUCCESS
;
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
);
1534 output
= reply
->output
;
1535 out_size
= reply
->out_size
;
1536 in_size
= wine_server_reply_size( reply
);
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
;
1549 TRACE( "failed to get next request: %#x\n", 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
);
1559 create_screen_buffer( console
, output
, 80, 150 );
1563 status
= console_input_ioctl( console
, code
, ioctl_buffer
, in_size
, &out_size
);
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
;
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
;
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
;
1607 res
= WaitForMultipleObjects( wait_cnt
, wait_handles
, FALSE
, INFINITE
);
1612 EnterCriticalSection( &console_section
);
1613 status
= process_console_ioctls( console
);
1614 LeaveCriticalSection( &console_section
);
1615 if (status
) return 0;
1618 case WAIT_OBJECT_0
+ 1:
1619 if (signal_io
.Status
|| signal_io
.Information
!= sizeof(signal_id
))
1621 TRACE( "signaled quit\n" );
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;
1631 TRACE( "wait failed, quit\n");
1639 int __cdecl
wmain(int argc
, WCHAR
*argv
[])
1641 int headless
= 0, i
, width
= 80, height
= 150;
1642 HANDLE signal
= NULL
, input_thread
;
1645 static struct console console
;
1647 for (i
= 0; i
< argc
; i
++) TRACE("%s ", wine_dbgstr_w(argv
[i
]));
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"))
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;
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;
1678 if (!wcscmp( argv
[i
], L
"--signal" ))
1680 if (++i
== argc
) return 1;
1681 signal
= ULongToHandle( wcstol( argv
[i
], &end
, 0 ));
1685 if (!wcscmp( argv
[i
], L
"--server" ))
1687 if (++i
== argc
) return 1;
1688 console
.server
= ULongToHandle( wcstol( argv
[i
], &end
, 0 ));
1692 FIXME( "unknown option %s\n", debugstr_w(argv
[i
]) );
1698 FIXME( "windowed mode not supported\n" );
1702 if (!console
.server
)
1704 ERR( "no server handle\n" );
1708 if (!(console
.active
= create_screen_buffer( &console
, 1, width
, height
))) return 1;
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
)))
1717 CloseHandle( input_thread
);
1720 return main_loop( &console
, signal
);