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 #include "wine/server.h"
28 #include "wine/debug.h"
30 WINE_DEFAULT_DEBUG_CHANNEL(console
);
32 static const char_info_t empty_char_info
= { ' ', 0x0007 }; /* white on black space */
34 static CRITICAL_SECTION console_section
;
35 static CRITICAL_SECTION_DEBUG critsect_debug
=
37 0, 0, &console_section
,
38 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
39 0, 0, { (DWORD_PTR
)(__FILE__
": console_section") }
41 static CRITICAL_SECTION console_section
= { &critsect_debug
, -1, 0, 0, 0, 0 };
43 static void *ioctl_buffer
;
44 static size_t ioctl_buffer_size
;
46 static void *alloc_ioctl_buffer( size_t size
)
48 if (size
> ioctl_buffer_size
)
51 if (!(new_buffer
= realloc( ioctl_buffer
, size
))) return NULL
;
52 ioctl_buffer
= new_buffer
;
53 ioctl_buffer_size
= size
;
58 static int screen_buffer_compare_id( const void *key
, const struct wine_rb_entry
*entry
)
60 struct screen_buffer
*screen_buffer
= WINE_RB_ENTRY_VALUE( entry
, struct screen_buffer
, entry
);
61 return PtrToLong(key
) - screen_buffer
->id
;
64 static struct wine_rb_tree screen_buffer_map
= { screen_buffer_compare_id
};
66 static void destroy_screen_buffer( struct screen_buffer
*screen_buffer
)
68 if (screen_buffer
->console
->active
== screen_buffer
)
69 screen_buffer
->console
->active
= NULL
;
70 wine_rb_remove( &screen_buffer_map
, &screen_buffer
->entry
);
71 free( screen_buffer
->font
.face_name
);
72 free( screen_buffer
->data
);
73 free( screen_buffer
);
76 static struct screen_buffer
*create_screen_buffer( struct console
*console
, int id
, int width
, int height
)
78 struct screen_buffer
*screen_buffer
;
81 if (!(screen_buffer
= calloc( 1, sizeof(*screen_buffer
) ))) return NULL
;
82 screen_buffer
->console
= console
;
83 screen_buffer
->id
= id
;
84 screen_buffer
->mode
= ENABLE_PROCESSED_OUTPUT
| ENABLE_WRAP_AT_EOL_OUTPUT
;
85 screen_buffer
->cursor_size
= 25;
86 screen_buffer
->cursor_visible
= 1;
87 screen_buffer
->width
= width
;
88 screen_buffer
->height
= height
;
92 screen_buffer
->max_width
= console
->active
->max_width
;
93 screen_buffer
->max_height
= console
->active
->max_height
;
94 screen_buffer
->win
.right
= console
->active
->win
.right
- console
->active
->win
.left
;
95 screen_buffer
->win
.bottom
= console
->active
->win
.bottom
- console
->active
->win
.top
;
96 screen_buffer
->attr
= console
->active
->attr
;
97 screen_buffer
->popup_attr
= console
->active
->attr
;
98 screen_buffer
->font
= console
->active
->font
;
99 memcpy( screen_buffer
->color_map
, console
->active
->color_map
, sizeof(console
->active
->color_map
) );
101 if (screen_buffer
->font
.face_len
)
103 screen_buffer
->font
.face_name
= malloc( screen_buffer
->font
.face_len
* sizeof(WCHAR
) );
104 if (!screen_buffer
->font
.face_name
)
106 free( screen_buffer
);
110 memcpy( screen_buffer
->font
.face_name
, console
->active
->font
.face_name
,
111 screen_buffer
->font
.face_len
* sizeof(WCHAR
) );
116 screen_buffer
->max_width
= width
;
117 screen_buffer
->max_height
= height
;
118 screen_buffer
->win
.right
= width
- 1;
119 screen_buffer
->win
.bottom
= height
- 1;
120 screen_buffer
->attr
= FOREGROUND_BLUE
|FOREGROUND_GREEN
|FOREGROUND_RED
;
121 screen_buffer
->popup_attr
= 0xf5;
122 screen_buffer
->font
.weight
= FW_NORMAL
;
123 screen_buffer
->font
.pitch_family
= FIXED_PITCH
| FF_DONTCARE
;
126 if (wine_rb_put( &screen_buffer_map
, LongToPtr(id
), &screen_buffer
->entry
))
128 free( screen_buffer
);
129 ERR( "id %x already exists\n", id
);
133 if (!(screen_buffer
->data
= malloc( screen_buffer
->width
* screen_buffer
->height
*
134 sizeof(*screen_buffer
->data
) )))
136 destroy_screen_buffer( screen_buffer
);
140 /* clear the first row */
141 for (i
= 0; i
< screen_buffer
->width
; i
++) screen_buffer
->data
[i
] = empty_char_info
;
142 /* and copy it to all other rows */
143 for (i
= 1; i
< screen_buffer
->height
; i
++)
144 memcpy( &screen_buffer
->data
[i
* screen_buffer
->width
], screen_buffer
->data
,
145 screen_buffer
->width
* sizeof(char_info_t
) );
147 return screen_buffer
;
150 static BOOL
is_active( struct screen_buffer
*screen_buffer
)
152 return screen_buffer
== screen_buffer
->console
->active
;
155 static unsigned int get_tty_cp( struct console
*console
)
157 return console
->is_unix
? CP_UNIXCP
: CP_UTF8
;
160 static void tty_flush( struct console
*console
)
162 if (!console
->tty_output
|| !console
->tty_buffer_count
) return;
163 TRACE("%s\n", debugstr_an(console
->tty_buffer
, console
->tty_buffer_count
));
164 if (!WriteFile( console
->tty_output
, console
->tty_buffer
, console
->tty_buffer_count
,
166 WARN( "write failed: %lu\n", GetLastError() );
167 console
->tty_buffer_count
= 0;
170 static void tty_write( struct console
*console
, const char *buffer
, size_t size
)
172 if (!size
|| !console
->tty_output
) return;
173 if (console
->tty_buffer_count
+ size
> sizeof(console
->tty_buffer
))
174 tty_flush( console
);
175 if (console
->tty_buffer_count
+ size
<= sizeof(console
->tty_buffer
))
177 memcpy( console
->tty_buffer
+ console
->tty_buffer_count
, buffer
, size
);
178 console
->tty_buffer_count
+= size
;
182 assert( !console
->tty_buffer_count
);
183 if (!WriteFile( console
->tty_output
, buffer
, size
, NULL
, NULL
))
184 WARN( "write failed: %lu\n", GetLastError() );
188 static void *tty_alloc_buffer( struct console
*console
, size_t size
)
191 if (console
->tty_buffer_count
+ size
> sizeof(console
->tty_buffer
)) return NULL
;
192 ret
= console
->tty_buffer
+ console
->tty_buffer_count
;
193 console
->tty_buffer_count
+= size
;
197 static void hide_tty_cursor( struct console
*console
)
199 if (console
->tty_cursor_visible
)
201 tty_write( console
, "\x1b[?25l", 6 );
202 console
->tty_cursor_visible
= FALSE
;
206 static void set_tty_cursor( struct console
*console
, unsigned int x
, unsigned int y
)
210 if (console
->tty_cursor_x
== x
&& console
->tty_cursor_y
== y
) return;
212 if (!x
&& y
== console
->tty_cursor_y
+ 1) strcpy( buf
, "\r\n" );
213 else if (!x
&& y
== console
->tty_cursor_y
) strcpy( buf
, "\r" );
214 else if (y
== console
->tty_cursor_y
)
216 if (console
->tty_cursor_x
>= console
->active
->width
)
218 if (console
->is_unix
)
220 /* Unix will usually have the cursor at width-1 in this case. instead of depending
221 * on the exact behaviour, move the cursor to the first column and move forward
223 tty_write( console
, "\r", 1 );
224 console
->tty_cursor_x
= 0;
226 else if (console
->active
->mode
& ENABLE_WRAP_AT_EOL_OUTPUT
)
228 console
->tty_cursor_x
--;
230 if (console
->tty_cursor_x
== x
) return;
232 if (x
+ 1 == console
->tty_cursor_x
) strcpy( buf
, "\b" );
233 else if (x
> console
->tty_cursor_x
) sprintf( buf
, "\x1b[%uC", x
- console
->tty_cursor_x
);
234 else sprintf( buf
, "\x1b[%uD", console
->tty_cursor_x
- x
);
238 hide_tty_cursor( console
);
239 sprintf( buf
, "\x1b[%u;%uH", y
+ 1, x
+ 1);
241 else strcpy( buf
, "\x1b[H" );
242 console
->tty_cursor_x
= x
;
243 console
->tty_cursor_y
= y
;
244 tty_write( console
, buf
, strlen(buf
) );
247 static void set_tty_cursor_relative( struct console
*console
, unsigned int x
, unsigned int y
)
249 if (y
< console
->tty_cursor_y
)
252 sprintf( buf
, "\x1b[%uA", console
->tty_cursor_y
- y
);
253 tty_write( console
, buf
, strlen(buf
) );
254 console
->tty_cursor_y
= y
;
258 while (console
->tty_cursor_y
< y
)
260 console
->tty_cursor_x
= 0;
261 console
->tty_cursor_y
++;
262 tty_write( console
, "\r\n", 2 );
265 set_tty_cursor( console
, x
, y
);
268 static void set_tty_attr( struct console
*console
, unsigned int attr
)
272 if ((attr
& 0x0f) != (console
->tty_attr
& 0x0f))
274 if ((attr
& 0x0f) != 7)
277 if (attr
& FOREGROUND_BLUE
) n
+= 4;
278 if (attr
& FOREGROUND_GREEN
) n
+= 2;
279 if (attr
& FOREGROUND_RED
) n
+= 1;
280 if (attr
& FOREGROUND_INTENSITY
) n
+= 60;
281 sprintf(buf
, "\x1b[%um", n
);
282 tty_write( console
, buf
, strlen(buf
) );
284 else tty_write( console
, "\x1b[m", 3 );
287 if ((attr
& 0xf0) != (console
->tty_attr
& 0xf0) && attr
!= 7)
290 if (attr
& BACKGROUND_BLUE
) n
+= 4;
291 if (attr
& BACKGROUND_GREEN
) n
+= 2;
292 if (attr
& BACKGROUND_RED
) n
+= 1;
293 if (attr
& BACKGROUND_INTENSITY
) n
+= 60;
294 sprintf(buf
, "\x1b[%um", n
);
295 tty_write( console
, buf
, strlen(buf
) );
298 console
->tty_attr
= attr
;
301 static void tty_sync( struct console
*console
)
303 if (!console
->tty_output
) return;
305 if (console
->active
->cursor_visible
)
307 set_tty_cursor( console
, get_bounded_cursor_x( console
->active
), console
->active
->cursor_y
);
308 if (!console
->tty_cursor_visible
)
310 tty_write( console
, "\x1b[?25h", 6 ); /* show cursor */
311 console
->tty_cursor_visible
= TRUE
;
314 else if (console
->tty_cursor_visible
)
315 hide_tty_cursor( console
);
316 tty_flush( console
);
319 static void init_tty_output( struct console
*console
)
321 if (!console
->is_unix
)
323 /* initialize tty output, but don't flush */
324 tty_write( console
, "\x1b[2J", 4 ); /* clear screen */
325 set_tty_attr( console
, console
->active
->attr
);
326 tty_write( console
, "\x1b[H", 3 ); /* move cursor to (0,0) */
328 else console
->tty_attr
= empty_char_info
.attr
;
329 console
->tty_cursor_visible
= TRUE
;
332 /* no longer use relative cursor positioning (legacy API have been used) */
333 static void enter_absolute_mode( struct console
*console
)
335 console
->use_relative_cursor
= 0;
338 static void scroll_to_cursor( struct screen_buffer
*screen_buffer
)
340 unsigned int cursor_x
= get_bounded_cursor_x( screen_buffer
);
341 int w
= screen_buffer
->win
.right
- screen_buffer
->win
.left
+ 1;
342 int h
= screen_buffer
->win
.bottom
- screen_buffer
->win
.top
+ 1;
344 if (cursor_x
< screen_buffer
->win
.left
)
345 screen_buffer
->win
.left
= min( cursor_x
, screen_buffer
->width
- w
);
346 else if (cursor_x
> screen_buffer
->win
.right
)
347 screen_buffer
->win
.left
= max( cursor_x
, w
) - w
+ 1;
348 screen_buffer
->win
.right
= screen_buffer
->win
.left
+ w
- 1;
350 if (screen_buffer
->cursor_y
< screen_buffer
->win
.top
)
351 screen_buffer
->win
.top
= min( screen_buffer
->cursor_y
, screen_buffer
->height
- h
);
352 else if (screen_buffer
->cursor_y
> screen_buffer
->win
.bottom
)
353 screen_buffer
->win
.top
= max( screen_buffer
->cursor_y
, h
) - h
+ 1;
354 screen_buffer
->win
.bottom
= screen_buffer
->win
.top
+ h
- 1;
357 static void update_output( struct screen_buffer
*screen_buffer
, RECT
*rect
)
359 int x
, y
, size
, trailing_spaces
;
363 const unsigned int mask
= (1u << '\0') | (1u << '\b') | (1u << '\t') | (1u << '\n') | (1u << '\a') | (1u << '\r');
365 if (!is_active( screen_buffer
) || rect
->top
> rect
->bottom
|| rect
->right
< rect
->left
)
368 TRACE( "%s\n", wine_dbgstr_rect( rect
));
370 if (screen_buffer
->console
->window
)
372 update_window_region( screen_buffer
->console
, rect
);
375 if (!screen_buffer
->console
->tty_output
) return;
377 hide_tty_cursor( screen_buffer
->console
);
379 for (y
= rect
->top
; y
<= rect
->bottom
; y
++)
381 for (trailing_spaces
= 0; trailing_spaces
< screen_buffer
->width
; trailing_spaces
++)
383 ch
= &screen_buffer
->data
[(y
+ 1) * screen_buffer
->width
- trailing_spaces
- 1];
384 if (ch
->ch
!= ' ' || ch
->attr
!= 7) break;
386 if (trailing_spaces
< 4) trailing_spaces
= 0;
388 for (x
= rect
->left
; x
<= rect
->right
; x
++)
390 ch
= &screen_buffer
->data
[y
* screen_buffer
->width
+ x
];
391 set_tty_attr( screen_buffer
->console
, ch
->attr
);
392 set_tty_cursor( screen_buffer
->console
, x
, y
);
394 if (x
+ trailing_spaces
>= screen_buffer
->width
)
396 tty_write( screen_buffer
->console
, "\x1b[K", 3 );
400 if (screen_buffer
->console
->is_unix
&& wch
< L
' ' && mask
& (1u << wch
))
402 size
= WideCharToMultiByte( get_tty_cp( screen_buffer
->console
), 0,
403 &wch
, 1, buf
, sizeof(buf
), NULL
, NULL
);
404 tty_write( screen_buffer
->console
, buf
, size
);
405 screen_buffer
->console
->tty_cursor_x
++;
409 empty_update_rect( screen_buffer
, rect
);
412 static void new_line( struct screen_buffer
*screen_buffer
, RECT
*update_rect
)
416 assert( screen_buffer
->cursor_y
>= screen_buffer
->height
);
417 screen_buffer
->cursor_y
= screen_buffer
->height
- 1;
419 if (screen_buffer
->console
->tty_output
)
420 update_output( screen_buffer
, update_rect
);
422 SetRect( update_rect
, 0, 0, screen_buffer
->width
- 1, screen_buffer
->height
- 1 );
424 memmove( screen_buffer
->data
, screen_buffer
->data
+ screen_buffer
->width
,
425 screen_buffer
->width
* (screen_buffer
->height
- 1) * sizeof(*screen_buffer
->data
) );
426 for (i
= 0; i
< screen_buffer
->width
; i
++)
427 screen_buffer
->data
[screen_buffer
->width
* (screen_buffer
->height
- 1) + i
] = empty_char_info
;
428 if (is_active( screen_buffer
))
430 screen_buffer
->console
->tty_cursor_y
--;
431 if (screen_buffer
->console
->tty_cursor_y
!= screen_buffer
->height
- 2)
432 set_tty_cursor( screen_buffer
->console
, 0, screen_buffer
->height
- 2 );
433 set_tty_cursor( screen_buffer
->console
, 0, screen_buffer
->height
- 1 );
437 static void write_char( struct screen_buffer
*screen_buffer
, WCHAR ch
, RECT
*update_rect
, unsigned int *home_y
)
439 if (screen_buffer
->cursor_x
== screen_buffer
->width
)
441 screen_buffer
->cursor_x
= 0;
442 screen_buffer
->cursor_y
++;
444 if (screen_buffer
->cursor_y
== screen_buffer
->height
)
448 if (!*home_y
) return;
451 new_line( screen_buffer
, update_rect
);
454 screen_buffer
->data
[screen_buffer
->cursor_y
* screen_buffer
->width
+ screen_buffer
->cursor_x
].ch
= ch
;
455 screen_buffer
->data
[screen_buffer
->cursor_y
* screen_buffer
->width
+ screen_buffer
->cursor_x
].attr
= screen_buffer
->attr
;
456 update_rect
->left
= min( update_rect
->left
, screen_buffer
->cursor_x
);
457 update_rect
->top
= min( update_rect
->top
, screen_buffer
->cursor_y
);
458 update_rect
->right
= max( update_rect
->right
, screen_buffer
->cursor_x
);
459 update_rect
->bottom
= max( update_rect
->bottom
, screen_buffer
->cursor_y
);
460 screen_buffer
->cursor_x
++;
463 static NTSTATUS
read_complete( struct console
*console
, NTSTATUS status
, const void *buf
, size_t size
, int signal
)
465 SERVER_START_REQ( get_next_console_request
)
467 req
->handle
= wine_server_obj_handle( console
->server
);
468 req
->signal
= signal
;
470 req
->status
= status
;
471 if (console
->read_ioctl
== IOCTL_CONDRV_READ_CONSOLE_CONTROL
)
472 wine_server_add_data( req
, &console
->key_state
, sizeof(console
->key_state
) );
473 wine_server_add_data( req
, buf
, size
);
474 status
= wine_server_call( req
);
477 if (status
&& (console
->read_ioctl
|| status
!= STATUS_INVALID_HANDLE
)) ERR( "failed: %#lx\n", status
);
478 console
->signaled
= signal
;
479 console
->read_ioctl
= 0;
480 console
->pending_read
= 0;
484 static NTSTATUS
read_console_input( struct console
*console
, size_t out_size
)
486 size_t count
= min( out_size
/ sizeof(INPUT_RECORD
), console
->record_count
);
488 TRACE("count %Iu\n", count
);
490 read_complete( console
, STATUS_SUCCESS
, console
->records
, count
* sizeof(*console
->records
),
491 console
->record_count
> count
);
493 if (count
< console
->record_count
)
494 memmove( console
->records
, console
->records
+ count
,
495 (console
->record_count
- count
) * sizeof(*console
->records
) );
496 console
->record_count
-= count
;
497 return STATUS_SUCCESS
;
500 static void read_from_buffer( struct console
*console
, size_t out_size
)
502 size_t len
, read_len
= 0;
505 switch( console
->read_ioctl
)
507 case IOCTL_CONDRV_READ_CONSOLE
:
508 case IOCTL_CONDRV_READ_CONSOLE_CONTROL
:
509 out_size
= min( out_size
, console
->read_buffer_count
* sizeof(WCHAR
) );
510 read_complete( console
, STATUS_SUCCESS
, console
->read_buffer
, out_size
, console
->record_count
!= 0 );
511 read_len
= out_size
/ sizeof(WCHAR
);
513 case IOCTL_CONDRV_READ_FILE
:
515 while (read_len
< console
->read_buffer_count
&& len
< out_size
)
517 len
+= WideCharToMultiByte( console
->input_cp
, 0, console
->read_buffer
+ read_len
, 1, NULL
, 0, NULL
, NULL
);
522 if (!(buf
= malloc( len
)))
524 read_complete( console
, STATUS_NO_MEMORY
, NULL
, 0, console
->record_count
!= 0 );
527 WideCharToMultiByte( console
->input_cp
, 0, console
->read_buffer
, read_len
, buf
, len
, NULL
, NULL
);
529 len
= min( out_size
, len
);
530 read_complete( console
, STATUS_SUCCESS
, buf
, len
, console
->record_count
!= 0 );
535 if (read_len
< console
->read_buffer_count
)
537 memmove( console
->read_buffer
, console
->read_buffer
+ read_len
,
538 (console
->read_buffer_count
- read_len
) * sizeof(WCHAR
) );
540 if (!(console
->read_buffer_count
-= read_len
))
541 free( console
->read_buffer
);
544 static void append_input_history( struct console
*console
, const WCHAR
*str
, size_t len
)
546 struct history_line
*ptr
;
548 if (!console
->history_size
) return;
550 /* don't duplicate entry */
551 if (console
->history_mode
&& console
->history_index
&&
552 console
->history
[console
->history_index
- 1]->len
== len
&&
553 !memcmp( console
->history
[console
->history_index
- 1]->text
, str
, len
))
556 if (!(ptr
= malloc( offsetof( struct history_line
, text
[len
/ sizeof(WCHAR
)] )))) return;
558 memcpy( ptr
->text
, str
, len
);
560 if (console
->history_index
< console
->history_size
)
562 console
->history
[console
->history_index
++] = ptr
;
566 free( console
->history
[0]) ;
567 memmove( &console
->history
[0], &console
->history
[1],
568 (console
->history_size
- 1) * sizeof(*console
->history
) );
569 console
->history
[console
->history_size
- 1] = ptr
;
573 static void edit_line_update( struct console
*console
, unsigned int begin
, unsigned int length
)
575 struct edit_line
*ctx
= &console
->edit_line
;
577 ctx
->update_begin
= min( ctx
->update_begin
, begin
);
578 ctx
->update_end
= max( ctx
->update_end
, begin
+ length
- 1 );
581 static BOOL
edit_line_grow( struct console
*console
, size_t length
)
583 struct edit_line
*ctx
= &console
->edit_line
;
587 if (ctx
->len
+ length
< ctx
->size
) return TRUE
;
589 /* round up size to 32 byte-WCHAR boundary */
590 new_size
= (ctx
->len
+ length
+ 32) & ~31;
591 if (!(new_buf
= realloc( ctx
->buf
, sizeof(WCHAR
) * new_size
)))
593 ctx
->status
= STATUS_NO_MEMORY
;
597 ctx
->size
= new_size
;
601 static void edit_line_delete( struct console
*console
, int begin
, int end
)
603 struct edit_line
*ctx
= &console
->edit_line
;
604 unsigned int len
= end
- begin
;
606 edit_line_update( console
, begin
, ctx
->len
- begin
);
608 memmove( &ctx
->buf
[begin
], &ctx
->buf
[end
], (ctx
->len
- end
) * sizeof(WCHAR
));
610 edit_line_update( console
, 0, ctx
->len
);
611 ctx
->buf
[ctx
->len
] = 0;
614 static void edit_line_insert( struct console
*console
, const WCHAR
*str
, unsigned int len
)
616 struct edit_line
*ctx
= &console
->edit_line
;
617 unsigned int update_len
;
620 if (ctx
->insert_mode
)
622 if (!edit_line_grow( console
, len
)) return;
623 if (ctx
->len
> ctx
->cursor
)
624 memmove( &ctx
->buf
[ctx
->cursor
+ len
], &ctx
->buf
[ctx
->cursor
],
625 (ctx
->len
- ctx
->cursor
) * sizeof(WCHAR
) );
627 update_len
= ctx
->len
- ctx
->cursor
;
631 if (ctx
->cursor
+ len
> ctx
->len
)
633 if (!edit_line_grow( console
, (ctx
->cursor
+ len
) - ctx
->len
) )
635 ctx
->len
= ctx
->cursor
+ len
;
639 memcpy( &ctx
->buf
[ctx
->cursor
], str
, len
* sizeof(WCHAR
) );
640 ctx
->buf
[ctx
->len
] = 0;
641 edit_line_update( console
, ctx
->cursor
, update_len
);
645 static void edit_line_save_yank( struct console
*console
, unsigned int begin
, unsigned int end
)
647 struct edit_line
*ctx
= &console
->edit_line
;
648 unsigned int len
= end
- begin
;
649 if (len
<= 0) return;
652 ctx
->yanked
= malloc( (len
+ 1) * sizeof(WCHAR
) );
655 ctx
->status
= STATUS_NO_MEMORY
;
658 memcpy( ctx
->yanked
, &ctx
->buf
[begin
], len
* sizeof(WCHAR
) );
659 ctx
->yanked
[len
] = 0;
662 static int edit_line_left_word_transition( struct console
*console
, int offset
)
665 while (offset
>= 0 && !iswalnum( console
->edit_line
.buf
[offset
] )) offset
--;
666 while (offset
>= 0 && iswalnum( console
->edit_line
.buf
[offset
] )) offset
--;
667 if (offset
>= 0) offset
++;
668 return max( offset
, 0 );
671 static int edit_line_right_word_transition( struct console
*console
, int offset
)
674 while (offset
<= console
->edit_line
.len
&& iswalnum( console
->edit_line
.buf
[offset
] ))
676 while (offset
<= console
->edit_line
.len
&& !iswalnum( console
->edit_line
.buf
[offset
] ))
678 return min(offset
, console
->edit_line
.len
);
681 static WCHAR
*edit_line_history( struct console
*console
, unsigned int index
)
685 if (index
< console
->history_index
)
687 if ((ptr
= malloc( console
->history
[index
]->len
+ sizeof(WCHAR
) )))
689 memcpy( ptr
, console
->history
[index
]->text
, console
->history
[index
]->len
);
690 ptr
[console
->history
[index
]->len
/ sizeof(WCHAR
)] = 0;
693 else if(console
->edit_line
.current_history
)
695 if ((ptr
= malloc( (lstrlenW(console
->edit_line
.current_history
) + 1) * sizeof(WCHAR
) )))
696 lstrcpyW( ptr
, console
->edit_line
.current_history
);
701 static void edit_line_move_to_history( struct console
*console
, int index
)
703 struct edit_line
*ctx
= &console
->edit_line
;
704 WCHAR
*line
= edit_line_history(console
, index
);
705 size_t len
= line
? lstrlenW(line
) : 0;
707 /* save current line edition for recall when needed */
708 if (ctx
->history_index
== console
->history_index
)
710 free( ctx
->current_history
);
711 ctx
->current_history
= malloc( (ctx
->len
+ 1) * sizeof(WCHAR
) );
712 if (ctx
->current_history
)
714 memcpy( ctx
->current_history
, ctx
->buf
, (ctx
->len
+ 1) * sizeof(WCHAR
) );
719 ctx
->status
= STATUS_NO_MEMORY
;
724 /* need to clean also the screen if new string is shorter than old one */
725 edit_line_delete(console
, 0, ctx
->len
);
727 /* insert new string */
728 if (edit_line_grow(console
, len
+ 1))
730 edit_line_insert( console
, line
, len
);
731 ctx
->history_index
= index
;
736 static void edit_line_find_in_history( struct console
*console
)
738 struct edit_line
*ctx
= &console
->edit_line
;
739 int start_pos
= ctx
->history_index
;
740 unsigned int len
, oldoffset
;
743 if (!console
->history_index
) return;
744 if (ctx
->history_index
&& ctx
->history_index
== console
->history_index
)
747 ctx
->history_index
--;
752 line
= edit_line_history(console
, ctx
->history_index
);
754 if (ctx
->history_index
) ctx
->history_index
--;
755 else ctx
->history_index
= console
->history_index
- 1;
757 len
= lstrlenW(line
) + 1;
758 if (len
>= ctx
->cursor
&& !memcmp( ctx
->buf
, line
, ctx
->cursor
* sizeof(WCHAR
) ))
760 /* need to clean also the screen if new string is shorter than old one */
761 edit_line_delete(console
, 0, ctx
->len
);
763 if (edit_line_grow(console
, len
))
765 oldoffset
= ctx
->cursor
;
767 edit_line_insert( console
, line
, len
- 1 );
768 ctx
->cursor
= oldoffset
;
775 while (ctx
->history_index
!= start_pos
);
778 static void edit_line_move_left( struct console
*console
)
780 if (console
->edit_line
.cursor
> 0) console
->edit_line
.cursor
--;
783 static void edit_line_move_right( struct console
*console
)
785 struct edit_line
*ctx
= &console
->edit_line
;
786 if (ctx
->cursor
< ctx
->len
) ctx
->cursor
++;
789 static void edit_line_move_left_word( struct console
*console
)
791 console
->edit_line
.cursor
= edit_line_left_word_transition( console
, console
->edit_line
.cursor
);
794 static void edit_line_move_right_word( struct console
*console
)
796 console
->edit_line
.cursor
= edit_line_right_word_transition( console
, console
->edit_line
.cursor
);
799 static void edit_line_move_home( struct console
*console
)
801 console
->edit_line
.cursor
= 0;
804 static void edit_line_move_end( struct console
*console
)
806 console
->edit_line
.cursor
= console
->edit_line
.len
;
809 static void edit_line_set_mark( struct console
*console
)
811 console
->edit_line
.mark
= console
->edit_line
.cursor
;
814 static void edit_line_exchange_mark( struct console
*console
)
816 struct edit_line
*ctx
= &console
->edit_line
;
819 if (ctx
->mark
> ctx
->len
) return;
820 cursor
= ctx
->cursor
;
821 ctx
->cursor
= ctx
->mark
;
825 static void edit_line_copy_marked_zone( struct console
*console
)
827 struct edit_line
*ctx
= &console
->edit_line
;
828 unsigned int begin
, end
;
830 if (ctx
->mark
> ctx
->len
|| ctx
->mark
== ctx
->cursor
) return;
831 if (ctx
->mark
> ctx
->cursor
)
841 edit_line_save_yank( console
, begin
, end
);
844 static void edit_line_transpose_char( struct console
*console
)
846 struct edit_line
*ctx
= &console
->edit_line
;
849 if (!ctx
->cursor
|| ctx
->cursor
== ctx
->len
) return;
851 c
= ctx
->buf
[ctx
->cursor
];
852 ctx
->buf
[ctx
->cursor
] = ctx
->buf
[ctx
->cursor
- 1];
853 ctx
->buf
[ctx
->cursor
- 1] = c
;
855 edit_line_update( console
, ctx
->cursor
- 1, 2 );
859 static void edit_line_transpose_words( struct console
*console
)
861 struct edit_line
*ctx
= &console
->edit_line
;
862 unsigned int left_offset
= edit_line_left_word_transition( console
, ctx
->cursor
);
863 unsigned int right_offset
= edit_line_right_word_transition( console
, ctx
->cursor
);
864 if (left_offset
< ctx
->cursor
&& right_offset
> ctx
->cursor
)
866 unsigned int len_r
= right_offset
- ctx
->cursor
;
867 unsigned int len_l
= ctx
->cursor
- left_offset
;
868 char *tmp
= malloc( len_r
* sizeof(WCHAR
) );
871 ctx
->status
= STATUS_NO_MEMORY
;
875 memcpy( tmp
, &ctx
->buf
[ctx
->cursor
], len_r
* sizeof(WCHAR
) );
876 memmove( &ctx
->buf
[left_offset
+ len_r
], &ctx
->buf
[left_offset
],
877 len_l
* sizeof(WCHAR
) );
878 memcpy( &ctx
->buf
[left_offset
], tmp
, len_r
* sizeof(WCHAR
) );
881 edit_line_update( console
, left_offset
, len_l
+ len_r
);
882 ctx
->cursor
= right_offset
;
886 static void edit_line_lower_case_word( struct console
*console
)
888 struct edit_line
*ctx
= &console
->edit_line
;
889 unsigned int new_offset
= edit_line_right_word_transition( console
, ctx
->cursor
);
890 if (new_offset
!= ctx
->cursor
)
892 CharLowerBuffW( ctx
->buf
+ ctx
->cursor
, new_offset
- ctx
->cursor
+ 1 );
893 edit_line_update( console
, ctx
->cursor
, new_offset
- ctx
->cursor
+ 1 );
894 ctx
->cursor
= new_offset
;
898 static void edit_line_upper_case_word( struct console
*console
)
900 struct edit_line
*ctx
= &console
->edit_line
;
901 unsigned int new_offset
= edit_line_right_word_transition( console
, ctx
->cursor
);
902 if (new_offset
!= ctx
->cursor
)
904 CharUpperBuffW( ctx
->buf
+ ctx
->cursor
, new_offset
- ctx
->cursor
+ 1 );
905 edit_line_update( console
, ctx
->cursor
, new_offset
- ctx
->cursor
+ 1 );
906 ctx
->cursor
= new_offset
;
910 static void edit_line_capitalize_word( struct console
*console
)
912 struct edit_line
*ctx
= &console
->edit_line
;
913 unsigned int new_offset
= edit_line_right_word_transition( console
, ctx
->cursor
);
914 if (new_offset
!= ctx
->cursor
)
916 CharUpperBuffW( ctx
->buf
+ ctx
->cursor
, 1 );
917 CharLowerBuffW( ctx
->buf
+ ctx
->cursor
+ 1, new_offset
- ctx
->cursor
);
918 edit_line_update( console
, ctx
->cursor
, new_offset
- ctx
->cursor
+ 1 );
919 ctx
->cursor
= new_offset
;
923 static void edit_line_yank( struct console
*console
)
925 struct edit_line
*ctx
= &console
->edit_line
;
926 if (ctx
->yanked
) edit_line_insert( console
, ctx
->yanked
, wcslen(ctx
->yanked
) );
929 static void edit_line_kill_suffix( struct console
*console
)
931 struct edit_line
*ctx
= &console
->edit_line
;
932 edit_line_save_yank( console
, ctx
->cursor
, ctx
->len
);
933 edit_line_delete( console
, ctx
->cursor
, ctx
->len
);
936 static void edit_line_kill_prefix( struct console
*console
)
938 struct edit_line
*ctx
= &console
->edit_line
;
941 edit_line_save_yank( console
, 0, ctx
->cursor
);
942 edit_line_delete( console
, 0, ctx
->cursor
);
947 static void edit_line_kill_marked_zone( struct console
*console
)
949 struct edit_line
*ctx
= &console
->edit_line
;
950 unsigned int begin
, end
;
952 if (ctx
->mark
> ctx
->len
|| ctx
->mark
== ctx
->cursor
)
954 if (ctx
->mark
> ctx
->cursor
)
964 edit_line_save_yank( console
, begin
, end
);
965 edit_line_delete( console
, begin
, end
);
969 static void edit_line_delete_prev( struct console
*console
)
971 struct edit_line
*ctx
= &console
->edit_line
;
974 edit_line_delete( console
, ctx
->cursor
- 1, ctx
->cursor
);
979 static void edit_line_delete_char( struct console
*console
)
981 struct edit_line
*ctx
= &console
->edit_line
;
982 if (ctx
->cursor
< ctx
->len
)
983 edit_line_delete( console
, ctx
->cursor
, ctx
->cursor
+ 1 );
986 static void edit_line_delete_left_word( struct console
*console
)
988 struct edit_line
*ctx
= &console
->edit_line
;
989 unsigned int new_offset
= edit_line_left_word_transition( console
, ctx
->cursor
);
990 if (new_offset
!= ctx
->cursor
)
992 edit_line_delete( console
, new_offset
, ctx
->cursor
);
993 ctx
->cursor
= new_offset
;
997 static void edit_line_delete_right_word( struct console
*console
)
999 struct edit_line
*ctx
= &console
->edit_line
;
1000 unsigned int new_offset
= edit_line_right_word_transition( console
, ctx
->cursor
);
1001 if (new_offset
!= ctx
->cursor
)
1003 edit_line_delete( console
, ctx
->cursor
, new_offset
);
1007 static void edit_line_move_to_prev_hist( struct console
*console
)
1009 if (console
->edit_line
.history_index
)
1010 edit_line_move_to_history( console
, console
->edit_line
.history_index
- 1 );
1013 static void edit_line_move_to_next_hist( struct console
*console
)
1015 if (console
->edit_line
.history_index
< console
->history_index
)
1016 edit_line_move_to_history( console
, console
->edit_line
.history_index
+ 1 );
1019 static void edit_line_move_to_first_hist( struct console
*console
)
1021 if (console
->edit_line
.history_index
)
1022 edit_line_move_to_history( console
, 0 );
1025 static void edit_line_move_to_last_hist( struct console
*console
)
1027 if (console
->edit_line
.history_index
!= console
->history_index
)
1028 edit_line_move_to_history( console
, console
->history_index
);
1031 static void edit_line_redraw( struct console
*console
)
1033 if (console
->mode
& ENABLE_ECHO_INPUT
)
1034 edit_line_update( console
, 0, console
->edit_line
.len
);
1037 static void edit_line_toggle_insert( struct console
*console
)
1039 struct edit_line
*ctx
= &console
->edit_line
;
1040 ctx
->insert_key
= !ctx
->insert_key
;
1041 console
->active
->cursor_size
= ctx
->insert_key
? 100 : 25;
1044 static void edit_line_done( struct console
*console
)
1046 console
->edit_line
.status
= STATUS_SUCCESS
;
1049 struct edit_line_key_entry
1051 WCHAR val
; /* vk or unicode char */
1052 void (*func
)( struct console
*console
);
1055 struct edit_line_key_map
1057 DWORD key_state
; /* keyState (from INPUT_RECORD) to match */
1058 BOOL is_char
; /* check vk or char */
1059 const struct edit_line_key_entry
*entries
;
1062 #define CTRL(x) ((x) - '@')
1063 static const struct edit_line_key_entry std_key_map
[] =
1065 { VK_BACK
, edit_line_delete_prev
},
1066 { VK_RETURN
, edit_line_done
},
1067 { VK_DELETE
, edit_line_delete_char
},
1071 static const struct edit_line_key_entry emacs_key_map_ctrl
[] =
1073 { CTRL('@'), edit_line_set_mark
},
1074 { CTRL('A'), edit_line_move_home
},
1075 { CTRL('B'), edit_line_move_left
},
1076 { CTRL('D'), edit_line_delete_char
},
1077 { CTRL('E'), edit_line_move_end
},
1078 { CTRL('F'), edit_line_move_right
},
1079 { CTRL('H'), edit_line_delete_prev
},
1080 { CTRL('J'), edit_line_done
},
1081 { CTRL('K'), edit_line_kill_suffix
},
1082 { CTRL('L'), edit_line_redraw
},
1083 { CTRL('M'), edit_line_done
},
1084 { CTRL('N'), edit_line_move_to_next_hist
},
1085 { CTRL('P'), edit_line_move_to_prev_hist
},
1086 { CTRL('T'), edit_line_transpose_char
},
1087 { CTRL('W'), edit_line_kill_marked_zone
},
1088 { CTRL('X'), edit_line_exchange_mark
},
1089 { CTRL('Y'), edit_line_yank
},
1093 static const struct edit_line_key_entry emacs_key_map_alt
[] =
1095 { 0x7f, edit_line_delete_left_word
},
1096 { '<', edit_line_move_to_first_hist
},
1097 { '>', edit_line_move_to_last_hist
},
1098 { 'b', edit_line_move_left_word
},
1099 { 'c', edit_line_capitalize_word
},
1100 { 'd', edit_line_delete_right_word
},
1101 { 'f', edit_line_move_right_word
},
1102 { 'l', edit_line_lower_case_word
},
1103 { 't', edit_line_transpose_words
},
1104 { 'u', edit_line_upper_case_word
},
1105 { 'w', edit_line_copy_marked_zone
},
1109 static const struct edit_line_key_entry emacs_std_key_map
[] =
1111 { VK_PRIOR
, edit_line_move_to_prev_hist
},
1112 { VK_NEXT
, edit_line_move_to_next_hist
},
1113 { VK_END
, edit_line_move_end
},
1114 { VK_HOME
, edit_line_move_home
},
1115 { VK_RIGHT
, edit_line_move_right
},
1116 { VK_LEFT
, edit_line_move_left
},
1117 { VK_INSERT
, edit_line_toggle_insert
},
1121 static const struct edit_line_key_map emacs_key_map
[] =
1123 { 0, 0, std_key_map
},
1124 { 0, 0, emacs_std_key_map
},
1125 { RIGHT_ALT_PRESSED
, 1, emacs_key_map_alt
},
1126 { LEFT_ALT_PRESSED
, 1, emacs_key_map_alt
},
1127 { RIGHT_CTRL_PRESSED
, 1, emacs_key_map_ctrl
},
1128 { LEFT_CTRL_PRESSED
, 1, emacs_key_map_ctrl
},
1132 static const struct edit_line_key_entry win32_std_key_map
[] =
1134 { VK_LEFT
, edit_line_move_left
},
1135 { VK_RIGHT
, edit_line_move_right
},
1136 { VK_HOME
, edit_line_move_home
},
1137 { VK_END
, edit_line_move_end
},
1138 { VK_UP
, edit_line_move_to_prev_hist
},
1139 { VK_DOWN
, edit_line_move_to_next_hist
},
1140 { VK_INSERT
, edit_line_toggle_insert
},
1141 { VK_F8
, edit_line_find_in_history
},
1145 static const struct edit_line_key_entry win32_key_map_ctrl
[] =
1147 { VK_LEFT
, edit_line_move_left_word
},
1148 { VK_RIGHT
, edit_line_move_right_word
},
1149 { VK_END
, edit_line_kill_suffix
},
1150 { VK_HOME
, edit_line_kill_prefix
},
1151 { 'M', edit_line_done
},
1155 static const struct edit_line_key_map win32_key_map
[] =
1157 { 0, 0, std_key_map
},
1158 { SHIFT_PRESSED
, 0, std_key_map
},
1159 { 0, 0, win32_std_key_map
},
1160 { RIGHT_CTRL_PRESSED
, 0, win32_key_map_ctrl
},
1161 { LEFT_CTRL_PRESSED
, 0, win32_key_map_ctrl
},
1166 static unsigned int edit_line_string_width( const WCHAR
*str
, unsigned int len
)
1168 unsigned int i
, offset
= 0;
1169 for (i
= 0; i
< len
; i
++) offset
+= str
[i
] < ' ' ? 2 : 1;
1173 static void update_read_output( struct console
*console
, BOOL newline
)
1175 struct screen_buffer
*screen_buffer
= console
->active
;
1176 struct edit_line
*ctx
= &console
->edit_line
;
1177 int offset
= 0, j
, end_offset
;
1180 empty_update_rect( screen_buffer
, &update_rect
);
1182 if (ctx
->update_end
>= ctx
->update_begin
)
1184 TRACE( "update %d-%d %s\n", ctx
->update_begin
, ctx
->update_end
,
1185 debugstr_wn( ctx
->buf
+ ctx
->update_begin
, ctx
->update_end
- ctx
->update_begin
+ 1 ));
1187 hide_tty_cursor( screen_buffer
->console
);
1189 offset
= edit_line_string_width( ctx
->buf
, ctx
->update_begin
);
1190 screen_buffer
->cursor_x
= (ctx
->home_x
+ offset
) % screen_buffer
->width
;
1191 screen_buffer
->cursor_y
= ctx
->home_y
+ (ctx
->home_x
+ offset
) / screen_buffer
->width
;
1192 for (j
= ctx
->update_begin
; j
<= ctx
->update_end
; j
++)
1194 if (screen_buffer
->cursor_y
>= screen_buffer
->height
&& !ctx
->home_y
) break;
1195 if (j
>= ctx
->len
) break;
1196 if (ctx
->buf
[j
] < ' ')
1198 write_char( screen_buffer
, '^', &update_rect
, &ctx
->home_y
);
1199 write_char( screen_buffer
, '@' + ctx
->buf
[j
], &update_rect
, &ctx
->home_y
);
1204 write_char( screen_buffer
, ctx
->buf
[j
], &update_rect
, &ctx
->home_y
);
1208 end_offset
= ctx
->end_offset
;
1209 ctx
->end_offset
= offset
;
1212 /* clear trailing characters if buffer was shortened */
1213 while (offset
< end_offset
&& screen_buffer
->cursor_y
< screen_buffer
->height
)
1215 write_char( screen_buffer
, ' ', &update_rect
, &ctx
->home_y
);
1223 offset
= edit_line_string_width( ctx
->buf
, ctx
->len
);
1224 screen_buffer
->cursor_x
= 0;
1225 screen_buffer
->cursor_y
= ctx
->home_y
+ (ctx
->home_x
+ offset
) / screen_buffer
->width
;
1226 if (++screen_buffer
->cursor_y
>= screen_buffer
->height
)
1227 new_line( screen_buffer
, &update_rect
);
1231 offset
= edit_line_string_width( ctx
->buf
, ctx
->cursor
);
1232 screen_buffer
->cursor_y
= ctx
->home_y
+ (ctx
->home_x
+ offset
) / screen_buffer
->width
;
1233 if (screen_buffer
->cursor_y
< screen_buffer
->height
)
1235 screen_buffer
->cursor_x
= (ctx
->home_x
+ offset
) % screen_buffer
->width
;
1239 screen_buffer
->cursor_x
= screen_buffer
->width
- 1;
1240 screen_buffer
->cursor_y
= screen_buffer
->height
- 1;
1244 /* always try to use relative cursor positions in UNIX mode so that it works even if cursor
1245 * position is out of sync */
1246 if (update_rect
.left
<= update_rect
.right
&& update_rect
.top
<= update_rect
.bottom
)
1248 if (console
->is_unix
)
1249 set_tty_cursor_relative( screen_buffer
->console
, update_rect
.left
, update_rect
.top
);
1250 update_output( screen_buffer
, &update_rect
);
1251 scroll_to_cursor( screen_buffer
);
1253 if (console
->is_unix
)
1254 set_tty_cursor_relative( screen_buffer
->console
, screen_buffer
->cursor_x
, screen_buffer
->cursor_y
);
1255 tty_sync( screen_buffer
->console
);
1256 update_window_config( screen_buffer
->console
, TRUE
);
1259 /* can end on any ctrl-character: from 0x00 up to 0x1F) */
1260 #define FIRST_NON_CONTROL_CHAR (L' ')
1262 static NTSTATUS
process_console_input( struct console
*console
)
1264 struct edit_line
*ctx
= &console
->edit_line
;
1266 WCHAR ctrl_value
= FIRST_NON_CONTROL_CHAR
;
1267 unsigned int ctrl_keyvalue
= 0;
1269 switch (console
->read_ioctl
)
1271 case IOCTL_CONDRV_READ_INPUT
:
1272 if (console
->record_count
) read_console_input( console
, console
->pending_read
);
1273 return STATUS_SUCCESS
;
1274 case IOCTL_CONDRV_READ_CONSOLE
:
1275 case IOCTL_CONDRV_READ_CONSOLE_CONTROL
:
1276 case IOCTL_CONDRV_READ_FILE
:
1279 assert( !console
->read_ioctl
);
1280 if (console
->record_count
&& !console
->signaled
)
1281 read_complete( console
, STATUS_PENDING
, NULL
, 0, TRUE
); /* signal server */
1282 return STATUS_SUCCESS
;
1285 ctx
->update_begin
= ctx
->len
+ 1;
1286 ctx
->update_end
= 0;
1288 for (i
= 0; i
< console
->record_count
&& ctx
->status
== STATUS_PENDING
; i
++)
1290 void (*func
)( struct console
*console
) = NULL
;
1291 INPUT_RECORD ir
= console
->records
[i
];
1293 if (ir
.EventType
!= KEY_EVENT
|| !ir
.Event
.KeyEvent
.bKeyDown
) continue;
1295 TRACE( "key code=%02x scan=%02x char=%02x state=%08lx\n",
1296 ir
.Event
.KeyEvent
.wVirtualKeyCode
, ir
.Event
.KeyEvent
.wVirtualScanCode
,
1297 ir
.Event
.KeyEvent
.uChar
.UnicodeChar
, ir
.Event
.KeyEvent
.dwControlKeyState
);
1299 if (console
->mode
& ENABLE_LINE_INPUT
)
1301 const struct edit_line_key_entry
*entry
;
1302 const struct edit_line_key_map
*map
;
1305 /* mask out some bits which don't interest us */
1306 state
= ir
.Event
.KeyEvent
.dwControlKeyState
& ~(NUMLOCK_ON
|SCROLLLOCK_ON
|CAPSLOCK_ON
|ENHANCED_KEY
);
1308 if (ctx
->ctrl_mask
&&
1309 ir
.Event
.KeyEvent
.uChar
.UnicodeChar
&&
1310 ir
.Event
.KeyEvent
.uChar
.UnicodeChar
< FIRST_NON_CONTROL_CHAR
)
1312 if (ctx
->ctrl_mask
& (1u << ir
.Event
.KeyEvent
.uChar
.UnicodeChar
))
1314 ctrl_value
= ir
.Event
.KeyEvent
.uChar
.UnicodeChar
;
1315 ctrl_keyvalue
= ir
.Event
.KeyEvent
.dwControlKeyState
;
1316 ctx
->status
= STATUS_SUCCESS
;
1317 TRACE("Found ctrl char in mask: ^%lc %x\n", ir
.Event
.KeyEvent
.uChar
.UnicodeChar
+ '@', ctx
->ctrl_mask
);
1320 if (ir
.Event
.KeyEvent
.uChar
.UnicodeChar
== 10) continue;
1323 for (map
= console
->edition_mode
? emacs_key_map
: win32_key_map
; map
->entries
!= NULL
; map
++)
1325 if (map
->key_state
!= state
)
1329 for (entry
= &map
->entries
[0]; entry
->func
!= 0; entry
++)
1330 if (entry
->val
== ir
.Event
.KeyEvent
.uChar
.UnicodeChar
) break;
1334 for (entry
= &map
->entries
[0]; entry
->func
!= 0; entry
++)
1335 if (entry
->val
== ir
.Event
.KeyEvent
.wVirtualKeyCode
) break;
1346 ctx
->insert_mode
= ((console
->mode
& (ENABLE_INSERT_MODE
| ENABLE_EXTENDED_FLAGS
)) ==
1347 (ENABLE_INSERT_MODE
| ENABLE_EXTENDED_FLAGS
))
1350 if (func
) func( console
);
1351 else if (ir
.Event
.KeyEvent
.uChar
.UnicodeChar
)
1352 edit_line_insert( console
, &ir
.Event
.KeyEvent
.uChar
.UnicodeChar
, 1 );
1354 if (!(console
->mode
& ENABLE_LINE_INPUT
) && ctx
->status
== STATUS_PENDING
)
1356 if (console
->read_ioctl
== IOCTL_CONDRV_READ_FILE
)
1358 if (WideCharToMultiByte(console
->input_cp
, 0, ctx
->buf
, ctx
->len
, NULL
, 0, NULL
, NULL
)
1359 >= console
->pending_read
)
1360 ctx
->status
= STATUS_SUCCESS
;
1362 else if (ctx
->len
>= console
->pending_read
/ sizeof(WCHAR
))
1363 ctx
->status
= STATUS_SUCCESS
;
1367 if (console
->record_count
> i
) memmove( console
->records
, console
->records
+ i
,
1368 (console
->record_count
- i
) * sizeof(*console
->records
) );
1369 console
->record_count
-= i
;
1371 if (ctx
->status
== STATUS_PENDING
&& !(console
->mode
& ENABLE_LINE_INPUT
) && ctx
->len
)
1372 ctx
->status
= STATUS_SUCCESS
;
1374 if (console
->mode
& ENABLE_ECHO_INPUT
) update_read_output( console
, !ctx
->status
&& ctrl_value
== FIRST_NON_CONTROL_CHAR
);
1375 if (ctx
->status
== STATUS_PENDING
) return STATUS_SUCCESS
;
1377 if (!ctx
->status
&& (console
->mode
& ENABLE_LINE_INPUT
))
1379 if (ctrl_value
< FIRST_NON_CONTROL_CHAR
)
1381 edit_line_insert( console
, &ctrl_value
, 1 );
1382 console
->key_state
= ctrl_keyvalue
;
1386 if (ctx
->len
) append_input_history( console
, ctx
->buf
, ctx
->len
* sizeof(WCHAR
) );
1387 if (edit_line_grow(console
, 2))
1389 ctx
->buf
[ctx
->len
++] = '\r';
1390 ctx
->buf
[ctx
->len
++] = '\n';
1391 ctx
->buf
[ctx
->len
] = 0;
1394 TRACE( "return %s\n", debugstr_wn( ctx
->buf
, ctx
->len
));
1397 console
->read_buffer
= ctx
->buf
;
1398 console
->read_buffer_count
= ctx
->len
;
1399 console
->read_buffer_size
= ctx
->size
;
1401 if (ctx
->status
) read_complete( console
, ctx
->status
, NULL
, 0, console
->record_count
);
1402 else read_from_buffer( console
, console
->pending_read
);
1405 free( ctx
->yanked
);
1406 free( ctx
->current_history
);
1407 memset( &console
->edit_line
, 0, sizeof(console
->edit_line
) );
1408 return STATUS_SUCCESS
;
1411 static NTSTATUS
read_console( struct console
*console
, unsigned int ioctl
, size_t out_size
,
1412 const WCHAR
*initial
, unsigned int initial_len
, unsigned int ctrl_mask
)
1414 struct edit_line
*ctx
= &console
->edit_line
;
1417 if (out_size
> INT_MAX
)
1419 read_complete( console
, STATUS_NO_MEMORY
, NULL
, 0, console
->record_count
);
1420 return STATUS_NO_MEMORY
;
1423 console
->read_ioctl
= ioctl
;
1424 console
->key_state
= 0;
1425 if (!out_size
|| console
->read_buffer_count
)
1427 read_from_buffer( console
, out_size
);
1428 return STATUS_SUCCESS
;
1431 ctx
->history_index
= console
->history_index
;
1432 ctx
->home_x
= console
->active
->cursor_x
;
1433 ctx
->home_y
= console
->active
->cursor_y
;
1434 ctx
->status
= STATUS_PENDING
;
1435 if (initial_len
&& edit_line_grow( console
, initial_len
+ 1 ))
1437 unsigned offset
= edit_line_string_width( initial
, initial_len
);
1438 if (offset
> ctx
->home_x
)
1441 offset
-= ctx
->home_x
+ 1;
1442 deltay
= offset
/ console
->active
->width
+ 1;
1443 if (ctx
->home_y
>= deltay
)
1444 ctx
->home_y
-= deltay
;
1448 FIXME("Support for negative ordinates is missing\n");
1450 ctx
->home_x
= console
->active
->width
- 1 - (offset
% console
->active
->width
);
1453 ctx
->home_x
-= offset
;
1454 ctx
->cursor
= initial_len
;
1455 memcpy( ctx
->buf
, initial
, initial_len
* sizeof(WCHAR
) );
1456 ctx
->buf
[initial_len
] = 0;
1457 ctx
->len
= initial_len
;
1458 ctx
->end_offset
= initial_len
;
1460 else if (edit_line_grow( console
, 1 )) ctx
->buf
[0] = 0;
1461 ctx
->ctrl_mask
= ctrl_mask
;
1463 console
->pending_read
= out_size
;
1464 return process_console_input( console
);
1467 static BOOL
map_to_ctrlevent( struct console
*console
, const INPUT_RECORD
*record
,
1468 unsigned int* event
)
1470 if (record
->EventType
== KEY_EVENT
)
1472 if (record
->Event
.KeyEvent
.uChar
.UnicodeChar
== 'C' - 64 &&
1473 !(record
->Event
.KeyEvent
.dwControlKeyState
& ENHANCED_KEY
))
1475 *event
= CTRL_C_EVENT
;
1478 /* we want to get ctrl-pause/break, but it's already translated by user32 into VK_CANCEL */
1479 if (record
->Event
.KeyEvent
.uChar
.UnicodeChar
== 0 &&
1480 record
->Event
.KeyEvent
.wVirtualKeyCode
== VK_CANCEL
&&
1481 record
->Event
.KeyEvent
.dwControlKeyState
== LEFT_CTRL_PRESSED
)
1483 *event
= CTRL_BREAK_EVENT
;
1490 /* add input events to a console input queue */
1491 NTSTATUS
write_console_input( struct console
*console
, const INPUT_RECORD
*records
,
1492 unsigned int count
, BOOL flush
)
1494 TRACE( "%u\n", count
);
1496 if (!count
) return STATUS_SUCCESS
;
1497 if (console
->record_count
+ count
> console
->record_size
)
1499 INPUT_RECORD
*new_rec
;
1500 if (!(new_rec
= realloc( console
->records
, (console
->record_size
* 2 + count
) * sizeof(INPUT_RECORD
) )))
1501 return STATUS_NO_MEMORY
;
1502 console
->records
= new_rec
;
1503 console
->record_size
= console
->record_size
* 2 + count
;
1505 memcpy( console
->records
+ console
->record_count
, records
, count
* sizeof(INPUT_RECORD
) );
1507 if (console
->mode
& ENABLE_PROCESSED_INPUT
)
1514 if (map_to_ctrlevent(console
, &records
[i
], &event
))
1517 memcpy( &console
->records
[console
->record_count
+ i
],
1518 &console
->records
[console
->record_count
+ i
+ 1],
1519 (count
- i
- 1) * sizeof(INPUT_RECORD
) );
1521 if (records
[i
].Event
.KeyEvent
.bKeyDown
)
1523 struct condrv_ctrl_event ctrl_event
;
1526 ctrl_event
.event
= event
;
1527 ctrl_event
.group_id
= 0;
1528 NtDeviceIoControlFile( console
->server
, NULL
, NULL
, NULL
, &io
, IOCTL_CONDRV_CTRL_EVENT
,
1529 &ctrl_event
, sizeof(ctrl_event
), NULL
, 0 );
1536 console
->record_count
+= count
;
1537 return flush
? process_console_input( console
) : STATUS_SUCCESS
;
1540 static void set_key_input_record( INPUT_RECORD
*record
, WCHAR ch
, unsigned int vk
, BOOL is_down
, unsigned int ctrl_state
)
1542 record
->EventType
= KEY_EVENT
;
1543 record
->Event
.KeyEvent
.bKeyDown
= is_down
;
1544 record
->Event
.KeyEvent
.wRepeatCount
= 1;
1545 record
->Event
.KeyEvent
.uChar
.UnicodeChar
= ch
;
1546 record
->Event
.KeyEvent
.wVirtualKeyCode
= vk
;
1547 record
->Event
.KeyEvent
.wVirtualScanCode
= MapVirtualKeyW( vk
, MAPVK_VK_TO_VSC
);
1548 record
->Event
.KeyEvent
.dwControlKeyState
= ctrl_state
;
1551 static NTSTATUS
key_press( struct console
*console
, WCHAR ch
, unsigned int vk
, unsigned int ctrl_state
)
1553 INPUT_RECORD records
[8];
1554 unsigned int count
= 0, ctrl
= 0;
1556 if (ctrl_state
& SHIFT_PRESSED
)
1558 ctrl
|= SHIFT_PRESSED
;
1559 set_key_input_record( &records
[count
++], 0, VK_SHIFT
, TRUE
, ctrl
);
1561 if (ctrl_state
& LEFT_ALT_PRESSED
)
1563 ctrl
|= LEFT_ALT_PRESSED
;
1564 set_key_input_record( &records
[count
++], 0, VK_MENU
, TRUE
, ctrl
);
1566 if (ctrl_state
& LEFT_CTRL_PRESSED
)
1568 ctrl
|= LEFT_CTRL_PRESSED
;
1569 set_key_input_record( &records
[count
++], 0, VK_CONTROL
, TRUE
, ctrl
);
1572 set_key_input_record( &records
[count
++], ch
, vk
, TRUE
, ctrl
);
1573 set_key_input_record( &records
[count
++], ch
, vk
, FALSE
, ctrl
);
1575 if (ctrl
& LEFT_CTRL_PRESSED
)
1577 ctrl
&= ~LEFT_CTRL_PRESSED
;
1578 set_key_input_record( &records
[count
++], 0, VK_CONTROL
, FALSE
, ctrl
);
1580 if (ctrl
& LEFT_ALT_PRESSED
)
1582 ctrl
&= ~LEFT_ALT_PRESSED
;
1583 set_key_input_record( &records
[count
++], 0, VK_MENU
, FALSE
, ctrl
);
1585 if (ctrl
& SHIFT_PRESSED
)
1587 ctrl
&= ~SHIFT_PRESSED
;
1588 set_key_input_record( &records
[count
++], 0, VK_SHIFT
, FALSE
, ctrl
);
1591 return write_console_input( console
, records
, count
, FALSE
);
1594 static void char_key_press( struct console
*console
, WCHAR ch
, unsigned int ctrl
)
1596 unsigned int vk
= VkKeyScanW( ch
);
1597 if (vk
== ~0) vk
= 0;
1598 if (vk
& 0x0100) ctrl
|= SHIFT_PRESSED
;
1599 if (vk
& 0x0200) ctrl
|= LEFT_CTRL_PRESSED
;
1600 if (vk
& 0x0400) ctrl
|= LEFT_ALT_PRESSED
;
1602 key_press( console
, ch
, vk
, ctrl
);
1605 static unsigned int escape_char_to_vk( WCHAR ch
, unsigned int *ctrl
, WCHAR
*outuch
)
1607 if (ctrl
) *ctrl
= 0;
1608 if (outuch
) *outuch
= '\0';
1612 case 'A': return VK_UP
;
1613 case 'B': return VK_DOWN
;
1614 case 'C': return VK_RIGHT
;
1615 case 'D': return VK_LEFT
;
1616 case 'H': return VK_HOME
;
1617 case 'F': return VK_END
;
1618 case 'P': return VK_F1
;
1619 case 'Q': return VK_F2
;
1620 case 'R': return VK_F3
;
1621 case 'S': return VK_F4
;
1622 case 'Z': if (ctrl
&& outuch
) {*ctrl
= SHIFT_PRESSED
; *outuch
= '\t'; return VK_TAB
;}
1628 static unsigned int escape_number_to_vk( unsigned int n
)
1632 case 2: return VK_INSERT
;
1633 case 3: return VK_DELETE
;
1634 case 5: return VK_PRIOR
;
1635 case 6: return VK_NEXT
;
1636 case 15: return VK_F5
;
1637 case 17: return VK_F6
;
1638 case 18: return VK_F7
;
1639 case 19: return VK_F8
;
1640 case 20: return VK_F9
;
1641 case 21: return VK_F10
;
1642 case 23: return VK_F11
;
1643 case 24: return VK_F12
;
1648 static unsigned int convert_modifiers( unsigned int n
)
1650 unsigned int ctrl
= 0;
1651 if (!n
|| n
> 16) return 0;
1653 if (n
& 1) ctrl
|= SHIFT_PRESSED
;
1654 if (n
& 2) ctrl
|= LEFT_ALT_PRESSED
;
1655 if (n
& 4) ctrl
|= LEFT_CTRL_PRESSED
;
1659 static unsigned int process_csi_sequence( struct console
*console
, const WCHAR
*buf
, size_t size
)
1661 unsigned int n
, count
= 0, params
[8], params_cnt
= 0, vk
, ctrl
;
1667 while (count
< size
&& '0' <= buf
[count
] && buf
[count
] <= '9')
1668 n
= n
* 10 + buf
[count
++] - '0';
1669 if (params_cnt
< ARRAY_SIZE(params
)) params
[params_cnt
++] = n
;
1670 else FIXME( "too many params, skipping %u\n", n
);
1671 if (count
== size
) return 0;
1672 if (buf
[count
] != ';') break;
1673 if (++count
== size
) return 0;
1676 if ((vk
= escape_char_to_vk( buf
[count
], &ctrl
, &outuch
)))
1678 key_press( console
, outuch
, vk
, params_cnt
>= 2 ? convert_modifiers( params
[1] ) : ctrl
);
1685 vk
= escape_number_to_vk( params
[0] );
1686 key_press( console
, 0, vk
, params_cnt
== 2 ? convert_modifiers( params
[1] ) : 0 );
1690 FIXME( "unhandled sequence %s\n", debugstr_wn( buf
, size
));
1695 static unsigned int process_input_escape( struct console
*console
, const WCHAR
*buf
, size_t size
)
1697 unsigned int vk
= 0, count
= 0, nlen
;
1701 key_press( console
, 0, VK_ESCAPE
, 0 );
1708 if (++count
== size
) break;
1709 if ((nlen
= process_csi_sequence( console
, buf
+ 1, size
- 1 ))) return count
+ nlen
;
1713 if (++count
== size
) break;
1714 vk
= escape_char_to_vk( buf
[1], NULL
, NULL
);
1717 key_press( console
, 0, vk
, 0 );
1722 char_key_press( console
, buf
[0], LEFT_ALT_PRESSED
);
1726 static DWORD WINAPI
tty_input( void *param
)
1728 struct console
*console
= param
;
1731 char read_buf
[4096];
1737 if (console
->is_unix
)
1739 unsigned int h
= condrv_handle( console
->tty_input
);
1740 status
= NtDeviceIoControlFile( console
->server
, NULL
, NULL
, NULL
, &io
, IOCTL_CONDRV_SETUP_INPUT
,
1741 &h
, sizeof(h
), NULL
, 0 );
1742 if (status
) ERR( "input setup failed: %#lx\n", status
);
1745 event
= CreateEventW( NULL
, TRUE
, FALSE
, NULL
);
1749 status
= NtReadFile( console
->tty_input
, event
, NULL
, NULL
, &io
, read_buf
, sizeof(read_buf
), NULL
, NULL
);
1750 if (status
== STATUS_PENDING
)
1752 if ((status
= NtWaitForSingleObject( event
, FALSE
, NULL
))) break;
1757 EnterCriticalSection( &console_section
);
1758 signaled
= console
->record_count
!= 0;
1760 /* FIXME: Handle partial char read */
1761 count
= MultiByteToWideChar( get_tty_cp( console
), 0, read_buf
, io
.Information
, buf
, ARRAY_SIZE(buf
) );
1763 TRACE( "%s\n", debugstr_wn(buf
, count
) );
1765 for (i
= 0; i
< count
; i
++)
1770 case 3: /* end of text */
1771 LeaveCriticalSection( &console_section
);
1774 key_press( console
, '\n', VK_RETURN
, LEFT_CTRL_PRESSED
);
1777 key_press( console
, ch
, 'H', LEFT_CTRL_PRESSED
);
1780 i
+= process_input_escape( console
, buf
+ i
+ 1, count
- i
- 1 );
1782 case 0x1c: /* map ctrl-\ unix-ism into ctrl-break/pause windows-ism for unix consoles */
1783 if (console
->is_unix
)
1784 key_press( console
, 0, VK_CANCEL
, LEFT_CTRL_PRESSED
);
1786 char_key_press( console
, ch
, 0 );
1789 key_press( console
, '\b', VK_BACK
, 0 );
1792 char_key_press( console
, ch
, 0 );
1796 process_console_input( console
);
1797 if (!signaled
&& console
->record_count
)
1799 assert( !console
->read_ioctl
);
1800 read_complete( console
, STATUS_SUCCESS
, NULL
, 0, TRUE
); /* signal console */
1802 LeaveCriticalSection( &console_section
);
1805 TRACE( "NtReadFile failed: %#lx\n", status
);
1808 EnterCriticalSection( &console_section
);
1809 if (console
->read_ioctl
) read_complete( console
, status
, NULL
, 0, FALSE
);
1810 if (console
->is_unix
)
1813 status
= NtDeviceIoControlFile( console
->server
, NULL
, NULL
, NULL
, &io
, IOCTL_CONDRV_SETUP_INPUT
,
1814 &h
, sizeof(h
), NULL
, 0 );
1815 if (status
) ERR( "input restore failed: %#lx\n", status
);
1817 CloseHandle( console
->input_thread
);
1818 console
->input_thread
= NULL
;
1819 LeaveCriticalSection( &console_section
);
1824 static BOOL
ensure_tty_input_thread( struct console
*console
)
1826 if (!console
->tty_input
) return TRUE
;
1827 if (!console
->input_thread
)
1828 console
->input_thread
= CreateThread( NULL
, 0, tty_input
, console
, 0, NULL
);
1829 return console
->input_thread
!= NULL
;
1832 static NTSTATUS
screen_buffer_activate( struct screen_buffer
*screen_buffer
)
1835 TRACE( "%p\n", screen_buffer
);
1836 screen_buffer
->console
->active
= screen_buffer
;
1837 SetRect( &update_rect
, 0, 0, screen_buffer
->width
- 1, screen_buffer
->height
- 1 );
1838 update_output( screen_buffer
, &update_rect
);
1839 tty_sync( screen_buffer
->console
);
1840 update_window_config( screen_buffer
->console
, FALSE
);
1841 return STATUS_SUCCESS
;
1844 static NTSTATUS
get_output_info( struct screen_buffer
*screen_buffer
, size_t *out_size
)
1846 struct condrv_output_info
*info
;
1848 *out_size
= min( *out_size
, sizeof(*info
) + screen_buffer
->font
.face_len
* sizeof(WCHAR
) );
1849 if (!(info
= alloc_ioctl_buffer( *out_size
))) return STATUS_NO_MEMORY
;
1851 info
->cursor_size
= screen_buffer
->cursor_size
;
1852 info
->cursor_visible
= screen_buffer
->cursor_visible
;
1853 info
->cursor_x
= get_bounded_cursor_x( screen_buffer
);
1854 info
->cursor_y
= screen_buffer
->cursor_y
;
1855 info
->width
= screen_buffer
->width
;
1856 info
->height
= screen_buffer
->height
;
1857 info
->attr
= screen_buffer
->attr
;
1858 info
->popup_attr
= screen_buffer
->popup_attr
;
1859 info
->win_left
= screen_buffer
->win
.left
;
1860 info
->win_top
= screen_buffer
->win
.top
;
1861 info
->win_right
= screen_buffer
->win
.right
;
1862 info
->win_bottom
= screen_buffer
->win
.bottom
;
1863 info
->max_width
= screen_buffer
->max_width
;
1864 info
->max_height
= screen_buffer
->max_height
;
1865 info
->font_width
= screen_buffer
->font
.width
;
1866 info
->font_height
= screen_buffer
->font
.height
;
1867 info
->font_weight
= screen_buffer
->font
.weight
;
1868 info
->font_pitch_family
= screen_buffer
->font
.pitch_family
;
1869 memcpy( info
->color_map
, screen_buffer
->color_map
, sizeof(info
->color_map
) );
1870 if (*out_size
> sizeof(*info
)) memcpy( info
+ 1, screen_buffer
->font
.face_name
, *out_size
- sizeof(*info
) );
1872 TRACE( "%p cursor_size=%u cursor_visible=%x cursor=(%u,%u) width=%u height=%u win=%s attr=%x popup_attr=%x"
1873 " font_width=%u font_height=%u %s\n", screen_buffer
, info
->cursor_size
, info
->cursor_visible
,
1874 info
->cursor_x
, info
->cursor_y
, info
->width
, info
->height
, wine_dbgstr_rect(&screen_buffer
->win
),
1875 info
->attr
, info
->popup_attr
, info
->font_width
, info
->font_height
,
1876 debugstr_wn( (const WCHAR
*)(info
+ 1), (*out_size
- sizeof(*info
)) / sizeof(WCHAR
) ) );
1877 return STATUS_SUCCESS
;
1880 void notify_screen_buffer_size( struct screen_buffer
*screen_buffer
)
1882 if (is_active( screen_buffer
) && screen_buffer
->console
->mode
& ENABLE_WINDOW_INPUT
)
1885 ir
.EventType
= WINDOW_BUFFER_SIZE_EVENT
;
1886 ir
.Event
.WindowBufferSizeEvent
.dwSize
.X
= screen_buffer
->width
;
1887 ir
.Event
.WindowBufferSizeEvent
.dwSize
.Y
= screen_buffer
->height
;
1888 write_console_input( screen_buffer
->console
, &ir
, 1, TRUE
);
1892 NTSTATUS
change_screen_buffer_size( struct screen_buffer
*screen_buffer
, int new_width
, int new_height
)
1894 int i
, old_width
, old_height
, copy_width
, copy_height
;
1895 char_info_t
*new_data
;
1897 if (!(new_data
= malloc( new_width
* new_height
* sizeof(*new_data
) ))) return STATUS_NO_MEMORY
;
1899 old_width
= screen_buffer
->width
;
1900 old_height
= screen_buffer
->height
;
1901 copy_width
= min( old_width
, new_width
);
1902 copy_height
= min( old_height
, new_height
);
1904 /* copy all the rows */
1905 for (i
= 0; i
< copy_height
; i
++)
1907 memcpy( &new_data
[i
* new_width
], &screen_buffer
->data
[i
* old_width
],
1908 copy_width
* sizeof(char_info_t
) );
1911 /* clear the end of each row */
1912 if (new_width
> old_width
)
1914 /* fill first row */
1915 for (i
= old_width
; i
< new_width
; i
++) new_data
[i
] = empty_char_info
;
1916 /* and blast it to the other rows */
1917 for (i
= 1; i
< copy_height
; i
++)
1918 memcpy( &new_data
[i
* new_width
+ old_width
], &new_data
[old_width
],
1919 (new_width
- old_width
) * sizeof(char_info_t
) );
1922 /* clear remaining rows */
1923 if (new_height
> old_height
)
1925 /* fill first row */
1926 for (i
= 0; i
< new_width
; i
++) new_data
[old_height
* new_width
+ i
] = empty_char_info
;
1927 /* and blast it to the other rows */
1928 for (i
= old_height
+1; i
< new_height
; i
++)
1929 memcpy( &new_data
[i
* new_width
], &new_data
[old_height
* new_width
],
1930 new_width
* sizeof(char_info_t
) );
1932 free( screen_buffer
->data
);
1933 screen_buffer
->data
= new_data
;
1934 screen_buffer
->width
= new_width
;
1935 screen_buffer
->height
= new_height
;
1936 return STATUS_SUCCESS
;
1939 static NTSTATUS
set_output_info( struct screen_buffer
*screen_buffer
,
1940 const struct condrv_output_info_params
*params
, size_t in_size
)
1942 const struct condrv_output_info
*info
= ¶ms
->info
;
1945 TRACE( "%p\n", screen_buffer
);
1947 if (params
->mask
& SET_CONSOLE_OUTPUT_INFO_CURSOR_GEOM
)
1949 if (info
->cursor_size
< 1 || info
->cursor_size
> 100) return STATUS_INVALID_PARAMETER
;
1951 screen_buffer
->cursor_size
= info
->cursor_size
;
1952 screen_buffer
->cursor_visible
= !!info
->cursor_visible
;
1954 if (params
->mask
& SET_CONSOLE_OUTPUT_INFO_CURSOR_POS
)
1956 if (info
->cursor_x
< 0 || info
->cursor_x
>= screen_buffer
->width
||
1957 info
->cursor_y
< 0 || info
->cursor_y
>= screen_buffer
->height
)
1959 return STATUS_INVALID_PARAMETER
;
1962 if (screen_buffer
->cursor_x
!= info
->cursor_x
|| screen_buffer
->cursor_y
!= info
->cursor_y
)
1964 struct console
*console
= screen_buffer
->console
;
1965 screen_buffer
->cursor_x
= info
->cursor_x
;
1966 screen_buffer
->cursor_y
= info
->cursor_y
;
1967 if (console
->use_relative_cursor
)
1968 set_tty_cursor_relative( console
, screen_buffer
->cursor_x
, screen_buffer
->cursor_y
);
1969 scroll_to_cursor( screen_buffer
);
1972 if (params
->mask
& SET_CONSOLE_OUTPUT_INFO_SIZE
)
1974 enter_absolute_mode( screen_buffer
->console
);
1975 /* new screen-buffer cannot be smaller than actual window */
1976 if (info
->width
< screen_buffer
->win
.right
- screen_buffer
->win
.left
+ 1 ||
1977 info
->height
< screen_buffer
->win
.bottom
- screen_buffer
->win
.top
+ 1)
1979 return STATUS_INVALID_PARAMETER
;
1981 /* FIXME: there are also some basic minimum and max size to deal with */
1982 if ((status
= change_screen_buffer_size( screen_buffer
, info
->width
, info
->height
))) return status
;
1984 /* scroll window to display sb */
1985 if (screen_buffer
->win
.right
>= info
->width
)
1987 screen_buffer
->win
.right
-= screen_buffer
->win
.left
;
1988 screen_buffer
->win
.left
= 0;
1990 if (screen_buffer
->win
.bottom
>= info
->height
)
1992 screen_buffer
->win
.bottom
-= screen_buffer
->win
.top
;
1993 screen_buffer
->win
.top
= 0;
1995 if (screen_buffer
->cursor_x
>= info
->width
) screen_buffer
->cursor_x
= info
->width
- 1;
1996 if (screen_buffer
->cursor_y
>= info
->height
) screen_buffer
->cursor_y
= info
->height
- 1;
1998 notify_screen_buffer_size( screen_buffer
);
2000 if (params
->mask
& SET_CONSOLE_OUTPUT_INFO_ATTR
)
2002 screen_buffer
->attr
= info
->attr
;
2004 if (params
->mask
& SET_CONSOLE_OUTPUT_INFO_POPUP_ATTR
)
2006 screen_buffer
->popup_attr
= info
->popup_attr
;
2008 if (params
->mask
& SET_CONSOLE_OUTPUT_INFO_DISPLAY_WINDOW
)
2010 enter_absolute_mode( screen_buffer
->console
);
2011 if (info
->win_left
< 0 || info
->win_left
> info
->win_right
||
2012 info
->win_right
>= screen_buffer
->width
||
2013 info
->win_top
< 0 || info
->win_top
> info
->win_bottom
||
2014 info
->win_bottom
>= screen_buffer
->height
)
2016 return STATUS_INVALID_PARAMETER
;
2018 if (screen_buffer
->win
.left
!= info
->win_left
|| screen_buffer
->win
.top
!= info
->win_top
||
2019 screen_buffer
->win
.right
!= info
->win_right
|| screen_buffer
->win
.bottom
!= info
->win_bottom
)
2021 screen_buffer
->win
.left
= info
->win_left
;
2022 screen_buffer
->win
.top
= info
->win_top
;
2023 screen_buffer
->win
.right
= info
->win_right
;
2024 screen_buffer
->win
.bottom
= info
->win_bottom
;
2027 if (params
->mask
& SET_CONSOLE_OUTPUT_INFO_MAX_SIZE
)
2029 enter_absolute_mode( screen_buffer
->console
);
2030 screen_buffer
->max_width
= info
->max_width
;
2031 screen_buffer
->max_height
= info
->max_height
;
2033 if (params
->mask
& SET_CONSOLE_OUTPUT_INFO_FONT
)
2035 WCHAR
*face_name
= (WCHAR
*)(params
+ 1);
2036 size_t face_name_size
= in_size
- sizeof(*params
);
2037 unsigned int height
= info
->font_height
;
2038 unsigned int weight
= FW_NORMAL
;
2040 if (!face_name_size
)
2042 face_name
= screen_buffer
->font
.face_name
;
2043 face_name_size
= screen_buffer
->font
.face_len
* sizeof(WCHAR
);
2046 if (!height
) height
= 12;
2047 if (info
->font_weight
>= FW_SEMIBOLD
) weight
= FW_BOLD
;
2049 update_console_font( screen_buffer
->console
, face_name
, face_name_size
, height
, weight
);
2052 if (is_active( screen_buffer
))
2054 tty_sync( screen_buffer
->console
);
2055 update_window_config( screen_buffer
->console
, FALSE
);
2057 return STATUS_SUCCESS
;
2060 static NTSTATUS
write_console( struct screen_buffer
*screen_buffer
, const WCHAR
*buffer
, size_t len
)
2065 TRACE( "%s\n", debugstr_wn(buffer
, len
) );
2067 empty_update_rect( screen_buffer
, &update_rect
);
2069 for (i
= 0; i
< len
; i
++)
2071 if (screen_buffer
->mode
& ENABLE_PROCESSED_OUTPUT
)
2076 screen_buffer
->cursor_x
= get_bounded_cursor_x( screen_buffer
);
2077 if (screen_buffer
->cursor_x
) screen_buffer
->cursor_x
--;
2080 j
= min( screen_buffer
->width
- screen_buffer
->cursor_x
, 8 - (screen_buffer
->cursor_x
% 8) );
2082 while (j
--) write_char( screen_buffer
, ' ', &update_rect
, NULL
);
2085 screen_buffer
->cursor_x
= 0;
2086 if (++screen_buffer
->cursor_y
== screen_buffer
->height
)
2087 new_line( screen_buffer
, &update_rect
);
2088 else if (screen_buffer
->mode
& ENABLE_WRAP_AT_EOL_OUTPUT
)
2090 update_output( screen_buffer
, &update_rect
);
2091 set_tty_cursor( screen_buffer
->console
, screen_buffer
->cursor_x
, screen_buffer
->cursor_y
);
2098 screen_buffer
->cursor_x
= 0;
2102 if (screen_buffer
->cursor_x
== screen_buffer
->width
&& !(screen_buffer
->mode
& ENABLE_WRAP_AT_EOL_OUTPUT
))
2103 screen_buffer
->cursor_x
= update_rect
.left
;
2104 write_char( screen_buffer
, buffer
[i
], &update_rect
, NULL
);
2107 if (screen_buffer
->cursor_x
== screen_buffer
->width
)
2109 if (screen_buffer
->mode
& ENABLE_WRAP_AT_EOL_OUTPUT
)
2111 if (!(screen_buffer
->mode
& ENABLE_VIRTUAL_TERMINAL_PROCESSING
))
2113 screen_buffer
->cursor_x
= 0;
2114 if (++screen_buffer
->cursor_y
== screen_buffer
->height
)
2115 new_line( screen_buffer
, &update_rect
);
2118 else screen_buffer
->cursor_x
= update_rect
.left
;
2121 scroll_to_cursor( screen_buffer
);
2122 update_output( screen_buffer
, &update_rect
);
2123 tty_sync( screen_buffer
->console
);
2124 update_window_config( screen_buffer
->console
, TRUE
);
2125 return STATUS_SUCCESS
;
2128 static NTSTATUS
write_output( struct screen_buffer
*screen_buffer
, const struct condrv_output_params
*params
,
2129 size_t in_size
, size_t *out_size
)
2131 unsigned int i
, entry_size
, entry_cnt
, x
, y
;
2135 enter_absolute_mode( screen_buffer
->console
);
2136 if (*out_size
== sizeof(SMALL_RECT
) && !params
->width
) return STATUS_INVALID_PARAMETER
;
2138 entry_size
= params
->mode
== CHAR_INFO_MODE_TEXTATTR
? sizeof(char_info_t
) : sizeof(WCHAR
);
2139 entry_cnt
= (in_size
- sizeof(*params
)) / entry_size
;
2141 TRACE( "(%u,%u) cnt %u\n", params
->x
, params
->y
, entry_cnt
);
2143 if (params
->x
>= screen_buffer
->width
)
2146 return STATUS_SUCCESS
;
2149 for (i
= 0, src
= (char *)(params
+ 1); i
< entry_cnt
; i
++, src
+= entry_size
)
2153 x
= params
->x
+ i
% params
->width
;
2154 y
= params
->y
+ i
/ params
->width
;
2155 if (x
>= screen_buffer
->width
) continue;
2159 x
= (params
->x
+ i
) % screen_buffer
->width
;
2160 y
= params
->y
+ (params
->x
+ i
) / screen_buffer
->width
;
2162 if (y
>= screen_buffer
->height
) break;
2164 dest
= &screen_buffer
->data
[y
* screen_buffer
->width
+ x
];
2165 switch(params
->mode
)
2167 case CHAR_INFO_MODE_TEXT
:
2168 dest
->ch
= *(const WCHAR
*)src
;
2170 case CHAR_INFO_MODE_ATTR
:
2171 dest
->attr
= *(const unsigned short *)src
;
2173 case CHAR_INFO_MODE_TEXTATTR
:
2174 *dest
= *(const char_info_t
*)src
;
2177 return STATUS_INVALID_PARAMETER
;
2181 if (i
&& is_active( screen_buffer
))
2185 update_rect
.left
= params
->x
;
2186 update_rect
.top
= params
->y
;
2189 update_rect
.bottom
= min( params
->y
+ entry_cnt
/ params
->width
, screen_buffer
->height
) - 1;
2190 update_rect
.right
= min( params
->x
+ params
->width
, screen_buffer
->width
) - 1;
2194 update_rect
.bottom
= params
->y
+ (params
->x
+ i
- 1) / screen_buffer
->width
;
2195 if (update_rect
.bottom
!= params
->y
)
2197 update_rect
.left
= 0;
2198 update_rect
.right
= screen_buffer
->width
- 1;
2202 update_rect
.right
= params
->x
+ i
- 1;
2205 update_output( screen_buffer
, &update_rect
);
2206 tty_sync( screen_buffer
->console
);
2209 if (*out_size
== sizeof(SMALL_RECT
))
2212 unsigned int width
= params
->width
;
2215 if (!(region
= alloc_ioctl_buffer( sizeof(*region
)))) return STATUS_NO_MEMORY
;
2218 region
->Right
= min( x
+ width
, screen_buffer
->width
) - 1;
2219 region
->Bottom
= min( y
+ entry_cnt
/ width
, screen_buffer
->height
) - 1;
2224 if (!(result
= alloc_ioctl_buffer( sizeof(*result
)))) return STATUS_NO_MEMORY
;
2228 return STATUS_SUCCESS
;
2231 static NTSTATUS
read_output( struct screen_buffer
*screen_buffer
, const struct condrv_output_params
*params
,
2234 enum char_info_mode mode
;
2235 unsigned int x
, y
, width
;
2236 unsigned int i
, count
;
2238 enter_absolute_mode( screen_buffer
->console
);
2241 mode
= params
->mode
;
2242 width
= params
->width
;
2243 TRACE( "(%u %u) mode %u width %u\n", x
, y
, mode
, width
);
2247 case CHAR_INFO_MODE_TEXT
:
2251 if (x
>= screen_buffer
->width
|| y
>= screen_buffer
->height
)
2254 return STATUS_SUCCESS
;
2256 src
= screen_buffer
->data
+ y
* screen_buffer
->width
+ x
;
2257 count
= min( screen_buffer
->data
+ screen_buffer
->height
* screen_buffer
->width
- src
,
2258 *out_size
/ sizeof(*data
) );
2259 *out_size
= count
* sizeof(*data
);
2260 if (!(data
= alloc_ioctl_buffer( *out_size
))) return STATUS_NO_MEMORY
;
2261 for (i
= 0; i
< count
; i
++) data
[i
] = src
[i
].ch
;
2264 case CHAR_INFO_MODE_ATTR
:
2266 unsigned short *data
;
2268 if (x
>= screen_buffer
->width
|| y
>= screen_buffer
->height
)
2271 return STATUS_SUCCESS
;
2273 src
= screen_buffer
->data
+ y
* screen_buffer
->width
+ x
;
2274 count
= min( screen_buffer
->data
+ screen_buffer
->height
* screen_buffer
->width
- src
,
2275 *out_size
/ sizeof(*data
) );
2276 *out_size
= count
* sizeof(*data
);
2277 if (!(data
= alloc_ioctl_buffer( *out_size
))) return STATUS_NO_MEMORY
;
2278 for (i
= 0; i
< count
; i
++) data
[i
] = src
[i
].attr
;
2281 case CHAR_INFO_MODE_TEXTATTR
:
2285 if (!width
|| *out_size
< sizeof(*region
) || x
>= screen_buffer
->width
|| y
>= screen_buffer
->height
)
2286 return STATUS_INVALID_PARAMETER
;
2287 count
= min( (*out_size
- sizeof(*region
)) / (width
* sizeof(*data
)), screen_buffer
->height
- y
);
2288 width
= min( width
, screen_buffer
->width
- x
);
2289 *out_size
= sizeof(*region
) + width
* count
* sizeof(*data
);
2290 if (!(region
= alloc_ioctl_buffer( *out_size
))) return STATUS_NO_MEMORY
;
2293 region
->Right
= x
+ width
- 1;
2294 region
->Bottom
= y
+ count
- 1;
2295 data
= (char_info_t
*)(region
+ 1);
2296 for (i
= 0; i
< count
; i
++)
2298 memcpy( &data
[i
* width
], &screen_buffer
->data
[(y
+ i
) * screen_buffer
->width
+ x
],
2299 width
* sizeof(*data
) );
2304 return STATUS_INVALID_PARAMETER
;
2307 return STATUS_SUCCESS
;
2310 static NTSTATUS
fill_output( struct screen_buffer
*screen_buffer
, const struct condrv_fill_output_params
*params
)
2312 char_info_t
*end
, *dest
;
2313 DWORD i
, count
, *result
;
2315 TRACE( "(%u %u) mode %u\n", params
->x
, params
->y
, params
->mode
);
2317 enter_absolute_mode( screen_buffer
->console
);
2318 if (params
->y
>= screen_buffer
->height
) return STATUS_SUCCESS
;
2319 dest
= screen_buffer
->data
+ min( params
->y
* screen_buffer
->width
+ params
->x
,
2320 screen_buffer
->height
* screen_buffer
->width
);
2322 end
= screen_buffer
->data
+ screen_buffer
->height
* screen_buffer
->width
;
2324 count
= params
->count
;
2325 if (count
> end
- dest
) count
= end
- dest
;
2327 switch(params
->mode
)
2329 case CHAR_INFO_MODE_TEXT
:
2330 for (i
= 0; i
< count
; i
++) dest
[i
].ch
= params
->ch
;
2332 case CHAR_INFO_MODE_ATTR
:
2333 for (i
= 0; i
< count
; i
++) dest
[i
].attr
= params
->attr
;
2335 case CHAR_INFO_MODE_TEXTATTR
:
2336 for (i
= 0; i
< count
; i
++)
2338 dest
[i
].ch
= params
->ch
;
2339 dest
[i
].attr
= params
->attr
;
2343 return STATUS_INVALID_PARAMETER
;
2346 if (count
&& is_active(screen_buffer
))
2349 SetRect( &update_rect
,
2350 params
->x
% screen_buffer
->width
,
2351 params
->y
+ params
->x
/ screen_buffer
->width
,
2352 (params
->x
+ i
- 1) % screen_buffer
->width
,
2353 params
->y
+ (params
->x
+ i
- 1) / screen_buffer
->width
);
2354 update_output( screen_buffer
, &update_rect
);
2355 tty_sync( screen_buffer
->console
);
2358 if (!(result
= alloc_ioctl_buffer( sizeof(*result
) ))) return STATUS_NO_MEMORY
;
2360 return STATUS_SUCCESS
;
2363 static NTSTATUS
scroll_output( struct screen_buffer
*screen_buffer
, const struct condrv_scroll_params
*params
)
2365 int x
, y
, xsrc
, ysrc
, w
, h
;
2366 char_info_t
*psrc
, *pdst
;
2367 SMALL_RECT src
, dst
;
2371 enter_absolute_mode( screen_buffer
->console
);
2372 xsrc
= params
->scroll
.Left
;
2373 ysrc
= params
->scroll
.Top
;
2374 w
= params
->scroll
.Right
- params
->scroll
.Left
+ 1;
2375 h
= params
->scroll
.Bottom
- params
->scroll
.Top
+ 1;
2377 TRACE( "(%d %d) -> (%u %u) w %u h %u\n", xsrc
, ysrc
, params
->origin
.X
, params
->origin
.Y
, w
, h
);
2379 clip
.Left
= max( params
->clip
.Left
, 0 );
2380 clip
.Top
= max( params
->clip
.Top
, 0 );
2381 clip
.Right
= min( params
->clip
.Right
, screen_buffer
->width
- 1 );
2382 clip
.Bottom
= min( params
->clip
.Bottom
, screen_buffer
->height
- 1 );
2383 if (clip
.Left
> clip
.Right
|| clip
.Top
> clip
.Bottom
|| params
->scroll
.Left
< 0 || params
->scroll
.Top
< 0 ||
2384 params
->scroll
.Right
>= screen_buffer
->width
|| params
->scroll
.Bottom
>= screen_buffer
->height
||
2385 params
->scroll
.Right
< params
->scroll
.Left
|| params
->scroll
.Top
> params
->scroll
.Bottom
||
2386 params
->origin
.X
< 0 || params
->origin
.X
>= screen_buffer
->width
|| params
->origin
.Y
< 0 ||
2387 params
->origin
.Y
>= screen_buffer
->height
)
2388 return STATUS_INVALID_PARAMETER
;
2390 src
.Left
= max( xsrc
, clip
.Left
);
2391 src
.Top
= max( ysrc
, clip
.Top
);
2392 src
.Right
= min( xsrc
+ w
- 1, clip
.Right
);
2393 src
.Bottom
= min( ysrc
+ h
- 1, clip
.Bottom
);
2395 dst
.Left
= params
->origin
.X
;
2396 dst
.Top
= params
->origin
.Y
;
2397 dst
.Right
= params
->origin
.X
+ w
- 1;
2398 dst
.Bottom
= params
->origin
.Y
+ h
- 1;
2400 if (dst
.Left
< clip
.Left
)
2402 xsrc
+= clip
.Left
- dst
.Left
;
2403 w
-= clip
.Left
- dst
.Left
;
2404 dst
.Left
= clip
.Left
;
2406 if (dst
.Top
< clip
.Top
)
2408 ysrc
+= clip
.Top
- dst
.Top
;
2409 h
-= clip
.Top
- dst
.Top
;
2412 if (dst
.Right
> clip
.Right
) w
-= dst
.Right
- clip
.Right
;
2413 if (dst
.Bottom
> clip
.Bottom
) h
-= dst
.Bottom
- clip
.Bottom
;
2419 psrc
= &screen_buffer
->data
[(ysrc
+ h
- 1) * screen_buffer
->width
+ xsrc
];
2420 pdst
= &screen_buffer
->data
[(dst
.Top
+ h
- 1) * screen_buffer
->width
+ dst
.Left
];
2422 for (y
= h
; y
> 0; y
--)
2424 memcpy( pdst
, psrc
, w
* sizeof(*pdst
) );
2425 pdst
-= screen_buffer
->width
;
2426 psrc
-= screen_buffer
->width
;
2431 psrc
= &screen_buffer
->data
[ysrc
* screen_buffer
->width
+ xsrc
];
2432 pdst
= &screen_buffer
->data
[dst
.Top
* screen_buffer
->width
+ dst
.Left
];
2434 for (y
= 0; y
< h
; y
++)
2436 /* we use memmove here because when psrc and pdst are the same,
2437 * copies are done on the same row, so the dst and src blocks
2439 memmove( pdst
, psrc
, w
* sizeof(*pdst
) );
2440 pdst
+= screen_buffer
->width
;
2441 psrc
+= screen_buffer
->width
;
2446 for (y
= src
.Top
; y
<= src
.Bottom
; y
++)
2448 int left
= src
.Left
;
2449 int right
= src
.Right
;
2450 if (dst
.Top
<= y
&& y
<= dst
.Bottom
)
2452 if (dst
.Left
<= src
.Left
) left
= max( left
, dst
.Right
+ 1 );
2453 if (dst
.Left
>= src
.Left
) right
= min( right
, dst
.Left
- 1 );
2455 for (x
= left
; x
<= right
; x
++) screen_buffer
->data
[y
* screen_buffer
->width
+ x
] = params
->fill
;
2458 SetRect( &update_rect
, min( src
.Left
, dst
.Left
), min( src
.Top
, dst
.Top
),
2459 max( src
.Right
, dst
.Right
), max( src
.Bottom
, dst
.Bottom
));
2460 update_output( screen_buffer
, &update_rect
);
2461 tty_sync( screen_buffer
->console
);
2462 return STATUS_SUCCESS
;
2465 static WCHAR
*set_title( const WCHAR
*in_title
, size_t size
)
2467 WCHAR
*title
= NULL
;
2469 title
= malloc( size
+ sizeof(WCHAR
) );
2470 if (!title
) return NULL
;
2472 memcpy( title
, in_title
, size
);
2473 title
[ size
/ sizeof(WCHAR
) ] = 0;
2478 static NTSTATUS
set_console_title( struct console
*console
, const WCHAR
*in_title
, size_t size
)
2480 WCHAR
*title
= NULL
;
2482 TRACE( "%s\n", debugstr_wn(in_title
, size
/ sizeof(WCHAR
)) );
2484 if (!(title
= set_title( in_title
, size
)))
2485 return STATUS_NO_MEMORY
;
2487 free( console
->title
);
2488 console
->title
= title
;
2490 if (!console
->title_orig
&& !(console
->title_orig
= set_title( in_title
, size
)))
2492 free( console
->title
);
2493 console
->title
= NULL
;
2494 return STATUS_NO_MEMORY
;
2497 if (console
->tty_output
)
2502 tty_write( console
, "\x1b]0;", 4 );
2503 len
= WideCharToMultiByte( get_tty_cp( console
), 0, console
->title
, size
/ sizeof(WCHAR
),
2504 NULL
, 0, NULL
, NULL
);
2505 if ((vt
= tty_alloc_buffer( console
, len
)))
2506 WideCharToMultiByte( get_tty_cp( console
), 0, console
->title
, size
/ sizeof(WCHAR
),
2507 vt
, len
, NULL
, NULL
);
2508 tty_write( console
, "\x07", 1 );
2509 tty_sync( console
);
2512 SetWindowTextW( console
->win
, console
->title
);
2513 return STATUS_SUCCESS
;
2516 static NTSTATUS
screen_buffer_ioctl( struct screen_buffer
*screen_buffer
, unsigned int code
,
2517 const void *in_data
, size_t in_size
, size_t *out_size
)
2521 case IOCTL_CONDRV_CLOSE_OUTPUT
:
2522 if (in_size
|| *out_size
) return STATUS_INVALID_PARAMETER
;
2523 destroy_screen_buffer( screen_buffer
);
2524 return STATUS_SUCCESS
;
2526 case IOCTL_CONDRV_ACTIVATE
:
2527 if (in_size
|| *out_size
) return STATUS_INVALID_PARAMETER
;
2528 return screen_buffer_activate( screen_buffer
);
2530 case IOCTL_CONDRV_GET_MODE
:
2533 TRACE( "returning mode %x\n", screen_buffer
->mode
);
2534 if (in_size
|| *out_size
!= sizeof(*mode
)) return STATUS_INVALID_PARAMETER
;
2535 if (!(mode
= alloc_ioctl_buffer( *out_size
))) return STATUS_NO_MEMORY
;
2536 *mode
= screen_buffer
->mode
;
2537 return STATUS_SUCCESS
;
2540 case IOCTL_CONDRV_SET_MODE
:
2541 if (in_size
!= sizeof(unsigned int) || *out_size
) return STATUS_INVALID_PARAMETER
;
2542 screen_buffer
->mode
= *(unsigned int *)in_data
;
2543 TRACE( "set %x mode\n", screen_buffer
->mode
);
2544 return STATUS_SUCCESS
;
2546 case IOCTL_CONDRV_IS_UNIX
:
2547 return screen_buffer
->console
->is_unix
? STATUS_SUCCESS
: STATUS_NOT_SUPPORTED
;
2549 case IOCTL_CONDRV_WRITE_CONSOLE
:
2550 if (in_size
% sizeof(WCHAR
) || *out_size
) return STATUS_INVALID_PARAMETER
;
2551 return write_console( screen_buffer
, in_data
, in_size
/ sizeof(WCHAR
) );
2553 case IOCTL_CONDRV_WRITE_FILE
:
2559 len
= MultiByteToWideChar( screen_buffer
->console
->output_cp
, 0, in_data
, in_size
,
2561 if (!len
) return STATUS_SUCCESS
;
2562 if (!(buf
= malloc( len
* sizeof(WCHAR
) ))) return STATUS_NO_MEMORY
;
2563 MultiByteToWideChar( screen_buffer
->console
->output_cp
, 0, in_data
, in_size
, buf
, len
);
2564 status
= write_console( screen_buffer
, buf
, len
);
2569 case IOCTL_CONDRV_WRITE_OUTPUT
:
2570 if ((*out_size
!= sizeof(DWORD
) && *out_size
!= sizeof(SMALL_RECT
)) ||
2571 in_size
< sizeof(struct condrv_output_params
))
2572 return STATUS_INVALID_PARAMETER
;
2573 return write_output( screen_buffer
, in_data
, in_size
, out_size
);
2575 case IOCTL_CONDRV_READ_OUTPUT
:
2576 if (in_size
!= sizeof(struct condrv_output_params
)) return STATUS_INVALID_PARAMETER
;
2577 return read_output( screen_buffer
, in_data
, out_size
);
2579 case IOCTL_CONDRV_GET_OUTPUT_INFO
:
2580 if (in_size
|| *out_size
< sizeof(struct condrv_output_info
)) return STATUS_INVALID_PARAMETER
;
2581 return get_output_info( screen_buffer
, out_size
);
2583 case IOCTL_CONDRV_SET_OUTPUT_INFO
:
2584 if (in_size
< sizeof(struct condrv_output_info_params
) || *out_size
)
2585 return STATUS_INVALID_PARAMETER
;
2586 return set_output_info( screen_buffer
, in_data
, in_size
);
2588 case IOCTL_CONDRV_FILL_OUTPUT
:
2589 if (in_size
!= sizeof(struct condrv_fill_output_params
) || *out_size
!= sizeof(DWORD
))
2590 return STATUS_INVALID_PARAMETER
;
2591 return fill_output( screen_buffer
, in_data
);
2593 case IOCTL_CONDRV_SCROLL
:
2594 if (in_size
!= sizeof(struct condrv_scroll_params
) || *out_size
)
2595 return STATUS_INVALID_PARAMETER
;
2596 return scroll_output( screen_buffer
, in_data
);
2599 WARN( "invalid ioctl %x\n", code
);
2600 return STATUS_INVALID_HANDLE
;
2604 static NTSTATUS
console_input_ioctl( struct console
*console
, unsigned int code
, const void *in_data
,
2605 size_t in_size
, size_t *out_size
)
2611 case IOCTL_CONDRV_GET_MODE
:
2614 TRACE( "returning mode %x\n", console
->mode
);
2615 if (in_size
|| *out_size
!= sizeof(*mode
)) return STATUS_INVALID_PARAMETER
;
2616 if (!(mode
= alloc_ioctl_buffer( *out_size
))) return STATUS_NO_MEMORY
;
2617 *mode
= console
->mode
;
2618 return STATUS_SUCCESS
;
2621 case IOCTL_CONDRV_SET_MODE
:
2622 if (in_size
!= sizeof(unsigned int) || *out_size
) return STATUS_INVALID_PARAMETER
;
2623 console
->mode
= *(unsigned int *)in_data
;
2624 TRACE( "set %x mode\n", console
->mode
);
2625 return STATUS_SUCCESS
;
2627 case IOCTL_CONDRV_IS_UNIX
:
2628 return console
->is_unix
? STATUS_SUCCESS
: STATUS_NOT_SUPPORTED
;
2630 case IOCTL_CONDRV_READ_CONSOLE
:
2631 if (in_size
|| *out_size
% sizeof(WCHAR
)) return STATUS_INVALID_PARAMETER
;
2632 ensure_tty_input_thread( console
);
2633 status
= read_console( console
, code
, *out_size
, NULL
, 0, 0 );
2637 case IOCTL_CONDRV_READ_CONSOLE_CONTROL
:
2638 if ((in_size
< sizeof(DWORD
)) || ((in_size
- sizeof(DWORD
)) % sizeof(WCHAR
)) ||
2639 (*out_size
< sizeof(DWORD
)) || ((*out_size
- sizeof(DWORD
)) % sizeof(WCHAR
)))
2640 return STATUS_INVALID_PARAMETER
;
2641 ensure_tty_input_thread( console
);
2642 status
= read_console( console
, code
, *out_size
- sizeof(DWORD
),
2643 (const WCHAR
*)((const char*)in_data
+ sizeof(DWORD
)),
2644 (in_size
- sizeof(DWORD
)) / sizeof(WCHAR
),
2649 case IOCTL_CONDRV_READ_FILE
:
2650 ensure_tty_input_thread( console
);
2651 status
= read_console( console
, code
, *out_size
, NULL
, 0, 0 );
2655 case IOCTL_CONDRV_READ_INPUT
:
2657 if (in_size
) return STATUS_INVALID_PARAMETER
;
2658 ensure_tty_input_thread( console
);
2659 if (!console
->record_count
&& *out_size
)
2661 TRACE( "pending read\n" );
2662 console
->read_ioctl
= IOCTL_CONDRV_READ_INPUT
;
2663 console
->pending_read
= *out_size
;
2664 return STATUS_PENDING
;
2666 status
= read_console_input( console
, *out_size
);
2671 case IOCTL_CONDRV_WRITE_INPUT
:
2672 if (in_size
% sizeof(INPUT_RECORD
) || *out_size
) return STATUS_INVALID_PARAMETER
;
2673 return write_console_input( console
, in_data
, in_size
/ sizeof(INPUT_RECORD
), TRUE
);
2675 case IOCTL_CONDRV_PEEK
:
2679 if (in_size
) return STATUS_INVALID_PARAMETER
;
2680 ensure_tty_input_thread( console
);
2681 *out_size
= min( *out_size
, console
->record_count
* sizeof(INPUT_RECORD
) );
2682 if (!(result
= alloc_ioctl_buffer( *out_size
))) return STATUS_NO_MEMORY
;
2683 if (*out_size
) memcpy( result
, console
->records
, *out_size
);
2684 return STATUS_SUCCESS
;
2687 case IOCTL_CONDRV_GET_INPUT_INFO
:
2689 struct condrv_input_info
*info
;
2690 TRACE( "get info\n" );
2691 if (in_size
|| *out_size
!= sizeof(*info
)) return STATUS_INVALID_PARAMETER
;
2692 if (!(info
= alloc_ioctl_buffer( sizeof(*info
)))) return STATUS_NO_MEMORY
;
2693 info
->input_cp
= console
->input_cp
;
2694 info
->output_cp
= console
->output_cp
;
2695 info
->input_count
= console
->record_count
;
2696 return STATUS_SUCCESS
;
2699 case IOCTL_CONDRV_GET_WINDOW
:
2701 condrv_handle_t
*result
;
2702 TRACE( "get window\n" );
2703 if (in_size
|| *out_size
!= sizeof(*result
)) return STATUS_INVALID_PARAMETER
;
2704 if (!(result
= alloc_ioctl_buffer( sizeof(*result
)))) return STATUS_NO_MEMORY
;
2705 if (!console
->win
&& !console
->no_window
) init_message_window( console
);
2706 *result
= condrv_handle( console
->win
);
2707 return STATUS_SUCCESS
;
2710 case IOCTL_CONDRV_SET_INPUT_INFO
:
2712 const struct condrv_input_info_params
*params
= in_data
;
2713 TRACE( "set info\n" );
2714 if (in_size
!= sizeof(*params
) || *out_size
) return STATUS_INVALID_PARAMETER
;
2715 if (params
->mask
& SET_CONSOLE_INPUT_INFO_INPUT_CODEPAGE
)
2717 if (!IsValidCodePage( params
->info
.input_cp
)) return STATUS_INVALID_PARAMETER
;
2718 console
->input_cp
= params
->info
.input_cp
;
2720 if (params
->mask
& SET_CONSOLE_INPUT_INFO_OUTPUT_CODEPAGE
)
2722 if (!IsValidCodePage( params
->info
.output_cp
)) return STATUS_INVALID_PARAMETER
;
2723 console
->output_cp
= params
->info
.output_cp
;
2725 return STATUS_SUCCESS
;
2728 case IOCTL_CONDRV_GET_TITLE
:
2732 size_t title_len
, str_size
;
2733 struct condrv_title_params
*params
;
2734 if (in_size
!= sizeof(BOOL
)) return STATUS_INVALID_PARAMETER
;
2735 current_title
= *(BOOL
*)in_data
;
2736 title
= current_title
? console
->title
: console
->title_orig
;
2737 title_len
= title
? wcslen( title
) : 0;
2738 str_size
= min( *out_size
- sizeof(*params
), title_len
* sizeof(WCHAR
) );
2739 *out_size
= sizeof(*params
) + str_size
;
2740 if (!(params
= alloc_ioctl_buffer( *out_size
))) return STATUS_NO_MEMORY
;
2741 TRACE( "returning %s %s\n", current_title
? "title" : "original title", debugstr_w(title
) );
2742 if (str_size
) memcpy( params
->buffer
, title
, str_size
);
2743 params
->title_len
= title_len
;
2744 return STATUS_SUCCESS
;
2747 case IOCTL_CONDRV_SET_TITLE
:
2748 if (in_size
% sizeof(WCHAR
) || *out_size
) return STATUS_INVALID_PARAMETER
;
2749 return set_console_title( console
, in_data
, in_size
);
2751 case IOCTL_CONDRV_BEEP
:
2752 if (in_size
|| *out_size
) return STATUS_INVALID_PARAMETER
;
2753 if (console
->is_unix
)
2755 tty_write( console
, "\a", 1 );
2756 tty_sync( console
);
2758 return STATUS_SUCCESS
;
2760 case IOCTL_CONDRV_FLUSH
:
2761 if (in_size
|| *out_size
) return STATUS_INVALID_PARAMETER
;
2763 console
->record_count
= 0;
2764 return STATUS_SUCCESS
;
2767 WARN( "unsupported ioctl %x\n", code
);
2768 return STATUS_INVALID_HANDLE
;
2772 static NTSTATUS
process_console_ioctls( struct console
*console
)
2774 size_t out_size
= 0, in_size
;
2777 NTSTATUS status
= STATUS_SUCCESS
;
2781 if (status
) out_size
= 0;
2783 console
->signaled
= console
->record_count
!= 0;
2784 SERVER_START_REQ( get_next_console_request
)
2786 req
->handle
= wine_server_obj_handle( console
->server
);
2787 req
->status
= status
;
2788 req
->signal
= console
->signaled
;
2789 wine_server_add_data( req
, ioctl_buffer
, out_size
);
2790 wine_server_set_reply( req
, ioctl_buffer
, ioctl_buffer_size
);
2791 status
= wine_server_call( req
);
2793 output
= reply
->output
;
2794 out_size
= reply
->out_size
;
2795 in_size
= wine_server_reply_size( reply
);
2799 if (status
== STATUS_PENDING
) return STATUS_SUCCESS
;
2800 if (status
== STATUS_BUFFER_OVERFLOW
)
2802 if (!alloc_ioctl_buffer( out_size
)) return STATUS_NO_MEMORY
;
2803 status
= STATUS_SUCCESS
;
2808 TRACE( "failed to get next request: %#lx\n", status
);
2812 if (code
== IOCTL_CONDRV_INIT_OUTPUT
)
2814 TRACE( "initializing output %x\n", output
);
2815 enter_absolute_mode( console
);
2816 if (console
->active
)
2817 create_screen_buffer( console
, output
, console
->active
->width
, console
->active
->height
);
2819 create_screen_buffer( console
, output
, 80, 150 );
2823 status
= console_input_ioctl( console
, code
, ioctl_buffer
, in_size
, &out_size
);
2827 struct wine_rb_entry
*entry
;
2828 if (!(entry
= wine_rb_get( &screen_buffer_map
, LongToPtr(output
) )))
2830 ERR( "invalid screen buffer id %x\n", output
);
2831 status
= STATUS_INVALID_HANDLE
;
2835 status
= screen_buffer_ioctl( WINE_RB_ENTRY_VALUE( entry
, struct screen_buffer
, entry
), code
,
2836 ioctl_buffer
, in_size
, &out_size
);
2842 static int main_loop( struct console
*console
, HANDLE signal
)
2844 HANDLE signal_event
= NULL
;
2845 HANDLE wait_handles
[3];
2846 unsigned int wait_cnt
= 0;
2847 unsigned short signal_id
;
2848 IO_STATUS_BLOCK signal_io
;
2854 if (!(signal_event
= CreateEventW( NULL
, TRUE
, FALSE
, NULL
))) return 1;
2855 status
= NtReadFile( signal
, signal_event
, NULL
, NULL
, &signal_io
, &signal_id
,
2856 sizeof(signal_id
), NULL
, NULL
);
2857 if (status
&& status
!= STATUS_PENDING
) return 1;
2860 if (!alloc_ioctl_buffer( 4096 )) return 1;
2862 wait_handles
[wait_cnt
++] = console
->server
;
2863 if (signal
) wait_handles
[wait_cnt
++] = signal_event
;
2864 if (console
->input_thread
) wait_handles
[wait_cnt
++] = console
->input_thread
;
2869 res
= MsgWaitForMultipleObjects( wait_cnt
, wait_handles
, FALSE
, INFINITE
, QS_ALLINPUT
);
2871 res
= WaitForMultipleObjects( wait_cnt
, wait_handles
, FALSE
, INFINITE
);
2873 if (res
== WAIT_OBJECT_0
+ wait_cnt
)
2876 while (PeekMessageW( &msg
, 0, 0, 0, PM_REMOVE
))
2878 if (msg
.message
== WM_QUIT
) return 0;
2879 DispatchMessageW(&msg
);
2887 EnterCriticalSection( &console_section
);
2888 status
= process_console_ioctls( console
);
2889 LeaveCriticalSection( &console_section
);
2890 if (status
) return 0;
2893 case WAIT_OBJECT_0
+ 1:
2894 if (signal_io
.Status
|| signal_io
.Information
!= sizeof(signal_id
))
2896 TRACE( "signaled quit\n" );
2899 FIXME( "unimplemented signal %x\n", signal_id
);
2900 status
= NtReadFile( signal
, signal_event
, NULL
, NULL
, &signal_io
, &signal_id
,
2901 sizeof(signal_id
), NULL
, NULL
);
2902 if (status
&& status
!= STATUS_PENDING
) return 1;
2906 TRACE( "wait failed, quit\n");
2914 static void teardown( struct console
*console
)
2916 if (console
->is_unix
)
2918 set_tty_attr( console
, empty_char_info
.attr
);
2919 tty_flush( console
);
2923 int __cdecl
wmain(int argc
, WCHAR
*argv
[])
2925 int headless
= 0, i
, width
= 0, height
= 0, ret
;
2926 HANDLE signal
= NULL
;
2929 static struct console console
;
2931 for (i
= 0; i
< argc
; i
++) TRACE("%s ", wine_dbgstr_w(argv
[i
]));
2934 console
.mode
= ENABLE_PROCESSED_INPUT
| ENABLE_LINE_INPUT
|
2935 ENABLE_ECHO_INPUT
| ENABLE_MOUSE_INPUT
| ENABLE_INSERT_MODE
|
2936 ENABLE_QUICK_EDIT_MODE
| ENABLE_EXTENDED_FLAGS
| ENABLE_AUTO_POSITION
;
2937 console
.input_cp
= console
.output_cp
= GetOEMCP();
2938 console
.history_size
= 50;
2939 if (!(console
.history
= calloc( console
.history_size
, sizeof(*console
.history
) ))) return 1;
2941 for (i
= 1; i
< argc
; i
++)
2943 if (!wcscmp( argv
[i
], L
"--headless"))
2948 if (!wcscmp( argv
[i
], L
"--unix"))
2950 console
.is_unix
= 1;
2951 console
.use_relative_cursor
= 1;
2955 if (!wcscmp( argv
[i
], L
"--width" ))
2957 if (++i
== argc
) return 1;
2958 width
= wcstol( argv
[i
], &end
, 0 );
2959 if ((!width
&& !console
.is_unix
) || width
> 0xffff || *end
) return 1;
2962 if (!wcscmp( argv
[i
], L
"--height" ))
2964 if (++i
== argc
) return 1;
2965 height
= wcstol( argv
[i
], &end
, 0 );
2966 if ((!height
&& !console
.is_unix
) || height
> 0xffff || *end
) return 1;
2969 if (!wcscmp( argv
[i
], L
"--signal" ))
2971 if (++i
== argc
) return 1;
2972 signal
= ULongToHandle( wcstol( argv
[i
], &end
, 0 ));
2976 if (!wcscmp( argv
[i
], L
"--server" ))
2978 if (++i
== argc
) return 1;
2979 console
.server
= ULongToHandle( wcstol( argv
[i
], &end
, 0 ));
2983 FIXME( "unknown option %s\n", debugstr_w(argv
[i
]) );
2987 if (!console
.server
)
2989 ERR( "no server handle\n" );
2993 if (!width
) width
= 80;
2994 if (!height
) height
= 150;
2996 if (!(console
.active
= create_screen_buffer( &console
, 1, width
, height
))) return 1;
2999 console
.tty_input
= GetStdHandle( STD_INPUT_HANDLE
);
3000 console
.tty_output
= GetStdHandle( STD_OUTPUT_HANDLE
);
3002 if (console
.tty_input
|| console
.tty_output
)
3004 init_tty_output( &console
);
3005 if (!console
.is_unix
&& !ensure_tty_input_thread( &console
)) return 1;
3007 else console
.no_window
= TRUE
;
3012 if (!init_window( &console
)) return 1;
3013 GetStartupInfoW( &si
);
3014 set_console_title( &console
, si
.lpTitle
, wcslen( si
.lpTitle
) * sizeof(WCHAR
) );
3015 ShowWindow( console
.win
, (si
.dwFlags
& STARTF_USESHOWWINDOW
) ? si
.wShowWindow
: SW_SHOW
);
3018 ret
= main_loop( &console
, signal
);
3019 teardown( &console
);