msvcrt: Fix *printf() handling of negative field width.
[wine] / server / console.c
1 /*
2  * Server-side console management
3  *
4  * Copyright (C) 1998 Alexandre Julliard
5  *               2001 Eric Pouech
6  *
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22
23 #include "config.h"
24 #include "wine/port.h"
25
26 #include <assert.h>
27 #include <string.h>
28 #include <stdio.h>
29 #include <unistd.h>
30 #include <signal.h>
31
32 #include "ntstatus.h"
33 #define WIN32_NO_STATUS
34 #include "handle.h"
35 #include "process.h"
36 #include "request.h"
37 #include "unicode.h"
38 #include "console.h"
39 #include "winternl.h"
40
41 /* specific access rights (FIXME: should use finer-grained access rights) */
42 #define CONSOLE_READ   0x01
43 #define CONSOLE_WRITE  0x02
44
45 static unsigned int console_map_access( struct object *obj, unsigned int access );
46
47 static void console_input_dump( struct object *obj, int verbose );
48 static void console_input_destroy( struct object *obj );
49
50 static const struct object_ops console_input_ops =
51 {
52     sizeof(struct console_input),     /* size */
53     console_input_dump,               /* dump */
54     no_add_queue,                     /* add_queue */
55     NULL,                             /* remove_queue */
56     NULL,                             /* signaled */
57     no_satisfied,                     /* satisfied */
58     no_signal,                        /* signal */
59     no_get_fd,                        /* get_fd */
60     console_map_access,               /* map_access */
61     no_lookup_name,                   /* lookup_name */
62     no_close_handle,                  /* close_handle */
63     console_input_destroy             /* destroy */
64 };
65
66 static void console_input_events_dump( struct object *obj, int verbose );
67 static void console_input_events_destroy( struct object *obj );
68 static int  console_input_events_signaled( struct object *obj, struct thread *thread );
69
70 struct console_input_events
71 {
72     struct object         obj;         /* object header */
73     int                   num_alloc;   /* number of allocated events */
74     int                   num_used;    /* number of actually used events */
75     struct console_renderer_event*      events;
76 };
77
78 static const struct object_ops console_input_events_ops =
79 {
80     sizeof(struct console_input_events), /* size */
81     console_input_events_dump,        /* dump */
82     add_queue,                        /* add_queue */
83     remove_queue,                     /* remove_queue */
84     console_input_events_signaled,    /* signaled */
85     no_satisfied,                     /* satisfied */
86     no_signal,                        /* signal */
87     no_get_fd,                        /* get_fd */
88     console_map_access,               /* map_access */
89     no_lookup_name,                   /* lookup_name */
90     no_close_handle,                  /* close_handle */
91     console_input_events_destroy      /* destroy */
92 };
93
94 struct screen_buffer
95 {
96     struct object         obj;           /* object header */
97     struct list           entry;         /* entry in list of all screen buffers */
98     struct console_input *input;         /* associated console input */
99     int                   mode;          /* output mode */
100     int                   cursor_size;   /* size of cursor (percentage filled) */
101     int                   cursor_visible;/* cursor visibility flag */
102     int                   cursor_x;      /* position of cursor */
103     int                   cursor_y;      /* position of cursor */
104     int                   width;         /* size (w-h) of the screen buffer */
105     int                   height;
106     int                   max_width;     /* size (w-h) of the window given font size */
107     int                   max_height;
108     char_info_t          *data;          /* the data for each cell - a width x height matrix */
109     unsigned short        attr;          /* default attribute for screen buffer */
110     rectangle_t           win;           /* current visible window on the screen buffer *
111                                           * as seen in wineconsole */
112 };
113
114 static void screen_buffer_dump( struct object *obj, int verbose );
115 static void screen_buffer_destroy( struct object *obj );
116
117 static const struct object_ops screen_buffer_ops =
118 {
119     sizeof(struct screen_buffer),     /* size */
120     screen_buffer_dump,               /* dump */
121     no_add_queue,                     /* add_queue */
122     NULL,                             /* remove_queue */
123     NULL,                             /* signaled */
124     NULL,                             /* satisfied */
125     no_signal,                        /* signal */
126     no_get_fd,                        /* get_fd */
127     console_map_access,               /* map_access */
128     no_lookup_name,                   /* lookup_name */
129     no_close_handle,                  /* close_handle */
130     screen_buffer_destroy             /* destroy */
131 };
132
133 static struct list screen_buffer_list = LIST_INIT(screen_buffer_list);
134
135 static const char_info_t empty_char_info = { ' ', 0x000f };  /* white on black space */
136
137 /* access mapping for all console objects */
138 static unsigned int console_map_access( struct object *obj, unsigned int access )
139 {
140     if (access & GENERIC_READ)    access |= SYNCHRONIZE | STANDARD_RIGHTS_READ | CONSOLE_READ;
141     if (access & GENERIC_WRITE)   access |= SYNCHRONIZE | STANDARD_RIGHTS_WRITE | CONSOLE_WRITE;
142     if (access & GENERIC_EXECUTE) access |= SYNCHRONIZE | STANDARD_RIGHTS_EXECUTE;
143     if (access & GENERIC_ALL)     access |= STANDARD_RIGHTS_ALL | CONSOLE_READ | CONSOLE_WRITE;
144     return access & ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL);
145 }
146
147 /* dumps the renderer events of a console */
148 static void console_input_events_dump( struct object *obj, int verbose )
149 {
150     struct console_input_events *evts = (struct console_input_events *)obj;
151     assert( obj->ops == &console_input_events_ops );
152     fprintf( stderr, "Console input events: %d/%d events\n",
153              evts->num_used, evts->num_alloc );
154 }
155
156 /* destroys the renderer events of a console */
157 static void console_input_events_destroy( struct object *obj )
158 {
159     struct console_input_events *evts = (struct console_input_events *)obj;
160     assert( obj->ops == &console_input_events_ops );
161     free( evts->events );
162 }
163
164 /* the renderer events list is signaled when it's not empty */
165 static int console_input_events_signaled( struct object *obj, struct thread *thread )
166 {
167     struct console_input_events *evts = (struct console_input_events *)obj;
168     assert( obj->ops == &console_input_events_ops );
169     return (evts->num_used != 0);
170 }
171
172 /* add an event to the console's renderer events list */
173 static void console_input_events_append( struct console_input_events* evts,
174                                          struct console_renderer_event* evt)
175 {
176     int collapsed = FALSE;
177
178     /* to be done even when evt has been generated by the rendere ? */
179
180     /* try to collapse evt into current queue's events */
181     if (evts->num_used)
182     {
183         struct console_renderer_event* last = &evts->events[evts->num_used - 1];
184
185         if (last->event == CONSOLE_RENDERER_UPDATE_EVENT &&
186             evt->event == CONSOLE_RENDERER_UPDATE_EVENT)
187         {
188             /* if two update events overlap, collapse them into a single one */
189             if (last->u.update.bottom + 1 >= evt->u.update.top &&
190                 evt->u.update.bottom + 1 >= last->u.update.top)
191             {
192                 last->u.update.top = min(last->u.update.top, evt->u.update.top);
193                 last->u.update.bottom = max(last->u.update.bottom, evt->u.update.bottom);
194                 collapsed = TRUE;
195             }
196         }
197     }
198     if (!collapsed)
199     {
200         if (evts->num_used == evts->num_alloc)
201         {
202             evts->num_alloc += 16;
203             evts->events = realloc( evts->events, evts->num_alloc * sizeof(*evt) );
204             assert(evts->events);
205         }
206         evts->events[evts->num_used++] = *evt;
207     }
208     wake_up( &evts->obj, 0 );
209 }
210
211 /* retrieves events from the console's renderer events list */
212 static void console_input_events_get( struct console_input_events* evts )
213 {
214     data_size_t num = get_reply_max_size() / sizeof(evts->events[0]);
215
216     if (num > evts->num_used) num = evts->num_used;
217     set_reply_data( evts->events, num * sizeof(evts->events[0]) );
218     if (num < evts->num_used)
219     {
220         memmove( &evts->events[0], &evts->events[num],
221                  (evts->num_used - num) * sizeof(evts->events[0]) );
222     }
223     evts->num_used -= num;
224 }
225
226 static struct console_input_events *create_console_input_events(void)
227 {
228     struct console_input_events*        evt;
229
230     if (!(evt = alloc_object( &console_input_events_ops ))) return NULL;
231     evt->num_alloc = evt->num_used = 0;
232     evt->events = NULL;
233     return evt;
234 }
235
236 static struct object *create_console_input( struct thread* renderer )
237 {
238     struct console_input *console_input;
239
240     if (!(console_input = alloc_object( &console_input_ops ))) return NULL;
241     console_input->renderer      = renderer;
242     console_input->mode          = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT |
243                                    ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT;
244     console_input->num_proc      = 0;
245     console_input->active        = NULL;
246     console_input->recnum        = 0;
247     console_input->records       = NULL;
248     console_input->evt           = create_console_input_events();
249     console_input->title         = NULL;
250     console_input->history_size  = 50;
251     console_input->history       = calloc( console_input->history_size, sizeof(WCHAR*) );
252     console_input->history_index = 0;
253     console_input->history_mode  = 0;
254     console_input->edition_mode  = 0;
255     console_input->event         = create_event( NULL, NULL, 0, 1, 0 );
256
257     if (!console_input->history || !console_input->evt)
258     {
259         release_object( console_input );
260         return NULL;
261     }
262     return &console_input->obj;
263 }
264
265 static struct screen_buffer *create_console_output( struct console_input *console_input )
266 {
267     struct screen_buffer *screen_buffer;
268     int i;
269
270     if (!(screen_buffer = alloc_object( &screen_buffer_ops ))) return NULL;
271     screen_buffer->mode           = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT;
272     screen_buffer->input          = console_input;
273     screen_buffer->cursor_size    = 100;
274     screen_buffer->cursor_visible = 1;
275     screen_buffer->width          = 80;
276     screen_buffer->height         = 150;
277     screen_buffer->max_width      = 80;
278     screen_buffer->max_height     = 25;
279     screen_buffer->cursor_x       = 0;
280     screen_buffer->cursor_y       = 0;
281     screen_buffer->attr           = 0x0F;
282     screen_buffer->win.left       = 0;
283     screen_buffer->win.right      = screen_buffer->max_width - 1;
284     screen_buffer->win.top        = 0;
285     screen_buffer->win.bottom     = screen_buffer->max_height - 1;
286
287     list_add_head( &screen_buffer_list, &screen_buffer->entry );
288
289     if (!(screen_buffer->data = malloc( screen_buffer->width * screen_buffer->height *
290                                         sizeof(*screen_buffer->data) )))
291     {
292         release_object( screen_buffer );
293         return NULL;
294     }
295     /* clear the first row */
296     for (i = 0; i < screen_buffer->width; i++) screen_buffer->data[i] = empty_char_info;
297     /* and copy it to all other rows */
298     for (i = 1; i < screen_buffer->height; i++)
299         memcpy( &screen_buffer->data[i * screen_buffer->width], screen_buffer->data,
300                 screen_buffer->width * sizeof(char_info_t) );
301
302     if (!console_input->active)
303     {
304         struct console_renderer_event evt;
305         console_input->active = (struct screen_buffer*)grab_object( screen_buffer );
306
307         /* generate the initial events */
308         evt.event = CONSOLE_RENDERER_ACTIVE_SB_EVENT;
309         memset(&evt.u, 0, sizeof(evt.u));
310         console_input_events_append( console_input->evt, &evt );
311
312         evt.event = CONSOLE_RENDERER_SB_RESIZE_EVENT;
313         evt.u.resize.width  = screen_buffer->width;
314         evt.u.resize.height = screen_buffer->height;
315         console_input_events_append( console_input->evt, &evt );
316
317         evt.event = CONSOLE_RENDERER_DISPLAY_EVENT;
318         evt.u.display.left   = screen_buffer->win.left;
319         evt.u.display.top    = screen_buffer->win.top;
320         evt.u.display.width  = screen_buffer->win.right - screen_buffer->win.left + 1;
321         evt.u.display.height = screen_buffer->win.bottom - screen_buffer->win.top + 1;
322         console_input_events_append( console_input->evt, &evt );
323
324         evt.event = CONSOLE_RENDERER_UPDATE_EVENT;
325         evt.u.update.top    = 0;
326         evt.u.update.bottom = screen_buffer->height - 1;
327         console_input_events_append( console_input->evt, &evt );
328
329         evt.event = CONSOLE_RENDERER_CURSOR_GEOM_EVENT;
330         evt.u.cursor_geom.size    = screen_buffer->cursor_size;
331         evt.u.cursor_geom.visible = screen_buffer->cursor_visible;
332         console_input_events_append( console_input->evt, &evt );
333
334         evt.event = CONSOLE_RENDERER_CURSOR_POS_EVENT;
335         evt.u.cursor_pos.x = screen_buffer->cursor_x;
336         evt.u.cursor_pos.y = screen_buffer->cursor_y;
337         console_input_events_append( console_input->evt, &evt );
338     }
339     return screen_buffer;
340 }
341
342 /* free the console for this process */
343 int free_console( struct process *process )
344 {
345     struct console_input* console = process->console;
346
347     if (!console || !console->renderer) return 0;
348
349     process->console = NULL;
350     if (--console->num_proc == 0)
351     {
352         /* all processes have terminated... tell the renderer to terminate too */
353         struct console_renderer_event evt;
354         evt.event = CONSOLE_RENDERER_EXIT_EVENT;
355         memset(&evt.u, 0, sizeof(evt.u));
356         console_input_events_append( console->evt, &evt );
357     }
358     release_object( console );
359
360     return 1;
361 }
362
363 /* let process inherit the console from parent... this handle two cases :
364  *      1/ generic console inheritance
365  *      2/ parent is a renderer which launches process, and process should attach to the console
366  *         renderered by parent
367  */
368 void inherit_console(struct thread *parent_thread, struct process *process, obj_handle_t hconin)
369 {
370     int done = 0;
371     struct process* parent = parent_thread->process;
372
373     /* if parent is a renderer, then attach current process to its console
374      * a bit hacky....
375      */
376     if (hconin)
377     {
378         struct console_input* console;
379
380         /* FIXME: should we check some access rights ? */
381         if ((console = (struct console_input*)get_handle_obj( parent, hconin,
382                                                               0, &console_input_ops )))
383         {
384             if (console->renderer == parent_thread)
385             {
386                 process->console = (struct console_input*)grab_object( console );
387                 process->console->num_proc++;
388                 done = 1;
389             }
390             release_object( console );
391         }
392         else clear_error();  /* ignore error */
393     }
394     /* otherwise, if parent has a console, attach child to this console */
395     if (!done && parent->console)
396     {
397         assert(parent->console->renderer);
398         process->console = (struct console_input*)grab_object( parent->console );
399         process->console->num_proc++;
400     }
401 }
402
403 static struct console_input* console_input_get( obj_handle_t handle, unsigned access )
404 {
405     struct console_input*       console = NULL;
406
407     if (handle)
408         console = (struct console_input *)get_handle_obj( current->process, handle,
409                                                           access, &console_input_ops );
410     else if (current->process->console)
411     {
412         assert( current->process->console->renderer );
413         console = (struct console_input *)grab_object( current->process->console );
414     }
415
416     if (!console && !get_error()) set_error(STATUS_INVALID_PARAMETER);
417     return console;
418 }
419
420 struct console_signal_info
421 {
422     struct console_input        *console;
423     process_id_t                 group;
424     int                          signal;
425 };
426
427 static int propagate_console_signal_cb(struct process *process, void *user)
428 {
429     struct console_signal_info* csi = (struct console_signal_info*)user;
430
431     if (process->console == csi->console && process->running_threads &&
432         (!csi->group || process->group_id == csi->group))
433     {
434         /* find a suitable thread to signal */
435         struct thread *thread;
436         LIST_FOR_EACH_ENTRY( thread, &process->thread_list, struct thread, proc_entry )
437         {
438             if (send_thread_signal( thread, csi->signal )) break;
439         }
440     }
441     return FALSE;
442 }
443
444 static void propagate_console_signal( struct console_input *console,
445                                       int sig, process_id_t group_id )
446 {
447     struct console_signal_info csi;
448
449     if (!console)
450     {
451         set_error( STATUS_INVALID_PARAMETER );
452         return;
453     }
454     /* FIXME: should support the other events (like CTRL_BREAK) */
455     if (sig != CTRL_C_EVENT)
456     {
457         set_error( STATUS_NOT_IMPLEMENTED );
458         return;
459     }
460     csi.console = console;
461     csi.signal  = SIGINT;
462     csi.group   = group_id;
463
464     enum_processes(propagate_console_signal_cb, &csi);
465 }
466
467 static int get_console_mode( obj_handle_t handle )
468 {
469     struct object *obj;
470     int ret = 0;
471
472     if ((obj = get_handle_obj( current->process, handle, CONSOLE_READ, NULL )))
473     {
474         if (obj->ops == &console_input_ops)
475             ret = ((struct console_input *)obj)->mode;
476         else if (obj->ops == &screen_buffer_ops)
477             ret = ((struct screen_buffer *)obj)->mode;
478         else
479             set_error( STATUS_OBJECT_TYPE_MISMATCH );
480         release_object( obj );
481     }
482     return ret;
483 }
484
485 /* changes the mode of either a console input or a screen buffer */
486 static int set_console_mode( obj_handle_t handle, int mode )
487 {
488     struct object *obj;
489     int ret = 0;
490
491     if (!(obj = get_handle_obj( current->process, handle, CONSOLE_WRITE, NULL )))
492         return 0;
493     if (obj->ops == &console_input_ops)
494     {
495         /* FIXME: if we remove the edit mode bits, we need (???) to clean up the history */
496         ((struct console_input *)obj)->mode = mode;
497         ret = 1;
498     }
499     else if (obj->ops == &screen_buffer_ops)
500     {
501         ((struct screen_buffer *)obj)->mode = mode;
502         ret = 1;
503     }
504     else set_error( STATUS_OBJECT_TYPE_MISMATCH );
505     release_object( obj );
506     return ret;
507 }
508
509 /* add input events to a console input queue */
510 static int write_console_input( struct console_input* console, int count,
511                                 const INPUT_RECORD *records )
512 {
513     INPUT_RECORD *new_rec;
514
515     if (!count) return 0;
516     if (!(new_rec = realloc( console->records,
517                              (console->recnum + count) * sizeof(INPUT_RECORD) )))
518     {
519         set_error( STATUS_NO_MEMORY );
520         release_object( console );
521         return -1;
522     }
523     console->records = new_rec;
524     memcpy( new_rec + console->recnum, records, count * sizeof(INPUT_RECORD) );
525
526     if (console->mode & ENABLE_PROCESSED_INPUT)
527     {
528         int i = 0;
529         while (i < count)
530         {
531             if (records[i].EventType == KEY_EVENT &&
532                 records[i].Event.KeyEvent.uChar.UnicodeChar == 'C' - 64 &&
533                 !(records[i].Event.KeyEvent.dwControlKeyState & ENHANCED_KEY))
534             {
535                 if (i != count - 1)
536                     memcpy( &console->records[console->recnum + i],
537                             &console->records[console->recnum + i + 1],
538                             (count - i - 1) * sizeof(INPUT_RECORD) );
539                 count--;
540                 if (records[i].Event.KeyEvent.bKeyDown)
541                 {
542                     /* send SIGINT to all processes attached to this console */
543                     propagate_console_signal( console, CTRL_C_EVENT, 0 );
544                 }
545             }
546             else i++;
547         }
548     }
549     if (!console->recnum && count) set_event( console->event );
550     console->recnum += count;
551     return count;
552 }
553
554 /* retrieve a pointer to the console input records */
555 static int read_console_input( obj_handle_t handle, int count, int flush )
556 {
557     struct console_input *console;
558
559     if (!(console = (struct console_input *)get_handle_obj( current->process, handle,
560                                                             CONSOLE_READ, &console_input_ops )))
561         return -1;
562
563     if (!count)
564     {
565         /* special case: do not retrieve anything, but return
566          * the total number of records available */
567         count = console->recnum;
568     }
569     else
570     {
571         if (count > console->recnum) count = console->recnum;
572         set_reply_data( console->records, count * sizeof(INPUT_RECORD) );
573     }
574     if (flush)
575     {
576         int i;
577         for (i = count; i < console->recnum; i++)
578             console->records[i-count] = console->records[i];
579         if ((console->recnum -= count) > 0)
580         {
581             INPUT_RECORD *new_rec = realloc( console->records,
582                                              console->recnum * sizeof(INPUT_RECORD) );
583             if (new_rec) console->records = new_rec;
584         }
585         else
586         {
587             free( console->records );
588             console->records = NULL;
589             reset_event( console->event );
590         }
591     }
592     release_object( console );
593     return count;
594 }
595
596 /* set misc console input information */
597 static int set_console_input_info( const struct set_console_input_info_request *req,
598                                    const WCHAR *title, data_size_t len )
599 {
600     struct console_input *console;
601     struct console_renderer_event evt;
602
603     if (!(console = console_input_get( req->handle, CONSOLE_WRITE ))) goto error;
604
605     memset(&evt.u, 0, sizeof(evt.u));
606     if (req->mask & SET_CONSOLE_INPUT_INFO_ACTIVE_SB)
607     {
608         struct screen_buffer *screen_buffer;
609
610         screen_buffer = (struct screen_buffer *)get_handle_obj( current->process, req->active_sb,
611                                                                 CONSOLE_READ, &screen_buffer_ops );
612         if (!screen_buffer || screen_buffer->input != console)
613         {
614             set_error( STATUS_INVALID_PARAMETER );
615             if (screen_buffer) release_object( screen_buffer );
616             goto error;
617         }
618
619         if (screen_buffer != console->active)
620         {
621             if (console->active) release_object( console->active );
622             console->active = screen_buffer;
623             evt.event = CONSOLE_RENDERER_ACTIVE_SB_EVENT;
624             console_input_events_append( console->evt, &evt );
625         }
626         else
627             release_object( screen_buffer );
628     }
629     if (req->mask & SET_CONSOLE_INPUT_INFO_TITLE)
630     {
631         WCHAR *new_title = mem_alloc( len + sizeof(WCHAR) );
632         if (new_title)
633         {
634             memcpy( new_title, title, len );
635             new_title[len / sizeof(WCHAR)] = 0;
636             free( console->title );
637             console->title = new_title;
638             evt.event = CONSOLE_RENDERER_TITLE_EVENT;
639             console_input_events_append( console->evt, &evt );
640         }
641     }
642     if (req->mask & SET_CONSOLE_INPUT_INFO_HISTORY_MODE)
643     {
644         console->history_mode = req->history_mode;
645     }
646     if ((req->mask & SET_CONSOLE_INPUT_INFO_HISTORY_SIZE) &&
647         console->history_size != req->history_size)
648     {
649         WCHAR** mem = NULL;
650         int     i;
651         int     delta;
652
653         if (req->history_size)
654         {
655             mem = mem_alloc( req->history_size * sizeof(WCHAR*) );
656             if (!mem) goto error;
657             memset( mem, 0, req->history_size * sizeof(WCHAR*) );
658         }
659
660         delta = (console->history_index > req->history_size) ?
661             (console->history_index - req->history_size) : 0;
662
663         for (i = delta; i < console->history_index; i++)
664         {
665             mem[i - delta] = console->history[i];
666             console->history[i] = NULL;
667         }
668         console->history_index -= delta;
669
670         for (i = 0; i < console->history_size; i++)
671             if (console->history[i]) free( console->history[i] );
672         free( console->history );
673         console->history = mem;
674         console->history_size = req->history_size;
675     }
676     if (req->mask & SET_CONSOLE_INPUT_INFO_EDITION_MODE)
677     {
678         console->edition_mode = req->edition_mode;
679     }
680     release_object( console );
681     return 1;
682  error:
683     if (console) release_object( console );
684     return 0;
685 }
686
687 /* resize a screen buffer */
688 static int change_screen_buffer_size( struct screen_buffer *screen_buffer,
689                                       int new_width, int new_height )
690 {
691     int i, old_width, old_height, copy_width, copy_height;
692     char_info_t *new_data;
693
694     if (!(new_data = malloc( new_width * new_height * sizeof(*new_data) )))
695     {
696         set_error( STATUS_NO_MEMORY );
697         return 0;
698     }
699     old_width = screen_buffer->width;
700     old_height = screen_buffer->height;
701     copy_width = min( old_width, new_width );
702     copy_height = min( old_height, new_height );
703
704     /* copy all the rows */
705     for (i = 0; i < copy_height; i++)
706     {
707         memcpy( &new_data[i * new_width], &screen_buffer->data[i * old_width],
708                 copy_width * sizeof(char_info_t) );
709     }
710
711     /* clear the end of each row */
712     if (new_width > old_width)
713     {
714         /* fill first row */
715         for (i = old_width; i < new_width; i++) new_data[i] = empty_char_info;
716         /* and blast it to the other rows */
717         for (i = 1; i < copy_height; i++)
718             memcpy( &new_data[i * new_width + old_width], &new_data[old_width],
719                     (new_width - old_width) * sizeof(char_info_t) );
720     }
721
722     /* clear remaining rows */
723     if (new_height > old_height)
724     {
725         /* fill first row */
726         for (i = 0; i < new_width; i++) new_data[old_height * new_width + i] = empty_char_info;
727         /* and blast it to the other rows */
728         for (i = old_height+1; i < new_height; i++)
729             memcpy( &new_data[i * new_width], &new_data[old_height * new_width],
730                     new_width * sizeof(char_info_t) );
731     }
732     free( screen_buffer->data );
733     screen_buffer->data = new_data;
734     screen_buffer->width = new_width;
735     screen_buffer->height = new_height;
736     return 1;
737 }
738
739 /* set misc screen buffer information */
740 static int set_console_output_info( struct screen_buffer *screen_buffer,
741                                     const struct set_console_output_info_request *req )
742 {
743     struct console_renderer_event evt;
744
745     memset(&evt.u, 0, sizeof(evt.u));
746     if (req->mask & SET_CONSOLE_OUTPUT_INFO_CURSOR_GEOM)
747     {
748         if (req->cursor_size < 1 || req->cursor_size > 100)
749         {
750             set_error( STATUS_INVALID_PARAMETER );
751             return 0;
752         }
753         if (screen_buffer->cursor_size != req->cursor_size ||
754             screen_buffer->cursor_visible != req->cursor_visible)
755         {
756             screen_buffer->cursor_size    = req->cursor_size;
757             screen_buffer->cursor_visible = req->cursor_visible;
758             evt.event = CONSOLE_RENDERER_CURSOR_GEOM_EVENT;
759             evt.u.cursor_geom.size    = req->cursor_size;
760             evt.u.cursor_geom.visible = req->cursor_visible;
761             console_input_events_append( screen_buffer->input->evt, &evt );
762         }
763     }
764     if (req->mask & SET_CONSOLE_OUTPUT_INFO_CURSOR_POS)
765     {
766         if (req->cursor_x < 0 || req->cursor_x >= screen_buffer->width ||
767             req->cursor_y < 0 || req->cursor_y >= screen_buffer->height)
768         {
769             set_error( STATUS_INVALID_PARAMETER );
770             return 0;
771         }
772         if (screen_buffer->cursor_x != req->cursor_x || screen_buffer->cursor_y != req->cursor_y)
773         {
774             screen_buffer->cursor_x       = req->cursor_x;
775             screen_buffer->cursor_y       = req->cursor_y;
776             evt.event = CONSOLE_RENDERER_CURSOR_POS_EVENT;
777             evt.u.cursor_pos.x = req->cursor_x;
778             evt.u.cursor_pos.y = req->cursor_y;
779             console_input_events_append( screen_buffer->input->evt, &evt );
780         }
781     }
782     if (req->mask & SET_CONSOLE_OUTPUT_INFO_SIZE)
783     {
784         unsigned cc;
785
786         /* new screen-buffer cannot be smaller than actual window */
787         if (req->width < screen_buffer->win.right - screen_buffer->win.left + 1 ||
788             req->height < screen_buffer->win.bottom - screen_buffer->win.top + 1)
789         {
790             set_error( STATUS_INVALID_PARAMETER );
791             return 0;
792         }
793         /* FIXME: there are also some basic minimum and max size to deal with */
794         if (!change_screen_buffer_size( screen_buffer, req->width, req->height )) return 0;
795
796         evt.event = CONSOLE_RENDERER_SB_RESIZE_EVENT;
797         evt.u.resize.width  = req->width;
798         evt.u.resize.height = req->height;
799         console_input_events_append( screen_buffer->input->evt, &evt );
800
801         evt.event = CONSOLE_RENDERER_UPDATE_EVENT;
802         evt.u.update.top    = 0;
803         evt.u.update.bottom = screen_buffer->height - 1;
804         console_input_events_append( screen_buffer->input->evt, &evt );
805
806         /* scroll window to display sb */
807         if (screen_buffer->win.right >= req->width)
808         {       
809             screen_buffer->win.right -= screen_buffer->win.left;
810             screen_buffer->win.left = 0;
811         }
812         if (screen_buffer->win.bottom >= req->height)
813         {       
814             screen_buffer->win.bottom -= screen_buffer->win.top;
815             screen_buffer->win.top = 0;
816         }
817         /* reset cursor if needed (normally, if cursor was outside of new sb, the
818          * window has been shifted so that the new position of the cursor will be 
819          * visible */
820         cc = 0;
821         if (screen_buffer->cursor_x >= req->width)
822         {
823             screen_buffer->cursor_x = req->width - 1;
824             cc++;
825         }
826         if (screen_buffer->cursor_y >= req->height)
827         {
828             screen_buffer->cursor_y = req->height - 1;
829             cc++;
830         }
831         if (cc)
832         {
833             evt.event = CONSOLE_RENDERER_CURSOR_POS_EVENT;
834             evt.u.cursor_pos.x = req->cursor_x;
835             evt.u.cursor_pos.y = req->cursor_y;
836             console_input_events_append( screen_buffer->input->evt, &evt );
837         }
838
839         if (screen_buffer == screen_buffer->input->active &&
840             screen_buffer->input->mode & ENABLE_WINDOW_INPUT)
841         {
842             INPUT_RECORD        ir;
843             ir.EventType = WINDOW_BUFFER_SIZE_EVENT;
844             ir.Event.WindowBufferSizeEvent.dwSize.X = req->width;
845             ir.Event.WindowBufferSizeEvent.dwSize.Y = req->height;
846             write_console_input( screen_buffer->input, 1, &ir );
847         }
848     }
849     if (req->mask & SET_CONSOLE_OUTPUT_INFO_ATTR)
850     {
851         screen_buffer->attr = req->attr;
852     }
853     if (req->mask & SET_CONSOLE_OUTPUT_INFO_DISPLAY_WINDOW)
854     {
855         if (req->win_left < 0 || req->win_left > req->win_right ||
856             req->win_right >= screen_buffer->width ||
857             req->win_top < 0  || req->win_top > req->win_bottom ||
858             req->win_bottom >= screen_buffer->height)
859         {
860             set_error( STATUS_INVALID_PARAMETER );
861             return 0;
862         }
863         if (screen_buffer->win.left != req->win_left || screen_buffer->win.top != req->win_top ||
864             screen_buffer->win.right != req->win_right || screen_buffer->win.bottom != req->win_bottom)
865         {
866             screen_buffer->win.left   = req->win_left;
867             screen_buffer->win.top    = req->win_top;
868             screen_buffer->win.right  = req->win_right;
869             screen_buffer->win.bottom = req->win_bottom;
870             evt.event = CONSOLE_RENDERER_DISPLAY_EVENT;
871             evt.u.display.left   = req->win_left;
872             evt.u.display.top    = req->win_top;
873             evt.u.display.width  = req->win_right - req->win_left + 1;
874             evt.u.display.height = req->win_bottom - req->win_top + 1;
875             console_input_events_append( screen_buffer->input->evt, &evt );
876         }
877     }
878     if (req->mask & SET_CONSOLE_OUTPUT_INFO_MAX_SIZE)
879     {
880         /* can only be done by renderer */
881         if (current->process->console != screen_buffer->input)
882         {
883             set_error( STATUS_INVALID_PARAMETER );
884             return 0;
885         }
886
887         screen_buffer->max_width  = req->max_width;
888         screen_buffer->max_height = req->max_height;
889     }
890
891     return 1;
892 }
893
894 /* appends a new line to history (history is a fixed size array) */
895 static void console_input_append_hist( struct console_input* console, const WCHAR* buf, data_size_t len )
896 {
897     WCHAR*      ptr = mem_alloc( (len + 1) * sizeof(WCHAR) );
898
899     if (!ptr)
900         return;
901
902     if (!console || !console->history_size)
903     {
904         set_error( STATUS_INVALID_PARAMETER ); /* FIXME */
905         free( ptr );
906         return;
907     }
908
909     memcpy( ptr, buf, len * sizeof(WCHAR) );
910     ptr[len] = 0;
911
912     if (console->history_mode && console->history_index &&
913         strncmpW( console->history[console->history_index - 1], ptr, len * sizeof(WCHAR) ) == 0)
914     {
915         /* ok, mode ask us to not use twice the same string...
916          * so just free mem and returns
917          */
918         set_error( STATUS_ALIAS_EXISTS );
919         free(ptr);
920         return;
921     }
922
923     if (console->history_index < console->history_size)
924     {
925         console->history[console->history_index++] = ptr;
926     }
927     else
928     {
929         free( console->history[0]) ;
930         memmove( &console->history[0], &console->history[1],
931                  (console->history_size - 1) * sizeof(WCHAR*) );
932         console->history[console->history_size - 1] = ptr;
933     }
934 }
935
936 /* returns a line from the cache */
937 static data_size_t console_input_get_hist( struct console_input *console, int index )
938 {
939     data_size_t ret = 0;
940
941     if (index >= console->history_index) set_error( STATUS_INVALID_PARAMETER );
942     else
943     {
944         ret = strlenW( console->history[index] ) * sizeof(WCHAR);
945         set_reply_data( console->history[index], min( ret, get_reply_max_size() ));
946     }
947     return ret;
948 }
949
950 /* dumb dump */
951 static void console_input_dump( struct object *obj, int verbose )
952 {
953     struct console_input *console = (struct console_input *)obj;
954     assert( obj->ops == &console_input_ops );
955     fprintf( stderr, "Console input active=%p evt=%p\n",
956              console->active, console->evt );
957 }
958
959 static void console_input_destroy( struct object *obj )
960 {
961     struct console_input*       console_in = (struct console_input *)obj;
962     struct screen_buffer*       curr;
963     int                         i;
964
965     assert( obj->ops == &console_input_ops );
966     free( console_in->title );
967     free( console_in->records );
968
969     if (console_in->active) release_object( console_in->active );
970     console_in->active = NULL;
971
972     LIST_FOR_EACH_ENTRY( curr, &screen_buffer_list, struct screen_buffer, entry )
973     {
974         if (curr->input == console_in) curr->input = NULL;
975     }
976
977     release_object( console_in->evt );
978     console_in->evt = NULL;
979     release_object( console_in->event );
980
981     for (i = 0; i < console_in->history_size; i++)
982         if (console_in->history[i]) free( console_in->history[i] );
983     free( console_in->history );
984 }
985
986 static void screen_buffer_dump( struct object *obj, int verbose )
987 {
988     struct screen_buffer *screen_buffer = (struct screen_buffer *)obj;
989     assert( obj->ops == &screen_buffer_ops );
990
991     fprintf(stderr, "Console screen buffer input=%p\n", screen_buffer->input );
992 }
993
994 static void screen_buffer_destroy( struct object *obj )
995 {
996     struct screen_buffer *screen_buffer = (struct screen_buffer *)obj;
997
998     assert( obj->ops == &screen_buffer_ops );
999
1000     list_remove( &screen_buffer->entry );
1001
1002     if (screen_buffer->input && screen_buffer->input->active == screen_buffer)
1003     {
1004         struct screen_buffer *sb;
1005
1006         screen_buffer->input->active = NULL;
1007         LIST_FOR_EACH_ENTRY( sb, &screen_buffer_list, struct screen_buffer, entry )
1008         {
1009             if (sb->input == screen_buffer->input)
1010             {
1011                 sb->input->active = sb;
1012                 break;
1013             }
1014         }
1015     }
1016     free( screen_buffer->data );
1017 }
1018
1019 /* write data into a screen buffer */
1020 static int write_console_output( struct screen_buffer *screen_buffer, data_size_t size,
1021                                  const void* data, enum char_info_mode mode,
1022                                  int x, int y, int wrap )
1023 {
1024     unsigned int i;
1025     char_info_t *end, *dest = screen_buffer->data + y * screen_buffer->width + x;
1026
1027     if (y >= screen_buffer->height) return 0;
1028
1029     if (wrap)
1030         end = screen_buffer->data + screen_buffer->height * screen_buffer->width;
1031     else
1032         end = screen_buffer->data + (y+1) * screen_buffer->width;
1033
1034     switch(mode)
1035     {
1036     case CHAR_INFO_MODE_TEXT:
1037         {
1038             const WCHAR *ptr = data;
1039             for (i = 0; i < size/sizeof(*ptr) && dest < end; dest++, i++) dest->ch = ptr[i];
1040         }
1041         break;
1042     case CHAR_INFO_MODE_ATTR:
1043         {
1044             const unsigned short *ptr = data;
1045             for (i = 0; i < size/sizeof(*ptr) && dest < end; dest++, i++) dest->attr = ptr[i];
1046         }
1047         break;
1048     case CHAR_INFO_MODE_TEXTATTR:
1049         {
1050             const char_info_t *ptr = data;
1051             for (i = 0; i < size/sizeof(*ptr) && dest < end; dest++, i++) *dest = ptr[i];
1052         }
1053         break;
1054     case CHAR_INFO_MODE_TEXTSTDATTR:
1055         {
1056             const WCHAR *ptr = data;
1057             for (i = 0; i < size/sizeof(*ptr) && dest < end; dest++, i++)
1058             {
1059                 dest->ch   = ptr[i];
1060                 dest->attr = screen_buffer->attr;
1061             }
1062         }
1063         break;
1064     default:
1065         set_error( STATUS_INVALID_PARAMETER );
1066         return 0;
1067     }
1068
1069     if (i && screen_buffer == screen_buffer->input->active)
1070     {
1071         struct console_renderer_event evt;
1072         evt.event = CONSOLE_RENDERER_UPDATE_EVENT;
1073         memset(&evt.u, 0, sizeof(evt.u));
1074         evt.u.update.top    = y + x / screen_buffer->width;
1075         evt.u.update.bottom = y + (x + i - 1) / screen_buffer->width;
1076         console_input_events_append( screen_buffer->input->evt, &evt );
1077     }
1078     return i;
1079 }
1080
1081 /* fill a screen buffer with uniform data */
1082 static int fill_console_output( struct screen_buffer *screen_buffer, char_info_t data,
1083                                 enum char_info_mode mode, int x, int y, int count, int wrap )
1084 {
1085     int i;
1086     char_info_t *end, *dest = screen_buffer->data + y * screen_buffer->width + x;
1087
1088     if (y >= screen_buffer->height) return 0;
1089
1090     if (wrap)
1091         end = screen_buffer->data + screen_buffer->height * screen_buffer->width;
1092     else
1093         end = screen_buffer->data + (y+1) * screen_buffer->width;
1094
1095     if (count > end - dest) count = end - dest;
1096
1097     switch(mode)
1098     {
1099     case CHAR_INFO_MODE_TEXT:
1100         for (i = 0; i < count; i++) dest[i].ch = data.ch;
1101         break;
1102     case CHAR_INFO_MODE_ATTR:
1103         for (i = 0; i < count; i++) dest[i].attr = data.attr;
1104         break;
1105     case CHAR_INFO_MODE_TEXTATTR:
1106         for (i = 0; i < count; i++) dest[i] = data;
1107         break;
1108     case CHAR_INFO_MODE_TEXTSTDATTR:
1109         for (i = 0; i < count; i++)
1110         {
1111             dest[i].ch   = data.ch;
1112             dest[i].attr = screen_buffer->attr;
1113         }
1114         break;
1115     default:
1116         set_error( STATUS_INVALID_PARAMETER );
1117         return 0;
1118     }
1119
1120     if (count && screen_buffer == screen_buffer->input->active)
1121     {
1122         struct console_renderer_event evt;
1123         evt.event = CONSOLE_RENDERER_UPDATE_EVENT;
1124         memset(&evt.u, 0, sizeof(evt.u));
1125         evt.u.update.top    = y;
1126         evt.u.update.bottom = (y * screen_buffer->width + x + count - 1) / screen_buffer->width;
1127         console_input_events_append( screen_buffer->input->evt, &evt );
1128     }
1129     return i;
1130 }
1131
1132 /* read data from a screen buffer */
1133 static void read_console_output( struct screen_buffer *screen_buffer, int x, int y,
1134                                  enum char_info_mode mode, int wrap )
1135 {
1136     int i;
1137     char_info_t *end, *src = screen_buffer->data + y * screen_buffer->width + x;
1138
1139     if (y >= screen_buffer->height) return;
1140
1141     if (wrap)
1142         end = screen_buffer->data + screen_buffer->height * screen_buffer->width;
1143     else
1144         end = screen_buffer->data + (y+1) * screen_buffer->width;
1145
1146     switch(mode)
1147     {
1148     case CHAR_INFO_MODE_TEXT:
1149         {
1150             WCHAR *data;
1151             int count = min( end - src, get_reply_max_size() / sizeof(*data) );
1152             if ((data = set_reply_data_size( count * sizeof(*data) )))
1153             {
1154                 for (i = 0; i < count; i++) data[i] = src[i].ch;
1155             }
1156         }
1157         break;
1158     case CHAR_INFO_MODE_ATTR:
1159         {
1160             unsigned short *data;
1161             int count = min( end - src, get_reply_max_size() / sizeof(*data) );
1162             if ((data = set_reply_data_size( count * sizeof(*data) )))
1163             {
1164                 for (i = 0; i < count; i++) data[i] = src[i].attr;
1165             }
1166         }
1167         break;
1168     case CHAR_INFO_MODE_TEXTATTR:
1169         {
1170             char_info_t *data;
1171             int count = min( end - src, get_reply_max_size() / sizeof(*data) );
1172             if ((data = set_reply_data_size( count * sizeof(*data) )))
1173             {
1174                 for (i = 0; i < count; i++) data[i] = src[i];
1175             }
1176         }
1177         break;
1178     default:
1179         set_error( STATUS_INVALID_PARAMETER );
1180         break;
1181     }
1182 }
1183
1184 /* scroll parts of a screen buffer */
1185 static void scroll_console_output( obj_handle_t handle, int xsrc, int ysrc, int xdst, int ydst,
1186                                    int w, int h )
1187 {
1188     struct screen_buffer *screen_buffer;
1189     int                         j;
1190     char_info_t *psrc, *pdst;
1191     struct console_renderer_event evt;
1192
1193     if (!(screen_buffer = (struct screen_buffer *)get_handle_obj( current->process, handle,
1194                                                                   CONSOLE_READ, &screen_buffer_ops )))
1195         return;
1196     if (xsrc < 0 || ysrc < 0 || xdst < 0 || ydst < 0 ||
1197         xsrc + w > screen_buffer->width  ||
1198         xdst + w > screen_buffer->width  ||
1199         ysrc + h > screen_buffer->height ||
1200         ydst + h > screen_buffer->height ||
1201         w == 0 || h == 0)
1202     {
1203         set_error( STATUS_INVALID_PARAMETER );
1204         release_object( screen_buffer );
1205         return;
1206     }
1207
1208     if (ysrc < ydst)
1209     {
1210         psrc = &screen_buffer->data[(ysrc + h - 1) * screen_buffer->width + xsrc];
1211         pdst = &screen_buffer->data[(ydst + h - 1) * screen_buffer->width + xdst];
1212
1213         for (j = h; j > 0; j--)
1214         {
1215             memcpy(pdst, psrc, w * sizeof(*pdst) );
1216             pdst -= screen_buffer->width;
1217             psrc -= screen_buffer->width;
1218         }
1219     }
1220     else
1221     {
1222         psrc = &screen_buffer->data[ysrc * screen_buffer->width + xsrc];
1223         pdst = &screen_buffer->data[ydst * screen_buffer->width + xdst];
1224
1225         for (j = 0; j < h; j++)
1226         {
1227             /* we use memmove here because when psrc and pdst are the same,
1228              * copies are done on the same row, so the dst and src blocks
1229              * can overlap */
1230             memmove( pdst, psrc, w * sizeof(*pdst) );
1231             pdst += screen_buffer->width;
1232             psrc += screen_buffer->width;
1233         }
1234     }
1235
1236     /* FIXME: this could be enhanced, by signalling scroll */
1237     evt.event = CONSOLE_RENDERER_UPDATE_EVENT;
1238     memset(&evt.u, 0, sizeof(evt.u));
1239     evt.u.update.top    = min(ysrc, ydst);
1240     evt.u.update.bottom = max(ysrc, ydst) + h - 1;
1241     console_input_events_append( screen_buffer->input->evt, &evt );
1242
1243     release_object( screen_buffer );
1244 }
1245
1246 /* allocate a console for the renderer */
1247 DECL_HANDLER(alloc_console)
1248 {
1249     obj_handle_t in = 0;
1250     obj_handle_t evt = 0;
1251     struct process *process;
1252     struct process *renderer = current->process;
1253     struct console_input *console;
1254
1255     if (req->pid)
1256     {
1257         if (!(process = get_process_from_id( req->pid ))) return;
1258     }
1259     else
1260     {
1261         if (!(process = renderer->parent))
1262         {
1263             set_error( STATUS_ACCESS_DENIED );
1264             return;
1265         }
1266         grab_object( process );
1267     }
1268
1269     if (process != renderer && process->console)
1270     {
1271         set_error( STATUS_ACCESS_DENIED );
1272         goto the_end;
1273     }
1274     if ((console = (struct console_input*)create_console_input( current )))
1275     {
1276         if ((in = alloc_handle( renderer, console, req->access, req->attributes )))
1277         {
1278             if ((evt = alloc_handle( renderer, console->evt, SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE, 0 )))
1279             {
1280                 if (process != renderer)
1281                 {
1282                     process->console = (struct console_input*)grab_object( console );
1283                     console->num_proc++;
1284                 }
1285                 reply->handle_in = in;
1286                 reply->event = evt;
1287                 release_object( console );
1288                 goto the_end;
1289             }
1290             close_handle( renderer, in );
1291         }
1292         free_console( process );
1293     }
1294  the_end:
1295     release_object( process );
1296 }
1297
1298 /* free the console of the current process */
1299 DECL_HANDLER(free_console)
1300 {
1301     free_console( current->process );
1302 }
1303
1304 /* let the renderer peek the events it's waiting on */
1305 DECL_HANDLER(get_console_renderer_events)
1306 {
1307     struct console_input_events *evt;
1308
1309     evt = (struct console_input_events *)get_handle_obj( current->process, req->handle,
1310                                                          CONSOLE_READ, &console_input_events_ops );
1311     if (!evt) return;
1312     console_input_events_get( evt );
1313     release_object( evt );
1314 }
1315
1316 /* open a handle to the process console */
1317 DECL_HANDLER(open_console)
1318 {
1319     struct object      *obj = NULL;
1320
1321     reply->handle = 0;
1322     if (req->from == (obj_handle_t)0)
1323     {
1324         if (current->process->console && current->process->console->renderer)
1325             obj = grab_object( (struct object*)current->process->console );
1326     }
1327     else if (req->from == (obj_handle_t)1)
1328     {
1329          if (current->process->console && current->process->console->renderer &&
1330              current->process->console->active)
1331              obj = grab_object( (struct object*)current->process->console->active );
1332     }
1333     else if ((obj = get_handle_obj( current->process, req->from,
1334                                     CONSOLE_READ|CONSOLE_WRITE, &console_input_ops )))
1335     {
1336         struct console_input *console = (struct console_input *)obj;
1337         obj = (console->active) ? grab_object( console->active ) : NULL;
1338         release_object( console );
1339     }
1340
1341     /* FIXME: req->share is not used (as in screen buffer creation)  */
1342     if (obj)
1343     {
1344         reply->handle = alloc_handle( current->process, obj, req->access, req->attributes );
1345         release_object( obj );
1346     }
1347     else if (!get_error()) set_error( STATUS_ACCESS_DENIED );
1348 }
1349
1350 /* set info about a console input */
1351 DECL_HANDLER(set_console_input_info)
1352 {
1353     set_console_input_info( req, get_req_data(), get_req_data_size() );
1354 }
1355
1356 /* get info about a console (output only) */
1357 DECL_HANDLER(get_console_input_info)
1358 {
1359     struct console_input *console;
1360
1361     if (!(console = console_input_get( req->handle, CONSOLE_READ ))) return;
1362     if (console->title)
1363     {
1364         data_size_t len = strlenW( console->title ) * sizeof(WCHAR);
1365         if (len > get_reply_max_size()) len = get_reply_max_size();
1366         set_reply_data( console->title, len );
1367     }
1368     reply->history_mode  = console->history_mode;
1369     reply->history_size  = console->history_size;
1370     reply->history_index = console->history_index;
1371     reply->edition_mode  = console->edition_mode;
1372
1373     release_object( console );
1374 }
1375
1376 /* get a console mode (input or output) */
1377 DECL_HANDLER(get_console_mode)
1378 {
1379     reply->mode = get_console_mode( req->handle );
1380 }
1381
1382 /* set a console mode (input or output) */
1383 DECL_HANDLER(set_console_mode)
1384 {
1385     set_console_mode( req->handle, req->mode );
1386 }
1387
1388 /* add input records to a console input queue */
1389 DECL_HANDLER(write_console_input)
1390 {
1391     struct console_input *console;
1392
1393     reply->written = 0;
1394     if (!(console = (struct console_input *)get_handle_obj( current->process, req->handle,
1395                                                             CONSOLE_WRITE, &console_input_ops )))
1396         return;
1397     reply->written = write_console_input( console, get_req_data_size() / sizeof(INPUT_RECORD),
1398                                           get_req_data() );
1399     release_object( console );
1400 }
1401
1402 /* fetch input records from a console input queue */
1403 DECL_HANDLER(read_console_input)
1404 {
1405     int count = get_reply_max_size() / sizeof(INPUT_RECORD);
1406     reply->read = read_console_input( req->handle, count, req->flush );
1407 }
1408
1409 /* appends a string to console's history */
1410 DECL_HANDLER(append_console_input_history)
1411 {
1412     struct console_input *console;
1413
1414     if (!(console = console_input_get( req->handle, CONSOLE_WRITE ))) return;
1415     console_input_append_hist( console, get_req_data(), get_req_data_size() / sizeof(WCHAR) );
1416     release_object( console );
1417 }
1418
1419 /* appends a string to console's history */
1420 DECL_HANDLER(get_console_input_history)
1421 {
1422     struct console_input *console;
1423
1424     if (!(console = console_input_get( req->handle, CONSOLE_WRITE ))) return;
1425     reply->total = console_input_get_hist( console, req->index );
1426     release_object( console );
1427 }
1428
1429 /* creates a screen buffer */
1430 DECL_HANDLER(create_console_output)
1431 {
1432     struct console_input*       console;
1433     struct screen_buffer*       screen_buffer;
1434
1435     if (!(console = console_input_get( req->handle_in, CONSOLE_WRITE ))) return;
1436
1437     screen_buffer = create_console_output( console );
1438     if (screen_buffer)
1439     {
1440         /* FIXME: should store sharing and test it when opening the CONOUT$ device
1441          * see file.c on how this could be done */
1442         reply->handle_out = alloc_handle( current->process, screen_buffer, req->access, req->attributes );
1443         release_object( screen_buffer );
1444     }
1445     release_object( console );
1446 }
1447
1448 /* set info about a console screen buffer */
1449 DECL_HANDLER(set_console_output_info)
1450 {
1451     struct screen_buffer *screen_buffer;
1452
1453     if ((screen_buffer = (struct screen_buffer*)get_handle_obj( current->process, req->handle,
1454                                                                 CONSOLE_WRITE, &screen_buffer_ops)))
1455     {
1456         set_console_output_info( screen_buffer, req );
1457         release_object( screen_buffer );
1458     }
1459 }
1460
1461 /* get info about a console screen buffer */
1462 DECL_HANDLER(get_console_output_info)
1463 {
1464     struct screen_buffer *screen_buffer;
1465
1466     if ((screen_buffer = (struct screen_buffer *)get_handle_obj( current->process, req->handle,
1467                                                                  CONSOLE_READ, &screen_buffer_ops)))
1468     {
1469         reply->cursor_size    = screen_buffer->cursor_size;
1470         reply->cursor_visible = screen_buffer->cursor_visible;
1471         reply->cursor_x       = screen_buffer->cursor_x;
1472         reply->cursor_y       = screen_buffer->cursor_y;
1473         reply->width          = screen_buffer->width;
1474         reply->height         = screen_buffer->height;
1475         reply->attr           = screen_buffer->attr;
1476         reply->win_left       = screen_buffer->win.left;
1477         reply->win_top        = screen_buffer->win.top;
1478         reply->win_right      = screen_buffer->win.right;
1479         reply->win_bottom     = screen_buffer->win.bottom;
1480         reply->max_width      = screen_buffer->max_width;
1481         reply->max_height     = screen_buffer->max_height;
1482         release_object( screen_buffer );
1483     }
1484 }
1485
1486 /* read data (chars & attrs) from a screen buffer */
1487 DECL_HANDLER(read_console_output)
1488 {
1489     struct screen_buffer *screen_buffer;
1490
1491     if ((screen_buffer = (struct screen_buffer*)get_handle_obj( current->process, req->handle,
1492                                                                 CONSOLE_READ, &screen_buffer_ops )))
1493     {
1494         read_console_output( screen_buffer, req->x, req->y, req->mode, req->wrap );
1495         reply->width  = screen_buffer->width;
1496         reply->height = screen_buffer->height;
1497         release_object( screen_buffer );
1498     }
1499 }
1500
1501 /* write data (char and/or attrs) to a screen buffer */
1502 DECL_HANDLER(write_console_output)
1503 {
1504     struct screen_buffer *screen_buffer;
1505
1506     if ((screen_buffer = (struct screen_buffer*)get_handle_obj( current->process, req->handle,
1507                                                                 CONSOLE_WRITE, &screen_buffer_ops)))
1508     {
1509         reply->written = write_console_output( screen_buffer, get_req_data_size(), get_req_data(),
1510                                                req->mode, req->x, req->y, req->wrap );
1511         reply->width  = screen_buffer->width;
1512         reply->height = screen_buffer->height;
1513         release_object( screen_buffer );
1514     }
1515 }
1516
1517 /* fill a screen buffer with constant data (chars and/or attributes) */
1518 DECL_HANDLER(fill_console_output)
1519 {
1520     struct screen_buffer *screen_buffer;
1521
1522     if ((screen_buffer = (struct screen_buffer*)get_handle_obj( current->process, req->handle,
1523                                                                 CONSOLE_WRITE, &screen_buffer_ops)))
1524     {
1525         reply->written = fill_console_output( screen_buffer, req->data, req->mode,
1526                                               req->x, req->y, req->count, req->wrap );
1527         release_object( screen_buffer );
1528     }
1529 }
1530
1531 /* move a rect of data in a screen buffer */
1532 DECL_HANDLER(move_console_output)
1533 {
1534     scroll_console_output( req->handle, req->x_src, req->y_src, req->x_dst, req->y_dst,
1535                            req->w, req->h );
1536 }
1537
1538 /* sends a signal to a console (process, group...) */
1539 DECL_HANDLER(send_console_signal)
1540 {
1541     process_id_t group;
1542
1543     group = req->group_id ? req->group_id : current->process->group_id;
1544
1545     if (!group)
1546         set_error( STATUS_INVALID_PARAMETER );
1547     else
1548         propagate_console_signal( current->process->console, req->signal, group );
1549 }
1550
1551 /* get console which renderer is 'current' */
1552 static int cgwe_enum( struct process* process, void* user)
1553 {
1554     if (process->console && process->console->renderer == current)
1555     {
1556         *(struct console_input**)user = process->console;
1557         return 1;
1558     }
1559     return 0;
1560 }
1561
1562 DECL_HANDLER(get_console_wait_event)
1563 {
1564     struct console_input* console = NULL;
1565
1566     if (current->process->console && current->process->console->renderer)
1567         console = (struct console_input*)grab_object( (struct object*)current->process->console );
1568     else enum_processes(cgwe_enum, &console);
1569
1570     if (console)
1571     {
1572         reply->handle = alloc_handle( current->process, console->event, EVENT_ALL_ACCESS, 0 );
1573         release_object( console );
1574     }
1575     else set_error( STATUS_INVALID_PARAMETER );
1576 }