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.
31 #include "wine/port.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);
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)
63 SERVER_START_REQ(free_console)
65 ret = !wine_server_call_err( req );
71 /******************************************************************
72 * start_console_renderer
74 * helper for AllocConsole
75 * starts the renderer process
77 static BOOL start_console_renderer(void)
82 PROCESS_INFORMATION pi;
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));
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))
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))
119 if (WaitForSingleObject(hEvent, INFINITE) != WAIT_OBJECT_0) goto the_end;
122 TRACE("Started wineconsole pid=%08lx tid=%08lx\n", pi.dwProcessId, pi.dwThreadId);
127 ERR("Can't allocate console\n");
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;
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);
156 if (!start_console_renderer())
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))
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)
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);
187 SetConsoleTitleW(si.lpTitle);
189 SetLastError(ERROR_SUCCESS);
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);
203 /******************************************************************************
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)
214 SERVER_START_REQ( read_console_input )
216 req->handle = handle;
218 wine_server_set_reply( req, buffer, count * sizeof(INPUT_RECORD) );
219 if ((ret = !wine_server_call_err( req ))) read = reply->read;
222 if (pRead) *pRead = read;
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));
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);
246 /***********************************************************************
247 * ReadConsoleW (KERNEL32.@)
249 BOOL WINAPI ReadConsoleW(HANDLE hConsoleInput, LPVOID lpBuffer,
250 DWORD nNumberOfCharsToRead, LPDWORD lpNumberOfCharsRead, LPVOID lpReserved)
253 LPWSTR xbuf = (LPWSTR)lpBuffer;
256 TRACE("(%d,%p,%ld,%p,%p)\n",
257 hConsoleInput, lpBuffer, nNumberOfCharsToRead, lpNumberOfCharsRead, lpReserved);
259 if (!GetConsoleMode(hConsoleInput, &mode))
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)))
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;
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;
304 /***********************************************************************
305 * ReadConsoleInputW (KERNEL32.@)
307 BOOL WINAPI ReadConsoleInputW(HANDLE hConsoleInput, LPINPUT_RECORD lpBuffer,
308 DWORD nLength, LPDWORD lpNumberOfEventsRead)
314 if (lpNumberOfEventsRead) *lpNumberOfEventsRead = 0;
318 /* loop until we get at least one event */
321 WaitForSingleObject(hConsoleInput, INFINITE);
322 if (!read_console_input(hConsoleInput, lpBuffer, nLength, &count, TRUE))
326 if (lpNumberOfEventsRead) *lpNumberOfEventsRead = count;
333 /******************************************************************************
334 * WriteConsoleOutputCharacterW [KERNEL32.@] Copies character to consecutive
335 * cells in the console screen buffer
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
349 BOOL WINAPI WriteConsoleOutputCharacterW( HANDLE hConsoleOutput, LPCWSTR str, DWORD length,
350 COORD coord, LPDWORD lpNumCharsWritten )
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;
362 req->mode = CHAR_INFO_MODE_TEXT;
364 wine_server_add_data( req, str, length * sizeof(WCHAR) );
365 if ((ret = !wine_server_call_err( req )))
367 if (lpNumCharsWritten) *lpNumCharsWritten = reply->written;
375 /******************************************************************************
376 * SetConsoleTitleW [KERNEL32.@] Sets title bar string for console
379 * title [I] Address of new title
385 BOOL WINAPI SetConsoleTitleW(LPCWSTR title)
389 SERVER_START_REQ( set_console_input_info )
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 );
401 /***********************************************************************
402 * GetNumberOfConsoleMouseButtons (KERNEL32.@)
404 BOOL WINAPI GetNumberOfConsoleMouseButtons(LPDWORD nrofbuttons)
406 FIXME("(%p): stub\n", nrofbuttons);
411 /******************************************************************************
412 * SetConsoleInputExeNameW [KERNEL32.@]
417 BOOL WINAPI SetConsoleInputExeNameW(LPCWSTR name)
419 FIXME("(%s): stub!\n", debugstr_w(name));
421 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
425 /******************************************************************************
426 * SetConsoleInputExeNameA [KERNEL32.@]
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));
437 if (!xptr) return FALSE;
439 MultiByteToWideChar(CP_ACP, 0, name, -1, xptr, len);
440 ret = SetConsoleInputExeNameW(xptr);
441 HeapFree(GetProcessHeap(), 0, xptr);
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);
455 /* should never go here */
459 /******************************************************************************
460 * SetConsoleCtrlHandler [KERNEL32.@] Adds function to calling process list
463 * func [I] Address of handler function
464 * add [I] Handler to add or remove
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);
490 console_ignore_ctrl_c = add;
495 for (; alloc_loop >= 0 && handlers[alloc_loop]; alloc_loop--);
498 FIXME("Out of space on CtrlHandler table\n");
501 handlers[alloc_loop] = func;
505 for (; alloc_loop >= 0 && handlers[alloc_loop] != func; alloc_loop--);
508 WARN("Attempt to remove non-installed CtrlHandler %p\n", func);
512 if (alloc_loop == sizeof(handlers)/sizeof(handlers[0]) - 1)
514 ERR("Who's trying to remove default handler???\n");
518 memmove(&handlers[1], &handlers[0], alloc_loop * sizeof(handlers[0]));
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
534 * dwCtrlEvent [I] Type of event
535 * dwProcessGroupID [I] Process group ID to send event to
539 * Failure: False (and *should* [but doesn't] set LastError)
541 BOOL WINAPI GenerateConsoleCtrlEvent(DWORD dwCtrlEvent,
542 DWORD dwProcessGroupID)
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);
554 SERVER_START_REQ( send_console_signal )
556 req->signal = dwCtrlEvent;
557 req->group_id = (void*)dwProcessGroupID;
558 ret = !wine_server_call_err( req );
566 /******************************************************************************
567 * CreateConsoleScreenBuffer [KERNEL32.@] Creates a console screen buffer
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
577 * Should call SetLastError
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)
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;
612 /***********************************************************************
613 * GetConsoleScreenBufferInfo (KERNEL32.@)
615 BOOL WINAPI GetConsoleScreenBufferInfo(HANDLE hConsoleOutput, LPCONSOLE_SCREEN_BUFFER_INFO csbi)
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;
643 /******************************************************************************
644 * SetConsoleActiveScreenBuffer [KERNEL32.@] Sets buffer to current console
650 BOOL WINAPI SetConsoleActiveScreenBuffer(HANDLE hConsoleOutput)
654 TRACE("(%x)\n", hConsoleOutput);
656 SERVER_START_REQ( set_console_input_info )
659 req->mask = SET_CONSOLE_INPUT_INFO_ACTIVE_SB;
660 req->active_sb = hConsoleOutput;
661 ret = !wine_server_call_err( req );
668 /***********************************************************************
669 * GetConsoleMode (KERNEL32.@)
671 BOOL WINAPI GetConsoleMode(HANDLE hcon, LPDWORD mode)
675 SERVER_START_REQ(get_console_mode)
678 ret = !wine_server_call_err( req );
679 if (ret && mode) *mode = reply->mode;
686 /******************************************************************************
687 * SetConsoleMode [KERNEL32.@] Sets input mode of console's input buffer
690 * hcon [I] Handle to console input or screen buffer
691 * mode [I] Input or output mode to set
697 BOOL WINAPI SetConsoleMode(HANDLE hcon, DWORD mode)
701 TRACE("(%x,%lx)\n", hcon, mode);
703 SERVER_START_REQ(set_console_mode)
707 ret = !wine_server_call_err( req );
710 /* FIXME: when resetting a console input to editline mode, I think we should
711 * empty the S_EditString buffer
717 /******************************************************************
720 * WriteConsoleOutput helper: hides server call semantics
722 static int write_char(HANDLE hCon, LPCWSTR lpBuffer, int nc, COORD* pos)
728 SERVER_START_REQ( write_console_output )
733 req->mode = CHAR_INFO_MODE_TEXTSTDATTR;
735 wine_server_add_data( req, lpBuffer, nc * sizeof(WCHAR) );
736 if (!wine_server_call_err( req )) written = reply->written;
740 if (written > 0) pos->X += written;
744 /******************************************************************
747 * WriteConsoleOutput helper: handles passing to next line (+scrolling if necessary)
750 static int next_line(HANDLE hCon, CONSOLE_SCREEN_BUFFER_INFO* csbi)
756 csbi->dwCursorPosition.X = 0;
757 csbi->dwCursorPosition.Y++;
759 if (csbi->dwCursorPosition.Y < csbi->dwSize.Y) return 1;
762 src.Bottom = csbi->dwSize.Y - 1;
764 src.Right = csbi->dwSize.X - 1;
769 ci.Attributes = csbi->wAttributes;
770 ci.Char.UnicodeChar = ' ';
772 csbi->dwCursorPosition.Y--;
773 if (!ScrollConsoleScreenBufferW(hCon, &src, NULL, dst, &ci))
778 /******************************************************************
781 * WriteConsoleOutput helper: writes a block of non special characters
782 * Block can spread on several lines, and wrapping, if needed, is
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 */
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)
803 if (csbi->dwCursorPosition.X == csbi->dwSize.X && !next_line(hCon, csbi))
809 blk = min(len, csbi->dwSize.X - csbi->dwCursorPosition.X);
811 if (write_char(hCon, ptr, blk, &csbi->dwCursorPosition) != blk)
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)
821 csbi->dwCursorPosition.X = csbi->dwSize.X - 1;
828 /***********************************************************************
829 * WriteConsoleW (KERNEL32.@)
831 BOOL WINAPI WriteConsoleW(HANDLE hConsoleOutput, LPCVOID lpBuffer, DWORD nNumberOfCharsToWrite,
832 LPDWORD lpNumberOfCharsWritten, LPVOID lpReserved)
836 WCHAR* psz = (WCHAR*)lpBuffer;
837 CONSOLE_SCREEN_BUFFER_INFO csbi;
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))
850 if (mode & ENABLE_PROCESSED_OUTPUT)
854 for (i = 0; i < nNumberOfCharsToWrite; 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))
872 if (csbi.dwCursorPosition.X > 0) csbi.dwCursorPosition.X--;
876 WCHAR tmp[8] = {' ',' ',' ',' ',' ',' ',' ',' '};
878 if (!write_block(hConsoleOutput, &csbi, mode, tmp,
879 ((csbi.dwCursorPosition.X + 8) & ~7) - csbi.dwCursorPosition.X))
884 next_line(hConsoleOutput, &csbi);
890 csbi.dwCursorPosition.X = 0;
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))
909 SetConsoleCursorPosition(hConsoleOutput, csbi.dwCursorPosition);
910 if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = nw;
915 /***********************************************************************
916 * WriteConsoleA (KERNEL32.@)
918 BOOL WINAPI WriteConsoleA(HANDLE hConsoleOutput, LPCVOID lpBuffer, DWORD nNumberOfCharsToWrite,
919 LPDWORD lpNumberOfCharsWritten, LPVOID lpReserved)
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);
940 /******************************************************************************
941 * SetConsoleCursorPosition [KERNEL32.@]
942 * Sets the cursor position in console
945 * hConsoleOutput [I] Handle of console screen buffer
946 * dwCursorPosition [I] New cursor position coordinates
950 BOOL WINAPI SetConsoleCursorPosition(HANDLE hcon, COORD pos)
953 CONSOLE_SCREEN_BUFFER_INFO csbi;
957 TRACE("%x %d %d\n", hcon, pos.X, pos.Y);
959 SERVER_START_REQ(set_console_output_info)
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 );
969 if (!ret || !GetConsoleScreenBufferInfo(hcon, &csbi))
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);
980 else if (pos.X > csbi.srWindow.Right)
982 csbi.srWindow.Left = max(pos.X, w) - w + 1;
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);
992 else if (pos.Y > csbi.srWindow.Bottom)
994 csbi.srWindow.Top = max(pos.Y, h) - h + 1;
997 csbi.srWindow.Bottom = csbi.srWindow.Top + h - 1;
999 ret = (do_move) ? SetConsoleWindowInfo(hcon, TRUE, &csbi.srWindow) : TRUE;
1004 /******************************************************************************
1005 * GetConsoleCursorInfo [KERNEL32.@] Gets size and visibility of console
1008 * hcon [I] Handle to console screen buffer
1009 * cinfo [O] Address of cursor information
1015 BOOL WINAPI GetConsoleCursorInfo(HANDLE hcon, LPCONSOLE_CURSOR_INFO cinfo)
1019 SERVER_START_REQ(get_console_output_info)
1022 ret = !wine_server_call_err( req );
1025 cinfo->dwSize = reply->cursor_size;
1026 cinfo->bVisible = reply->cursor_visible;
1034 /******************************************************************************
1035 * SetConsoleCursorInfo [KERNEL32.@] Sets size and visibility of cursor
1038 * hcon [I] Handle to console screen buffer
1039 * cinfo [I] Address of cursor information
1044 BOOL WINAPI SetConsoleCursorInfo(HANDLE hCon, LPCONSOLE_CURSOR_INFO cinfo)
1048 SERVER_START_REQ(set_console_output_info)
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 );
1061 /******************************************************************************
1062 * SetConsoleWindowInfo [KERNEL32.@] Sets size and position of console
1065 * hcon [I] Handle to console screen buffer
1066 * bAbsolute [I] Coordinate type flag
1067 * window [I] Address of new window rectangle
1072 BOOL WINAPI SetConsoleWindowInfo(HANDLE hCon, BOOL bAbsolute, LPSMALL_RECT window)
1074 SMALL_RECT p = *window;
1079 CONSOLE_SCREEN_BUFFER_INFO csbi;
1080 if (!GetConsoleScreenBufferInfo(hCon, &csbi))
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)
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 );
1103 /******************************************************************************
1104 * SetConsoleTextAttribute [KERNEL32.@] Sets colors for text
1106 * Sets the foreground and background color attributes of characters
1107 * written to the screen buffer.
1113 BOOL WINAPI SetConsoleTextAttribute(HANDLE hConsoleOutput, WORD wAttr)
1117 SERVER_START_REQ(set_console_output_info)
1119 req->handle = hConsoleOutput;
1121 req->mask = SET_CONSOLE_OUTPUT_INFO_ATTR;
1122 ret = !wine_server_call_err( req );
1129 /******************************************************************************
1130 * SetConsoleScreenBufferSize [KERNEL32.@] Changes size of console
1133 * hConsoleOutput [I] Handle to console screen buffer
1134 * dwSize [I] New size in character rows and cols
1140 BOOL WINAPI SetConsoleScreenBufferSize(HANDLE hConsoleOutput, COORD dwSize)
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 );
1157 /******************************************************************************
1158 * ScrollConsoleScreenBufferA [KERNEL32.@]
1161 BOOL WINAPI ScrollConsoleScreenBufferA(HANDLE hConsoleOutput, LPSMALL_RECT lpScrollRect,
1162 LPSMALL_RECT lpClipRect, COORD dwDestOrigin,
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 /******************************************************************
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;
1190 req->data.ch = lpFill->Char.UnicodeChar;
1191 req->data.attr = lpFill->Attributes;
1192 wine_server_call_err( req );
1197 /******************************************************************************
1198 * ScrollConsoleScreenBufferW [KERNEL32.@]
1202 BOOL WINAPI ScrollConsoleScreenBufferW(HANDLE hConsoleOutput, LPSMALL_RECT lpScrollRect,
1203 LPSMALL_RECT lpClipRect, COORD dwDestOrigin,
1211 CONSOLE_SCREEN_BUFFER_INFO csbi;
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);
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))
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 */
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);
1247 clip.Right = csbi.dwSize.X - 1;
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 );
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
1281 for (j = max(lpScrollRect->Top, clip.Top); j <= min(lpScrollRect->Bottom, clip.Bottom); j++)
1283 inside = dst.Top <= j && j <= dst.Bottom;
1285 for (i = max(lpScrollRect->Left, clip.Left); i <= min(lpScrollRect->Right, clip.Right); i++)
1287 if (inside && dst.Left <= i && i <= dst.Right)
1291 fill_line_uniform(hConsoleOutput, start, j, i - start, lpFill);
1297 if (start == -1) start = i;
1301 fill_line_uniform(hConsoleOutput, start, j, i - start, lpFill);
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)
1326 SERVER_START_REQ( get_console_input_history )
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;
1344 /******************************************************************
1345 * CONSOLE_AppendHistory
1349 BOOL CONSOLE_AppendHistory(const WCHAR* ptr)
1351 size_t len = strlenW(ptr);
1354 while (len && (ptr[len - 1] == '\n' || ptr[len - 1] == '\r')) len--;
1356 SERVER_START_REQ( append_console_input_history )
1359 wine_server_add_data( req, ptr, len * sizeof(WCHAR) );
1360 ret = !wine_server_call_err( req );
1366 /******************************************************************
1367 * CONSOLE_GetNumHistoryEntries
1371 unsigned CONSOLE_GetNumHistoryEntries(void)
1374 SERVER_START_REQ(get_console_input_info)
1377 if (!wine_server_call_err( req )) ret = reply->history_index;
1383 /******************************************************************
1384 * CONSOLE_HandleCtrlC
1386 * Check whether the shall manipulate CtrlC events
1388 int CONSOLE_HandleCtrlC(void)
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
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;