Portability fix.
[wine/wine64.git] / win32 / console.c
bloba98c971c913dca9a9a2a4c12c5e952beebfaa9e9
1 /*
2 * Win32 kernel functions
4 * Copyright 1995 Martin von Loewis and Cameron Heide
5 * Copyright 1997 Karl Garrison
6 * Copyright 1998 John Richardson
7 * Copyright 1998 Marcus Meissner
8 * Copyright 2001,2002 Eric Pouech
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 /* Reference applications:
26 * - IDA (interactive disassembler) full version 3.75. Works.
27 * - LYNX/W32. Works mostly, some keys crash it.
30 #include "config.h"
31 #include "wine/port.h"
33 #include <stdio.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <assert.h>
38 #include "winbase.h"
39 #include "winnls.h"
40 #include "winerror.h"
41 #include "wincon.h"
42 #include "heap.h"
43 #include "wine/server.h"
44 #include "wine/exception.h"
45 #include "wine/debug.h"
46 #include "msvcrt/excpt.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(console);
50 /* editline.c */
51 extern WCHAR* CONSOLE_Readline(HANDLE, int);
53 static WCHAR* S_EditString /* = NULL */;
54 static unsigned S_EditStrPos /* = 0 */;
56 /***********************************************************************
57 * FreeConsole (KERNEL32.@)
59 BOOL WINAPI FreeConsole(VOID)
61 BOOL ret;
63 SERVER_START_REQ(free_console)
65 ret = !wine_server_call_err( req );
67 SERVER_END_REQ;
68 return ret;
71 /******************************************************************
72 * start_console_renderer
74 * helper for AllocConsole
75 * starts the renderer process
77 static BOOL start_console_renderer(void)
79 char buffer[256];
80 int ret;
81 STARTUPINFOA si;
82 PROCESS_INFORMATION pi;
83 HANDLE hEvent = 0;
84 LPSTR p;
85 OBJECT_ATTRIBUTES attr;
87 attr.Length = sizeof(attr);
88 attr.RootDirectory = 0;
89 attr.Attributes = OBJ_INHERIT;
90 attr.ObjectName = NULL;
91 attr.SecurityDescriptor = NULL;
92 attr.SecurityQualityOfService = NULL;
94 NtCreateEvent(&hEvent, EVENT_ALL_ACCESS, &attr, TRUE, FALSE);
95 if (!hEvent) return FALSE;
97 memset(&si, 0, sizeof(si));
98 si.cb = sizeof(si);
100 /* FIXME: use dynamic allocation for most of the buffers below */
101 /* first try environment variable */
102 if ((p = getenv("WINECONSOLE")) != NULL)
104 ret = snprintf(buffer, sizeof(buffer), "%s --use-event=%d", p, hEvent);
105 if ((ret > -1) && (ret < sizeof(buffer)) &&
106 CreateProcessA(NULL, buffer, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &si, &pi))
107 goto succeed;
108 ERR("Couldn't launch Wine console from WINECONSOLE env var... trying default access\n");
111 /* then try the regular PATH */
112 sprintf(buffer, "wineconsole --use-event=%d", hEvent);
113 if (CreateProcessA(NULL, buffer, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &si, &pi))
114 goto succeed;
116 goto the_end;
118 succeed:
119 if (WaitForSingleObject(hEvent, INFINITE) != WAIT_OBJECT_0) goto the_end;
120 CloseHandle(hEvent);
122 TRACE("Started wineconsole pid=%08lx tid=%08lx\n", pi.dwProcessId, pi.dwThreadId);
124 return TRUE;
126 the_end:
127 ERR("Can't allocate console\n");
128 CloseHandle(hEvent);
129 return FALSE;
132 /***********************************************************************
133 * AllocConsole (KERNEL32.@)
135 * creates an xterm with a pty to our program
137 BOOL WINAPI AllocConsole(void)
139 HANDLE handle_in = INVALID_HANDLE_VALUE;
140 HANDLE handle_out = INVALID_HANDLE_VALUE;
141 HANDLE handle_err = INVALID_HANDLE_VALUE;
142 STARTUPINFOW si;
144 TRACE("()\n");
146 handle_in = CreateFileA( "CONIN$", GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE,
147 0, NULL, OPEN_EXISTING, 0, 0 );
149 if (handle_in != INVALID_HANDLE_VALUE)
151 /* we already have a console opened on this process, don't create a new one */
152 CloseHandle(handle_in);
153 return FALSE;
156 if (!start_console_renderer())
157 goto the_end;
159 handle_in = CreateFileA( "CONIN$", GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE,
160 0, NULL, OPEN_EXISTING, 0, 0 );
161 if (handle_in == INVALID_HANDLE_VALUE) goto the_end;
163 handle_out = CreateFileA( "CONOUT$", GENERIC_READ|GENERIC_WRITE,
164 0, NULL, OPEN_EXISTING, 0, 0 );
165 if (handle_out == INVALID_HANDLE_VALUE) goto the_end;
167 if (!DuplicateHandle(GetCurrentProcess(), handle_out, GetCurrentProcess(), &handle_err,
168 0, TRUE, DUPLICATE_SAME_ACCESS))
169 goto the_end;
171 /* NT resets the STD_*_HANDLEs on console alloc */
172 SetStdHandle(STD_INPUT_HANDLE, handle_in);
173 SetStdHandle(STD_OUTPUT_HANDLE, handle_out);
174 SetStdHandle(STD_ERROR_HANDLE, handle_err);
176 GetStartupInfoW(&si);
177 if (si.dwFlags & STARTF_USECOUNTCHARS)
179 COORD c;
180 c.X = si.dwXCountChars;
181 c.Y = si.dwYCountChars;
182 SetConsoleScreenBufferSize(handle_out, c);
184 if (si.dwFlags & STARTF_USEFILLATTRIBUTE)
185 SetConsoleTextAttribute(handle_out, si.dwFillAttribute);
186 if (si.lpTitle)
187 SetConsoleTitleW(si.lpTitle);
189 SetLastError(ERROR_SUCCESS);
191 return TRUE;
193 the_end:
194 ERR("Can't allocate console\n");
195 if (handle_in != INVALID_HANDLE_VALUE) CloseHandle(handle_in);
196 if (handle_out != INVALID_HANDLE_VALUE) CloseHandle(handle_out);
197 if (handle_err != INVALID_HANDLE_VALUE) CloseHandle(handle_err);
198 FreeConsole();
199 return FALSE;
203 /******************************************************************************
204 * read_console_input
206 * Helper function for ReadConsole, ReadConsoleInput and PeekConsoleInput
208 static BOOL read_console_input(HANDLE handle, LPINPUT_RECORD buffer, DWORD count,
209 LPDWORD pRead, BOOL flush)
211 BOOL ret;
212 unsigned read = 0;
214 SERVER_START_REQ( read_console_input )
216 req->handle = handle;
217 req->flush = flush;
218 wine_server_set_reply( req, buffer, count * sizeof(INPUT_RECORD) );
219 if ((ret = !wine_server_call_err( req ))) read = reply->read;
221 SERVER_END_REQ;
222 if (pRead) *pRead = read;
223 return ret;
227 /***********************************************************************
228 * ReadConsoleA (KERNEL32.@)
230 BOOL WINAPI ReadConsoleA(HANDLE hConsoleInput, LPVOID lpBuffer, DWORD nNumberOfCharsToRead,
231 LPDWORD lpNumberOfCharsRead, LPVOID lpReserved)
233 LPWSTR ptr = HeapAlloc(GetProcessHeap(), 0, nNumberOfCharsToRead * sizeof(WCHAR));
234 DWORD ncr = 0;
235 BOOL ret;
237 if ((ret = ReadConsoleW(hConsoleInput, ptr, nNumberOfCharsToRead, &ncr, 0)))
238 ncr = WideCharToMultiByte(CP_ACP, 0, ptr, ncr, lpBuffer, nNumberOfCharsToRead, NULL, NULL);
240 if (lpNumberOfCharsRead) *lpNumberOfCharsRead = ncr;
241 HeapFree(GetProcessHeap(), 0, ptr);
243 return ret;
246 /***********************************************************************
247 * ReadConsoleW (KERNEL32.@)
249 BOOL WINAPI ReadConsoleW(HANDLE hConsoleInput, LPVOID lpBuffer,
250 DWORD nNumberOfCharsToRead, LPDWORD lpNumberOfCharsRead, LPVOID lpReserved)
252 DWORD charsread;
253 LPWSTR xbuf = (LPWSTR)lpBuffer;
254 DWORD mode;
256 TRACE("(%d,%p,%ld,%p,%p)\n",
257 hConsoleInput, lpBuffer, nNumberOfCharsToRead, lpNumberOfCharsRead, lpReserved);
259 if (!GetConsoleMode(hConsoleInput, &mode))
260 return FALSE;
262 if (mode & ENABLE_LINE_INPUT)
264 if (!S_EditString || S_EditString[S_EditStrPos] == 0)
266 if (S_EditString) HeapFree(GetProcessHeap(), 0, S_EditString);
267 if (!(S_EditString = CONSOLE_Readline(hConsoleInput, mode & WINE_ENABLE_LINE_INPUT_EMACS)))
268 return FALSE;
269 S_EditStrPos = 0;
271 charsread = lstrlenW(&S_EditString[S_EditStrPos]);
272 if (charsread > nNumberOfCharsToRead) charsread = nNumberOfCharsToRead;
273 memcpy(xbuf, &S_EditString[S_EditStrPos], charsread * sizeof(WCHAR));
274 S_EditStrPos += charsread;
276 else
278 INPUT_RECORD ir;
279 DWORD count;
281 /* FIXME: should we read at least 1 char? The SDK does not say */
282 /* wait for at least one available input record (it doesn't mean we'll have
283 * chars stored in xbuf...
285 WaitForSingleObject(hConsoleInput, INFINITE);
286 for (charsread = 0; charsread < nNumberOfCharsToRead;)
288 if (!read_console_input(hConsoleInput, &ir, 1, &count, TRUE)) return FALSE;
289 if (count && ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown &&
290 ir.Event.KeyEvent.uChar.UnicodeChar &&
291 !(ir.Event.KeyEvent.dwControlKeyState & ENHANCED_KEY))
293 xbuf[charsread++] = ir.Event.KeyEvent.uChar.UnicodeChar;
298 if (lpNumberOfCharsRead) *lpNumberOfCharsRead = charsread;
300 return TRUE;
304 /***********************************************************************
305 * ReadConsoleInputW (KERNEL32.@)
307 BOOL WINAPI ReadConsoleInputW(HANDLE hConsoleInput, LPINPUT_RECORD lpBuffer,
308 DWORD nLength, LPDWORD lpNumberOfEventsRead)
310 DWORD count;
312 if (!nLength)
314 if (lpNumberOfEventsRead) *lpNumberOfEventsRead = 0;
315 return TRUE;
318 /* loop until we get at least one event */
319 for (;;)
321 WaitForSingleObject(hConsoleInput, INFINITE);
322 if (!read_console_input(hConsoleInput, lpBuffer, nLength, &count, TRUE))
323 return FALSE;
324 if (count)
326 if (lpNumberOfEventsRead) *lpNumberOfEventsRead = count;
327 return TRUE;
333 /******************************************************************************
334 * WriteConsoleOutputCharacterW [KERNEL32.@] Copies character to consecutive
335 * cells in the console screen buffer
337 * PARAMS
338 * hConsoleOutput [I] Handle to screen buffer
339 * str [I] Pointer to buffer with chars to write
340 * length [I] Number of cells to write to
341 * coord [I] Coords of first cell
342 * lpNumCharsWritten [O] Pointer to number of cells written
344 * RETURNS
345 * Success: TRUE
346 * Failure: FALSE
349 BOOL WINAPI WriteConsoleOutputCharacterW( HANDLE hConsoleOutput, LPCWSTR str, DWORD length,
350 COORD coord, LPDWORD lpNumCharsWritten )
352 BOOL ret;
354 TRACE("(%d,%s,%ld,%dx%d,%p)\n", hConsoleOutput,
355 debugstr_wn(str, length), length, coord.X, coord.Y, lpNumCharsWritten);
357 SERVER_START_REQ( write_console_output )
359 req->handle = hConsoleOutput;
360 req->x = coord.X;
361 req->y = coord.Y;
362 req->mode = CHAR_INFO_MODE_TEXT;
363 req->wrap = TRUE;
364 wine_server_add_data( req, str, length * sizeof(WCHAR) );
365 if ((ret = !wine_server_call_err( req )))
367 if (lpNumCharsWritten) *lpNumCharsWritten = reply->written;
370 SERVER_END_REQ;
371 return ret;
375 /******************************************************************************
376 * SetConsoleTitleW [KERNEL32.@] Sets title bar string for console
378 * PARAMS
379 * title [I] Address of new title
381 * RETURNS
382 * Success: TRUE
383 * Failure: FALSE
385 BOOL WINAPI SetConsoleTitleW(LPCWSTR title)
387 BOOL ret;
389 SERVER_START_REQ( set_console_input_info )
391 req->handle = 0;
392 req->mask = SET_CONSOLE_INPUT_INFO_TITLE;
393 wine_server_add_data( req, title, strlenW(title) * sizeof(WCHAR) );
394 ret = !wine_server_call_err( req );
396 SERVER_END_REQ;
397 return ret;
401 /***********************************************************************
402 * GetNumberOfConsoleMouseButtons (KERNEL32.@)
404 BOOL WINAPI GetNumberOfConsoleMouseButtons(LPDWORD nrofbuttons)
406 FIXME("(%p): stub\n", nrofbuttons);
407 *nrofbuttons = 2;
408 return TRUE;
411 /******************************************************************************
412 * SetConsoleInputExeNameW [KERNEL32.@]
414 * BUGS
415 * Unimplemented
417 BOOL WINAPI SetConsoleInputExeNameW(LPCWSTR name)
419 FIXME("(%s): stub!\n", debugstr_w(name));
421 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
422 return TRUE;
425 /******************************************************************************
426 * SetConsoleInputExeNameA [KERNEL32.@]
428 * BUGS
429 * Unimplemented
431 BOOL WINAPI SetConsoleInputExeNameA(LPCSTR name)
433 int len = MultiByteToWideChar(CP_ACP, 0, name, -1, NULL, 0);
434 LPWSTR xptr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
435 BOOL ret;
437 if (!xptr) return FALSE;
439 MultiByteToWideChar(CP_ACP, 0, name, -1, xptr, len);
440 ret = SetConsoleInputExeNameW(xptr);
441 HeapFree(GetProcessHeap(), 0, xptr);
443 return ret;
446 /******************************************************************
447 * CONSOLE_DefaultHandler
449 * Final control event handler
451 static BOOL WINAPI CONSOLE_DefaultHandler(DWORD dwCtrlType)
453 FIXME("Terminating process %lx on event %lx\n", GetCurrentProcessId(), dwCtrlType);
454 ExitProcess(0);
455 /* should never go here */
456 return TRUE;
459 /******************************************************************************
460 * SetConsoleCtrlHandler [KERNEL32.@] Adds function to calling process list
462 * PARAMS
463 * func [I] Address of handler function
464 * add [I] Handler to add or remove
466 * RETURNS
467 * Success: TRUE
468 * Failure: FALSE
470 * CHANGED
471 * James Sutherland (JamesSutherland@gmx.de)
472 * Added global variables console_ignore_ctrl_c and handlers[]
473 * Does not yet do any error checking, or set LastError if failed.
474 * This doesn't yet matter, since these handlers are not yet called...!
477 static unsigned int console_ignore_ctrl_c = 0; /* FIXME: this should be inherited somehow */
478 static PHANDLER_ROUTINE handlers[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,CONSOLE_DefaultHandler};
480 /*****************************************************************************/
482 BOOL WINAPI SetConsoleCtrlHandler(PHANDLER_ROUTINE func, BOOL add)
484 int alloc_loop = sizeof(handlers)/sizeof(handlers[0]) - 1;
486 FIXME("(%p,%i) - no error checking or testing yet\n", func, add);
488 if (!func)
490 console_ignore_ctrl_c = add;
491 return TRUE;
493 if (add)
495 for (; alloc_loop >= 0 && handlers[alloc_loop]; alloc_loop--);
496 if (alloc_loop <= 0)
498 FIXME("Out of space on CtrlHandler table\n");
499 return FALSE;
501 handlers[alloc_loop] = func;
503 else
505 for (; alloc_loop >= 0 && handlers[alloc_loop] != func; alloc_loop--);
506 if (alloc_loop <= 0)
508 WARN("Attempt to remove non-installed CtrlHandler %p\n", func);
509 return FALSE;
511 /* sanity check */
512 if (alloc_loop == sizeof(handlers)/sizeof(handlers[0]) - 1)
514 ERR("Who's trying to remove default handler???\n");
515 return FALSE;
517 if (alloc_loop)
518 memmove(&handlers[1], &handlers[0], alloc_loop * sizeof(handlers[0]));
519 handlers[0] = 0;
521 return TRUE;
524 static WINE_EXCEPTION_FILTER(CONSOLE_CtrlEventHandler)
526 TRACE("(%lx)\n", GetExceptionCode());
527 return EXCEPTION_EXECUTE_HANDLER;
530 /******************************************************************************
531 * GenerateConsoleCtrlEvent [KERNEL32.@] Simulate a CTRL-C or CTRL-BREAK
533 * PARAMS
534 * dwCtrlEvent [I] Type of event
535 * dwProcessGroupID [I] Process group ID to send event to
537 * RETURNS
538 * Success: True
539 * Failure: False (and *should* [but doesn't] set LastError)
541 BOOL WINAPI GenerateConsoleCtrlEvent(DWORD dwCtrlEvent,
542 DWORD dwProcessGroupID)
544 BOOL ret;
546 TRACE("(%ld, %ld)\n", dwCtrlEvent, dwProcessGroupID);
548 if (dwCtrlEvent != CTRL_C_EVENT && dwCtrlEvent != CTRL_BREAK_EVENT)
550 ERR("Invalid event %ld for PGID %ld\n", dwCtrlEvent, dwProcessGroupID);
551 return FALSE;
554 SERVER_START_REQ( send_console_signal )
556 req->signal = dwCtrlEvent;
557 req->group_id = (void*)dwProcessGroupID;
558 ret = !wine_server_call_err( req );
560 SERVER_END_REQ;
562 return ret;
566 /******************************************************************************
567 * CreateConsoleScreenBuffer [KERNEL32.@] Creates a console screen buffer
569 * PARAMS
570 * dwDesiredAccess [I] Access flag
571 * dwShareMode [I] Buffer share mode
572 * sa [I] Security attributes
573 * dwFlags [I] Type of buffer to create
574 * lpScreenBufferData [I] Reserved
576 * NOTES
577 * Should call SetLastError
579 * RETURNS
580 * Success: Handle to new console screen buffer
581 * Failure: INVALID_HANDLE_VALUE
583 HANDLE WINAPI CreateConsoleScreenBuffer(DWORD dwDesiredAccess, DWORD dwShareMode,
584 LPSECURITY_ATTRIBUTES sa, DWORD dwFlags,
585 LPVOID lpScreenBufferData)
587 HANDLE ret = INVALID_HANDLE_VALUE;
589 TRACE("(%ld,%ld,%p,%ld,%p)\n",
590 dwDesiredAccess, dwShareMode, sa, dwFlags, lpScreenBufferData);
592 if (dwFlags != CONSOLE_TEXTMODE_BUFFER || lpScreenBufferData != NULL)
594 SetLastError(ERROR_INVALID_PARAMETER);
595 return INVALID_HANDLE_VALUE;
598 SERVER_START_REQ(create_console_output)
600 req->handle_in = 0;
601 req->access = dwDesiredAccess;
602 req->share = dwShareMode;
603 req->inherit = (sa && sa->bInheritHandle);
604 if (!wine_server_call_err( req )) ret = reply->handle_out;
606 SERVER_END_REQ;
608 return ret;
612 /***********************************************************************
613 * GetConsoleScreenBufferInfo (KERNEL32.@)
615 BOOL WINAPI GetConsoleScreenBufferInfo(HANDLE hConsoleOutput, LPCONSOLE_SCREEN_BUFFER_INFO csbi)
617 BOOL ret;
619 SERVER_START_REQ(get_console_output_info)
621 req->handle = hConsoleOutput;
622 if ((ret = !wine_server_call_err( req )))
624 csbi->dwSize.X = reply->width;
625 csbi->dwSize.Y = reply->height;
626 csbi->dwCursorPosition.X = reply->cursor_x;
627 csbi->dwCursorPosition.Y = reply->cursor_y;
628 csbi->wAttributes = reply->attr;
629 csbi->srWindow.Left = reply->win_left;
630 csbi->srWindow.Right = reply->win_right;
631 csbi->srWindow.Top = reply->win_top;
632 csbi->srWindow.Bottom = reply->win_bottom;
633 csbi->dwMaximumWindowSize.X = reply->max_width;
634 csbi->dwMaximumWindowSize.Y = reply->max_height;
637 SERVER_END_REQ;
639 return ret;
643 /******************************************************************************
644 * SetConsoleActiveScreenBuffer [KERNEL32.@] Sets buffer to current console
646 * RETURNS
647 * Success: TRUE
648 * Failure: FALSE
650 BOOL WINAPI SetConsoleActiveScreenBuffer(HANDLE hConsoleOutput)
652 BOOL ret;
654 TRACE("(%x)\n", hConsoleOutput);
656 SERVER_START_REQ( set_console_input_info )
658 req->handle = 0;
659 req->mask = SET_CONSOLE_INPUT_INFO_ACTIVE_SB;
660 req->active_sb = hConsoleOutput;
661 ret = !wine_server_call_err( req );
663 SERVER_END_REQ;
664 return ret;
668 /***********************************************************************
669 * GetConsoleMode (KERNEL32.@)
671 BOOL WINAPI GetConsoleMode(HANDLE hcon, LPDWORD mode)
673 BOOL ret;
675 SERVER_START_REQ(get_console_mode)
677 req->handle = hcon;
678 ret = !wine_server_call_err( req );
679 if (ret && mode) *mode = reply->mode;
681 SERVER_END_REQ;
682 return ret;
686 /******************************************************************************
687 * SetConsoleMode [KERNEL32.@] Sets input mode of console's input buffer
689 * PARAMS
690 * hcon [I] Handle to console input or screen buffer
691 * mode [I] Input or output mode to set
693 * RETURNS
694 * Success: TRUE
695 * Failure: FALSE
697 BOOL WINAPI SetConsoleMode(HANDLE hcon, DWORD mode)
699 BOOL ret;
701 TRACE("(%x,%lx)\n", hcon, mode);
703 SERVER_START_REQ(set_console_mode)
705 req->handle = hcon;
706 req->mode = mode;
707 ret = !wine_server_call_err( req );
709 SERVER_END_REQ;
710 /* FIXME: when resetting a console input to editline mode, I think we should
711 * empty the S_EditString buffer
713 return ret;
717 /******************************************************************
718 * write_char
720 * WriteConsoleOutput helper: hides server call semantics
722 static int write_char(HANDLE hCon, LPCWSTR lpBuffer, int nc, COORD* pos)
724 int written = -1;
726 if (!nc) return 0;
728 SERVER_START_REQ( write_console_output )
730 req->handle = hCon;
731 req->x = pos->X;
732 req->y = pos->Y;
733 req->mode = CHAR_INFO_MODE_TEXTSTDATTR;
734 req->wrap = FALSE;
735 wine_server_add_data( req, lpBuffer, nc * sizeof(WCHAR) );
736 if (!wine_server_call_err( req )) written = reply->written;
738 SERVER_END_REQ;
740 if (written > 0) pos->X += written;
741 return written;
744 /******************************************************************
745 * next_line
747 * WriteConsoleOutput helper: handles passing to next line (+scrolling if necessary)
750 static int next_line(HANDLE hCon, CONSOLE_SCREEN_BUFFER_INFO* csbi)
752 SMALL_RECT src;
753 CHAR_INFO ci;
754 COORD dst;
756 csbi->dwCursorPosition.X = 0;
757 csbi->dwCursorPosition.Y++;
759 if (csbi->dwCursorPosition.Y < csbi->dwSize.Y) return 1;
761 src.Top = 1;
762 src.Bottom = csbi->dwSize.Y - 1;
763 src.Left = 0;
764 src.Right = csbi->dwSize.X - 1;
766 dst.X = 0;
767 dst.Y = 0;
769 ci.Attributes = csbi->wAttributes;
770 ci.Char.UnicodeChar = ' ';
772 csbi->dwCursorPosition.Y--;
773 if (!ScrollConsoleScreenBufferW(hCon, &src, NULL, dst, &ci))
774 return 0;
775 return 1;
778 /******************************************************************
779 * write_block
781 * WriteConsoleOutput helper: writes a block of non special characters
782 * Block can spread on several lines, and wrapping, if needed, is
783 * handled
786 static int write_block(HANDLE hCon, CONSOLE_SCREEN_BUFFER_INFO* csbi,
787 DWORD mode, LPWSTR ptr, int len)
789 int blk; /* number of chars to write on current line */
791 if (len <= 0) return 1;
793 if (mode & ENABLE_WRAP_AT_EOL_OUTPUT) /* writes remaining on next line */
795 int done;
797 for (done = 0; done < len; done += blk)
799 blk = min(len - done, csbi->dwSize.X - csbi->dwCursorPosition.X);
801 if (write_char(hCon, ptr + done, blk, &csbi->dwCursorPosition) != blk)
802 return 0;
803 if (csbi->dwCursorPosition.X == csbi->dwSize.X && !next_line(hCon, csbi))
804 return 0;
807 else
809 blk = min(len, csbi->dwSize.X - csbi->dwCursorPosition.X);
811 if (write_char(hCon, ptr, blk, &csbi->dwCursorPosition) != blk)
812 return 0;
813 if (blk < len)
815 csbi->dwCursorPosition.X = csbi->dwSize.X - 1;
816 /* all remaining chars should be written on last column,
817 * so only overwrite the last column with last char in block
819 if (write_char(hCon, ptr + len - 1, 1, &csbi->dwCursorPosition) != 1)
820 return 0;
821 csbi->dwCursorPosition.X = csbi->dwSize.X - 1;
825 return 1;
828 /***********************************************************************
829 * WriteConsoleW (KERNEL32.@)
831 BOOL WINAPI WriteConsoleW(HANDLE hConsoleOutput, LPCVOID lpBuffer, DWORD nNumberOfCharsToWrite,
832 LPDWORD lpNumberOfCharsWritten, LPVOID lpReserved)
834 DWORD mode;
835 DWORD nw = 0;
836 WCHAR* psz = (WCHAR*)lpBuffer;
837 CONSOLE_SCREEN_BUFFER_INFO csbi;
838 int k, first = 0;
840 TRACE("%d %s %ld %p %p\n",
841 hConsoleOutput, debugstr_wn(lpBuffer, nNumberOfCharsToWrite),
842 nNumberOfCharsToWrite, lpNumberOfCharsWritten, lpReserved);
844 if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = 0;
846 if (!GetConsoleMode(hConsoleOutput, &mode) ||
847 !GetConsoleScreenBufferInfo(hConsoleOutput, &csbi))
848 return FALSE;
850 if (mode & ENABLE_PROCESSED_OUTPUT)
852 int i;
854 for (i = 0; i < nNumberOfCharsToWrite; i++)
856 switch (psz[i])
858 case '\b': case '\t': case '\n': case '\a': case '\r':
859 /* don't handle here the i-th char... done below */
860 if ((k = i - first) > 0)
862 if (!write_block(hConsoleOutput, &csbi, mode, &psz[first], k))
863 goto the_end;
864 nw += k;
866 first = i + 1;
867 nw++;
869 switch (psz[i])
871 case '\b':
872 if (csbi.dwCursorPosition.X > 0) csbi.dwCursorPosition.X--;
873 break;
874 case '\t':
876 WCHAR tmp[8] = {' ',' ',' ',' ',' ',' ',' ',' '};
878 if (!write_block(hConsoleOutput, &csbi, mode, tmp,
879 ((csbi.dwCursorPosition.X + 8) & ~7) - csbi.dwCursorPosition.X))
880 goto the_end;
882 break;
883 case '\n':
884 next_line(hConsoleOutput, &csbi);
885 break;
886 case '\a':
887 Beep(400, 300);
888 break;
889 case '\r':
890 csbi.dwCursorPosition.X = 0;
891 break;
892 default:
893 break;
898 /* write the remaining block (if any) if processed output is enabled, or the
899 * entire buffer otherwise
901 if ((k = nNumberOfCharsToWrite - first) > 0)
903 if (!write_block(hConsoleOutput, &csbi, mode, &psz[first], k))
904 goto the_end;
905 nw += k;
908 the_end:
909 SetConsoleCursorPosition(hConsoleOutput, csbi.dwCursorPosition);
910 if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = nw;
911 return nw != 0;
915 /***********************************************************************
916 * WriteConsoleA (KERNEL32.@)
918 BOOL WINAPI WriteConsoleA(HANDLE hConsoleOutput, LPCVOID lpBuffer, DWORD nNumberOfCharsToWrite,
919 LPDWORD lpNumberOfCharsWritten, LPVOID lpReserved)
921 BOOL ret;
922 LPWSTR xstring;
923 DWORD n;
925 n = MultiByteToWideChar(CP_ACP, 0, lpBuffer, nNumberOfCharsToWrite, NULL, 0);
927 if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = 0;
928 xstring = HeapAlloc(GetProcessHeap(), 0, n * sizeof(WCHAR));
929 if (!xstring) return 0;
931 MultiByteToWideChar(CP_ACP, 0, lpBuffer, nNumberOfCharsToWrite, xstring, n);
933 ret = WriteConsoleW(hConsoleOutput, xstring, n, lpNumberOfCharsWritten, 0);
935 HeapFree(GetProcessHeap(), 0, xstring);
937 return ret;
940 /******************************************************************************
941 * SetConsoleCursorPosition [KERNEL32.@]
942 * Sets the cursor position in console
944 * PARAMS
945 * hConsoleOutput [I] Handle of console screen buffer
946 * dwCursorPosition [I] New cursor position coordinates
948 * RETURNS STD
950 BOOL WINAPI SetConsoleCursorPosition(HANDLE hcon, COORD pos)
952 BOOL ret;
953 CONSOLE_SCREEN_BUFFER_INFO csbi;
954 int do_move = 0;
955 int w, h;
957 TRACE("%x %d %d\n", hcon, pos.X, pos.Y);
959 SERVER_START_REQ(set_console_output_info)
961 req->handle = hcon;
962 req->cursor_x = pos.X;
963 req->cursor_y = pos.Y;
964 req->mask = SET_CONSOLE_OUTPUT_INFO_CURSOR_POS;
965 ret = !wine_server_call_err( req );
967 SERVER_END_REQ;
969 if (!ret || !GetConsoleScreenBufferInfo(hcon, &csbi))
970 return FALSE;
972 /* if cursor is no longer visible, scroll the visible window... */
973 w = csbi.srWindow.Right - csbi.srWindow.Left + 1;
974 h = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
975 if (pos.X < csbi.srWindow.Left)
977 csbi.srWindow.Left = min(pos.X, csbi.dwSize.X - w);
978 do_move++;
980 else if (pos.X > csbi.srWindow.Right)
982 csbi.srWindow.Left = max(pos.X, w) - w + 1;
983 do_move++;
985 csbi.srWindow.Right = csbi.srWindow.Left + w - 1;
987 if (pos.Y < csbi.srWindow.Top)
989 csbi.srWindow.Top = min(pos.Y, csbi.dwSize.Y - h);
990 do_move++;
992 else if (pos.Y > csbi.srWindow.Bottom)
994 csbi.srWindow.Top = max(pos.Y, h) - h + 1;
995 do_move++;
997 csbi.srWindow.Bottom = csbi.srWindow.Top + h - 1;
999 ret = (do_move) ? SetConsoleWindowInfo(hcon, TRUE, &csbi.srWindow) : TRUE;
1001 return ret;
1004 /******************************************************************************
1005 * GetConsoleCursorInfo [KERNEL32.@] Gets size and visibility of console
1007 * PARAMS
1008 * hcon [I] Handle to console screen buffer
1009 * cinfo [O] Address of cursor information
1011 * RETURNS
1012 * Success: TRUE
1013 * Failure: FALSE
1015 BOOL WINAPI GetConsoleCursorInfo(HANDLE hcon, LPCONSOLE_CURSOR_INFO cinfo)
1017 BOOL ret;
1019 SERVER_START_REQ(get_console_output_info)
1021 req->handle = hcon;
1022 ret = !wine_server_call_err( req );
1023 if (ret && cinfo)
1025 cinfo->dwSize = reply->cursor_size;
1026 cinfo->bVisible = reply->cursor_visible;
1029 SERVER_END_REQ;
1030 return ret;
1034 /******************************************************************************
1035 * SetConsoleCursorInfo [KERNEL32.@] Sets size and visibility of cursor
1037 * PARAMS
1038 * hcon [I] Handle to console screen buffer
1039 * cinfo [I] Address of cursor information
1040 * RETURNS
1041 * Success: TRUE
1042 * Failure: FALSE
1044 BOOL WINAPI SetConsoleCursorInfo(HANDLE hCon, LPCONSOLE_CURSOR_INFO cinfo)
1046 BOOL ret;
1048 SERVER_START_REQ(set_console_output_info)
1050 req->handle = hCon;
1051 req->cursor_size = cinfo->dwSize;
1052 req->cursor_visible = cinfo->bVisible;
1053 req->mask = SET_CONSOLE_OUTPUT_INFO_CURSOR_GEOM;
1054 ret = !wine_server_call_err( req );
1056 SERVER_END_REQ;
1057 return ret;
1061 /******************************************************************************
1062 * SetConsoleWindowInfo [KERNEL32.@] Sets size and position of console
1064 * PARAMS
1065 * hcon [I] Handle to console screen buffer
1066 * bAbsolute [I] Coordinate type flag
1067 * window [I] Address of new window rectangle
1068 * RETURNS
1069 * Success: TRUE
1070 * Failure: FALSE
1072 BOOL WINAPI SetConsoleWindowInfo(HANDLE hCon, BOOL bAbsolute, LPSMALL_RECT window)
1074 SMALL_RECT p = *window;
1075 BOOL ret;
1077 if (!bAbsolute)
1079 CONSOLE_SCREEN_BUFFER_INFO csbi;
1080 if (!GetConsoleScreenBufferInfo(hCon, &csbi))
1081 return FALSE;
1082 p.Left += csbi.srWindow.Left;
1083 p.Top += csbi.srWindow.Top;
1084 p.Right += csbi.srWindow.Left;
1085 p.Bottom += csbi.srWindow.Top;
1087 SERVER_START_REQ(set_console_output_info)
1089 req->handle = hCon;
1090 req->win_left = p.Left;
1091 req->win_top = p.Top;
1092 req->win_right = p.Right;
1093 req->win_bottom = p.Bottom;
1094 req->mask = SET_CONSOLE_OUTPUT_INFO_DISPLAY_WINDOW;
1095 ret = !wine_server_call_err( req );
1097 SERVER_END_REQ;
1099 return ret;
1103 /******************************************************************************
1104 * SetConsoleTextAttribute [KERNEL32.@] Sets colors for text
1106 * Sets the foreground and background color attributes of characters
1107 * written to the screen buffer.
1109 * RETURNS
1110 * Success: TRUE
1111 * Failure: FALSE
1113 BOOL WINAPI SetConsoleTextAttribute(HANDLE hConsoleOutput, WORD wAttr)
1115 BOOL ret;
1117 SERVER_START_REQ(set_console_output_info)
1119 req->handle = hConsoleOutput;
1120 req->attr = wAttr;
1121 req->mask = SET_CONSOLE_OUTPUT_INFO_ATTR;
1122 ret = !wine_server_call_err( req );
1124 SERVER_END_REQ;
1125 return ret;
1129 /******************************************************************************
1130 * SetConsoleScreenBufferSize [KERNEL32.@] Changes size of console
1132 * PARAMS
1133 * hConsoleOutput [I] Handle to console screen buffer
1134 * dwSize [I] New size in character rows and cols
1136 * RETURNS
1137 * Success: TRUE
1138 * Failure: FALSE
1140 BOOL WINAPI SetConsoleScreenBufferSize(HANDLE hConsoleOutput, COORD dwSize)
1142 BOOL ret;
1144 SERVER_START_REQ(set_console_output_info)
1146 req->handle = hConsoleOutput;
1147 req->width = dwSize.X;
1148 req->height = dwSize.Y;
1149 req->mask = SET_CONSOLE_OUTPUT_INFO_SIZE;
1150 ret = !wine_server_call_err( req );
1152 SERVER_END_REQ;
1153 return ret;
1157 /******************************************************************************
1158 * ScrollConsoleScreenBufferA [KERNEL32.@]
1161 BOOL WINAPI ScrollConsoleScreenBufferA(HANDLE hConsoleOutput, LPSMALL_RECT lpScrollRect,
1162 LPSMALL_RECT lpClipRect, COORD dwDestOrigin,
1163 LPCHAR_INFO lpFill)
1165 CHAR_INFO ciw;
1167 ciw.Attributes = lpFill->Attributes;
1168 MultiByteToWideChar(CP_ACP, 0, &lpFill->Char.AsciiChar, 1, &ciw.Char.UnicodeChar, 1);
1170 return ScrollConsoleScreenBufferW(hConsoleOutput, lpScrollRect, lpClipRect,
1171 dwDestOrigin, &ciw);
1174 /******************************************************************
1175 * fill_line_uniform
1177 * Helper function for ScrollConsoleScreenBufferW
1178 * Fills a part of a line with a constant character info
1180 static void fill_line_uniform(HANDLE hConsoleOutput, int i, int j, int len, LPCHAR_INFO lpFill)
1182 SERVER_START_REQ( fill_console_output )
1184 req->handle = hConsoleOutput;
1185 req->mode = CHAR_INFO_MODE_TEXTATTR;
1186 req->x = i;
1187 req->y = j;
1188 req->count = len;
1189 req->wrap = FALSE;
1190 req->data.ch = lpFill->Char.UnicodeChar;
1191 req->data.attr = lpFill->Attributes;
1192 wine_server_call_err( req );
1194 SERVER_END_REQ;
1197 /******************************************************************************
1198 * ScrollConsoleScreenBufferW [KERNEL32.@]
1202 BOOL WINAPI ScrollConsoleScreenBufferW(HANDLE hConsoleOutput, LPSMALL_RECT lpScrollRect,
1203 LPSMALL_RECT lpClipRect, COORD dwDestOrigin,
1204 LPCHAR_INFO lpFill)
1206 SMALL_RECT dst;
1207 DWORD ret;
1208 int i, j;
1209 int start = -1;
1210 SMALL_RECT clip;
1211 CONSOLE_SCREEN_BUFFER_INFO csbi;
1212 BOOL inside;
1214 if (lpClipRect)
1215 TRACE("(%d,(%d,%d-%d,%d),(%d,%d-%d,%d),%d-%d,%p)\n", hConsoleOutput,
1216 lpScrollRect->Left, lpScrollRect->Top,
1217 lpScrollRect->Right, lpScrollRect->Bottom,
1218 lpClipRect->Left, lpClipRect->Top,
1219 lpClipRect->Right, lpClipRect->Bottom,
1220 dwDestOrigin.X, dwDestOrigin.Y, lpFill);
1221 else
1222 TRACE("(%d,(%d,%d-%d,%d),(nil),%d-%d,%p)\n", hConsoleOutput,
1223 lpScrollRect->Left, lpScrollRect->Top,
1224 lpScrollRect->Right, lpScrollRect->Bottom,
1225 dwDestOrigin.X, dwDestOrigin.Y, lpFill);
1227 if (!GetConsoleScreenBufferInfo(hConsoleOutput, &csbi))
1228 return FALSE;
1230 /* step 1: get dst rect */
1231 dst.Left = dwDestOrigin.X;
1232 dst.Top = dwDestOrigin.Y;
1233 dst.Right = dst.Left + (lpScrollRect->Right - lpScrollRect->Left);
1234 dst.Bottom = dst.Top + (lpScrollRect->Bottom - lpScrollRect->Top);
1236 /* step 2a: compute the final clip rect (optional passed clip and screen buffer limits */
1237 if (lpClipRect)
1239 clip.Left = max(0, lpClipRect->Left);
1240 clip.Right = min(csbi.dwSize.X - 1, lpClipRect->Right);
1241 clip.Top = max(0, lpClipRect->Top);
1242 clip.Bottom = min(csbi.dwSize.Y - 1, lpClipRect->Bottom);
1244 else
1246 clip.Left = 0;
1247 clip.Right = csbi.dwSize.X - 1;
1248 clip.Top = 0;
1249 clip.Bottom = csbi.dwSize.Y - 1;
1251 if (clip.Left > clip.Right || clip.Top > clip.Bottom) return FALSE;
1253 /* step 2b: clip dst rect */
1254 if (dst.Left < clip.Left ) dst.Left = clip.Left;
1255 if (dst.Top < clip.Top ) dst.Top = clip.Top;
1256 if (dst.Right > clip.Right ) dst.Right = clip.Right;
1257 if (dst.Bottom > clip.Bottom) dst.Bottom = clip.Bottom;
1259 /* step 3: transfer the bits */
1260 SERVER_START_REQ(move_console_output)
1262 req->handle = hConsoleOutput;
1263 req->x_src = lpScrollRect->Left;
1264 req->y_src = lpScrollRect->Top;
1265 req->x_dst = dst.Left;
1266 req->y_dst = dst.Top;
1267 req->w = dst.Right - dst.Left + 1;
1268 req->h = dst.Bottom - dst.Top + 1;
1269 ret = !wine_server_call_err( req );
1271 SERVER_END_REQ;
1273 if (!ret) return FALSE;
1275 /* step 4: clean out the exposed part */
1277 /* have to write celll [i,j] if it is not in dst rect (because it has already
1278 * been written to by the scroll) and is in clip (we shall not write
1279 * outside of clip)
1281 for (j = max(lpScrollRect->Top, clip.Top); j <= min(lpScrollRect->Bottom, clip.Bottom); j++)
1283 inside = dst.Top <= j && j <= dst.Bottom;
1284 start = -1;
1285 for (i = max(lpScrollRect->Left, clip.Left); i <= min(lpScrollRect->Right, clip.Right); i++)
1287 if (inside && dst.Left <= i && i <= dst.Right)
1289 if (start != -1)
1291 fill_line_uniform(hConsoleOutput, start, j, i - start, lpFill);
1292 start = -1;
1295 else
1297 if (start == -1) start = i;
1300 if (start != -1)
1301 fill_line_uniform(hConsoleOutput, start, j, i - start, lpFill);
1304 return TRUE;
1308 /* ====================================================================
1310 * Console manipulation functions
1312 * ====================================================================*/
1314 /* some missing functions...
1315 * FIXME: those are likely to be defined as undocumented function in kernel32 (or part of them)
1316 * should get the right API and implement them
1317 * GetConsoleCommandHistory[AW] (dword dword dword)
1318 * GetConsoleCommandHistoryLength[AW]
1319 * SetConsoleCommandHistoryMode
1320 * SetConsoleNumberOfCommands[AW]
1322 int CONSOLE_GetHistory(int idx, WCHAR* buf, int buf_len)
1324 int len = 0;
1326 SERVER_START_REQ( get_console_input_history )
1328 req->handle = 0;
1329 req->index = idx;
1330 if (buf && buf_len > 1)
1332 wine_server_set_reply( req, buf, (buf_len - 1) * sizeof(WCHAR) );
1334 if (!wine_server_call_err( req ))
1336 if (buf) buf[wine_server_reply_size(reply) / sizeof(WCHAR)] = 0;
1337 len = reply->total / sizeof(WCHAR) + 1;
1340 SERVER_END_REQ;
1341 return len;
1344 /******************************************************************
1345 * CONSOLE_AppendHistory
1349 BOOL CONSOLE_AppendHistory(const WCHAR* ptr)
1351 size_t len = strlenW(ptr);
1352 BOOL ret;
1354 while (len && (ptr[len - 1] == '\n' || ptr[len - 1] == '\r')) len--;
1356 SERVER_START_REQ( append_console_input_history )
1358 req->handle = 0;
1359 wine_server_add_data( req, ptr, len * sizeof(WCHAR) );
1360 ret = !wine_server_call_err( req );
1362 SERVER_END_REQ;
1363 return ret;
1366 /******************************************************************
1367 * CONSOLE_GetNumHistoryEntries
1371 unsigned CONSOLE_GetNumHistoryEntries(void)
1373 unsigned ret = -1;
1374 SERVER_START_REQ(get_console_input_info)
1376 req->handle = 0;
1377 if (!wine_server_call_err( req )) ret = reply->history_index;
1379 SERVER_END_REQ;
1380 return ret;
1383 /******************************************************************
1384 * CONSOLE_HandleCtrlC
1386 * Check whether the shall manipulate CtrlC events
1388 int CONSOLE_HandleCtrlC(void)
1390 int i;
1392 /* FIXME: better test whether a console is attached to this process ??? */
1393 extern unsigned CONSOLE_GetNumHistoryEntries(void);
1394 if (CONSOLE_GetNumHistoryEntries() == (unsigned)-1) return 0;
1396 /* try to pass the exception to the debugger
1397 * if it continues, there's nothing more to do
1398 * otherwise, we need to send the ctrl-event to the handlers
1400 __TRY
1402 RaiseException( DBG_CONTROL_C, 0, 0, NULL );
1404 __EXCEPT(CONSOLE_CtrlEventHandler)
1406 /* the debugger didn't continue... so, pass to ctrl handlers */
1407 /* FIXME: since this routine is called while in a signal handler,
1408 * there are some serious synchronisation issues with
1409 * SetConsoleCtrlHandler (trouble ahead)
1411 for (i = 0; i < sizeof(handlers)/sizeof(handlers[0]); i++)
1413 if (handlers[i] && (handlers[i])(CTRL_C_EVENT)) break;
1416 __ENDTRY;
1417 return 1;