/* * Win32 kernel functions * * Copyright 1995 Martin von Loewis and Cameron Heide * Copyright 1997 Karl Garrison * Copyright 1998 John Richardson * Copyright 1998 Marcus Meissner */ /* FIXME: * - Completely lacks SCREENBUFFER interface. * - No abstraction for something other than xterm. * - Output sometimes is buffered (We switched off buffering by ~ICANON ?) */ /* Reference applications: * - IDA (interactive disassembler) full version 3.75. Works. * - LYNX/W32. Works mostly, some keys crash it. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_ERRNO_H #include #endif #include #include #include "winbase.h" #include "windef.h" #include "wingdi.h" #include "wine/winuser16.h" #include "wine/keyboard16.h" #include "thread.h" #include "file.h" #include "process.h" #include "winerror.h" #include "wincon.h" #include "heap.h" #include "server.h" #include "debugtools.h" #include "winnls.h" DEFAULT_DEBUG_CHANNEL(console) /* Ascii -> VK, generated by calling VkKeyScanA(i) */ static int vkkeyscan_table[256] = { 0,0,0,0,0,0,0,0,8,9,0,0,0,13,0,0,0,0,0,19,145,556,0,0,0,0,0,27,0,0,0, 0,32,305,478,307,308,309,311,222,313,304,312,443,188,189,190,191,48, 49,50,51,52,53,54,55,56,57,442,186,444,187,446,447,306,321,322,323, 324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340, 341,342,343,344,345,346,219,220,221,310,445,192,65,66,67,68,69,70,71, 72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,475,476,477, 448,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,400,0,0,0,0,0,0 }; static int mapvkey_0[256]={ 0,0,0,0,0,0,0,0,14,15,0,0,0,28,0,0,42,29,56,69,58,0,0,0,0,0,0,1,0,0, 0,0,57,73,81,79,71,75,72,77,80,0,0,0,55,82,83,0,11,2,3,4,5,6,7,8,9, 10,0,0,0,0,0,0,0,30,48,46,32,18,33,34,35,23,36,37,38,50,49,24,25,16, 19,31,20,22,47,17,45,21,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,55,78,0,74, 0,53,59,60,61,62,63,64,65,66,67,68,87,88,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,69,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,13,51,12,52,53,41,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,43,27,40,76,96,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; static int mapvkey_1[256]={ 0,27,49,50,51,52,53,54,55,56,57,48,189,187,8,9,81,87,69,82,84,89,85, 73,79,80,219,221,13,17,65,83,68,70,71,72,74,75,76,186,222,192,16,220, 90,88,67,86,66,78,77,188,190,191,16,106,18,32,20,112,113,114,115,116, 117,118,119,120,121,144,145,36,38,33,109,37,223,39,107,35,40,34,45, 46,0,0,0,122,123,0,0,0,0,0,0,0,224,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0 }; /* FIXME: Should be in an internal header file. OK, so which one? Used by CONSOLE_makecomplex. */ int wine_openpty(int *master, int *slave, char *name, struct termios *term, struct winsize *winsize); /**************************************************************************** * CONSOLE_GetPid */ static int CONSOLE_GetPid( HANDLE handle ) { int ret = 0; SERVER_START_REQ { struct get_console_info_request *req = server_alloc_req( sizeof(*req), 0 ); req->handle = handle; if (!server_call( REQ_GET_CONSOLE_INFO )) ret = req->pid; } SERVER_END_REQ; return ret; } /**************************************************************************** * XTERM_string_to_IR [internal] * * Transfers a string read from XTERM to INPUT_RECORDs and adds them to the * queue. Does translation of vt100 style function keys and xterm-mouse clicks. */ static void CONSOLE_string_to_IR( HANDLE hConsoleInput,unsigned char *buf,int len) { int j,k; INPUT_RECORD ir; DWORD junk; for (j=0;j 'normal' keyboard event */ ir.EventType = 1; /* Key_event */ ir.Event.KeyEvent.bKeyDown = 1; ir.Event.KeyEvent.wRepeatCount = 0; ir.Event.KeyEvent.dwControlKeyState = 0; if (inchar & 0x80) { ir.Event.KeyEvent.dwControlKeyState|=LEFT_ALT_PRESSED; inchar &= ~0x80; } ir.Event.KeyEvent.wVirtualKeyCode = vkkeyscan_table[inchar]; if (ir.Event.KeyEvent.wVirtualKeyCode & 0x0100) ir.Event.KeyEvent.dwControlKeyState|=SHIFT_PRESSED; if (ir.Event.KeyEvent.wVirtualKeyCode & 0x0200) ir.Event.KeyEvent.dwControlKeyState|=LEFT_CTRL_PRESSED; if (ir.Event.KeyEvent.wVirtualKeyCode & 0x0400) ir.Event.KeyEvent.dwControlKeyState|=LEFT_ALT_PRESSED; ir.Event.KeyEvent.wVirtualScanCode = mapvkey_0[ ir.Event.KeyEvent.wVirtualKeyCode & 0x00ff ]; /* VirtualKeyCodes to ScanCode */ ir.Event.KeyEvent.uChar.AsciiChar = inchar; if ((inchar==127)||(inchar=='\b')) { /* backspace */ ir.Event.KeyEvent.uChar.AsciiChar = '\b'; /* FIXME: hmm */ ir.Event.KeyEvent.wVirtualScanCode = 0x0e; ir.Event.KeyEvent.wVirtualKeyCode = VK_BACK; } else { if ((inchar=='\n')||(inchar=='\r')) { ir.Event.KeyEvent.uChar.AsciiChar = '\r'; ir.Event.KeyEvent.wVirtualKeyCode = VK_RETURN; ir.Event.KeyEvent.wVirtualScanCode = 0x1c; ir.Event.KeyEvent.dwControlKeyState = 0; } else { if (inchar<' ') { /* FIXME: find good values for ^X */ ir.Event.KeyEvent.wVirtualKeyCode = 0xdead; ir.Event.KeyEvent.wVirtualScanCode = 0xbeef; } } } assert(WriteConsoleInputA( hConsoleInput, &ir, 1, &junk )); ir.Event.KeyEvent.bKeyDown = 0; assert(WriteConsoleInputA( hConsoleInput, &ir, 1, &junk )); continue; } /* inchar is ESC */ if ((j==len-1) || (buf[j+1]!='[')) {/* add ESCape on its own */ ir.EventType = 1; /* Key_event */ ir.Event.KeyEvent.bKeyDown = 1; ir.Event.KeyEvent.wRepeatCount = 0; ir.Event.KeyEvent.wVirtualKeyCode = VK_ESCAPE; ir.Event.KeyEvent.wVirtualScanCode = mapvkey_0[ ir.Event.KeyEvent.wVirtualKeyCode ]; ir.Event.KeyEvent.dwControlKeyState = 0; ir.Event.KeyEvent.uChar.AsciiChar = 27; assert(WriteConsoleInputA( hConsoleInput, &ir, 1, &junk )); ir.Event.KeyEvent.bKeyDown = 0; assert(WriteConsoleInputA( hConsoleInput, &ir, 1, &junk )); continue; } for (k=j;k='A') && (buf[k]<='Z')) || ((buf[k]>='a') && (buf[k]<='z')) || (buf[k]=='~') ) break; } if (k) or * Release (ESCM# */ if (k1) { /* If we spot an ESC, we flush all up to it * since we can be sure that we have a complete * sequence. */ CONSOLE_string_to_IR(handle,buf,len-1); buf = HeapReAlloc(GetProcessHeap(),0,buf,1); buf[0] = 27; len = 1; } escape_seen = 1; } } CONSOLE_string_to_IR(handle,buf,len); HeapFree(GetProcessHeap(),0,buf); } /****************************************************************************** * read_console_input * * Helper function for ReadConsole, ReadConsoleInput and PeekConsoleInput */ static BOOL read_console_input( HANDLE handle, LPINPUT_RECORD buffer, DWORD count, LPDWORD read, BOOL flush ) { BOOL ret; count = min( count, REQUEST_MAX_VAR_SIZE/sizeof(INPUT_RECORD) ); SERVER_START_REQ { struct read_console_input_request *req = server_alloc_req( sizeof(*req), count*sizeof(INPUT_RECORD) ); req->handle = handle; req->flush = flush; if ((ret = !server_call( REQ_READ_CONSOLE_INPUT ))) { if (count) memcpy( buffer, server_data_ptr(req), server_data_size(req) ); if (read) *read = req->read; } } SERVER_END_REQ; return ret; } /****************************************************************************** * SetConsoleCtrlHandler [KERNEL32.459] Adds function to calling process list * * PARAMS * func [I] Address of handler function * add [I] Handler to add or remove * * RETURNS * Success: TRUE * Failure: FALSE * * CHANGED * James Sutherland (JamesSutherland@gmx.de) * Added global variables console_ignore_ctrl_c and handlers[] * Does not yet do any error checking, or set LastError if failed. * This doesn't yet matter, since these handlers are not yet called...! */ static unsigned int console_ignore_ctrl_c = 0; static HANDLER_ROUTINE *handlers[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; BOOL WINAPI SetConsoleCtrlHandler( HANDLER_ROUTINE *func, BOOL add ) { unsigned int alloc_loop = sizeof(handlers)/sizeof(HANDLER_ROUTINE *); unsigned int done = 0; FIXME("(%p,%i) - no error checking or testing yet\n", func, add); if (!func) { console_ignore_ctrl_c = add; return TRUE; } if (add) { for (;alloc_loop--;) if (!handlers[alloc_loop] && !done) { handlers[alloc_loop] = func; done++; } if (!done) FIXME("Out of space on CtrlHandler table\n"); return(done); } else { for (;alloc_loop--;) if (handlers[alloc_loop] == func && !done) { handlers[alloc_loop] = 0; done++; } if (!done) WARN("Attempt to remove non-installed CtrlHandler %p\n", func); return (done); } return (done); } /****************************************************************************** * GenerateConsoleCtrlEvent [KERNEL32.275] Simulate a CTRL-C or CTRL-BREAK * * PARAMS * dwCtrlEvent [I] Type of event * dwProcessGroupID [I] Process group ID to send event to * * NOTES * Doesn't yet work...! * * RETURNS * Success: True * Failure: False (and *should* [but doesn't] set LastError) */ BOOL WINAPI GenerateConsoleCtrlEvent( DWORD dwCtrlEvent, DWORD dwProcessGroupID ) { if (dwCtrlEvent != CTRL_C_EVENT && dwCtrlEvent != CTRL_BREAK_EVENT) { ERR("invalid event %d for PGID %ld\n", (unsigned short)dwCtrlEvent, dwProcessGroupID ); return FALSE; } if (dwProcessGroupID == GetCurrentProcessId() ) { FIXME("Attempt to send event %d to self - stub\n", (unsigned short)dwCtrlEvent ); return FALSE; } FIXME("event %d to external PGID %ld - not implemented yet\n", (unsigned short)dwCtrlEvent, dwProcessGroupID ); return FALSE; } /****************************************************************************** * CreateConsoleScreenBuffer [KERNEL32.151] Creates a console screen buffer * * PARAMS * dwDesiredAccess [I] Access flag * dwShareMode [I] Buffer share mode * sa [I] Security attributes * dwFlags [I] Type of buffer to create * lpScreenBufferData [I] Reserved * * NOTES * Should call SetLastError * * RETURNS * Success: Handle to new console screen buffer * Failure: INVALID_HANDLE_VALUE */ HANDLE WINAPI CreateConsoleScreenBuffer( DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES sa, DWORD dwFlags, LPVOID lpScreenBufferData ) { FIXME("(%ld,%ld,%p,%ld,%p): stub\n",dwDesiredAccess, dwShareMode, sa, dwFlags, lpScreenBufferData); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return INVALID_HANDLE_VALUE; } /*********************************************************************** * GetConsoleScreenBufferInfo (KERNEL32.190) */ BOOL WINAPI GetConsoleScreenBufferInfo( HANDLE hConsoleOutput, LPCONSOLE_SCREEN_BUFFER_INFO csbi ) { csbi->dwSize.X = 80; csbi->dwSize.Y = 24; csbi->dwCursorPosition.X = 0; csbi->dwCursorPosition.Y = 0; csbi->wAttributes = 0; csbi->srWindow.Left = 0; csbi->srWindow.Right = 79; csbi->srWindow.Top = 0; csbi->srWindow.Bottom = 23; csbi->dwMaximumWindowSize.X = 80; csbi->dwMaximumWindowSize.Y = 24; return TRUE; } /****************************************************************************** * SetConsoleActiveScreenBuffer [KERNEL32.623] Sets buffer to current console * * RETURNS * Success: TRUE * Failure: FALSE */ BOOL WINAPI SetConsoleActiveScreenBuffer( HANDLE hConsoleOutput) /* [in] Handle to console screen buffer */ { FIXME("(%x): stub\n", hConsoleOutput); return FALSE; } /*********************************************************************** * GetLargestConsoleWindowSize (KERNEL32.226) */ COORD WINAPI GetLargestConsoleWindowSize( HANDLE hConsoleOutput ) { COORD c; c.X = 80; c.Y = 24; return c; } /* gcc doesn't return structures the same way as dwords */ DWORD WINAPI WIN32_GetLargestConsoleWindowSize( HANDLE hConsoleOutput ) { COORD c = GetLargestConsoleWindowSize( hConsoleOutput ); return *(DWORD *)&c; } /*********************************************************************** * FreeConsole (KERNEL32.267) */ BOOL WINAPI FreeConsole(VOID) { BOOL ret; SERVER_START_REQ { struct free_console_request *req = server_alloc_req( sizeof(*req), 0 ); ret = !server_call( REQ_FREE_CONSOLE ); } SERVER_END_REQ; return ret; } /************************************************************************* * CONSOLE_OpenHandle * * Open a handle to the current process console. */ HANDLE CONSOLE_OpenHandle( BOOL output, DWORD access, LPSECURITY_ATTRIBUTES sa ) { int ret = -1; SERVER_START_REQ { struct open_console_request *req = server_alloc_req( sizeof(*req), 0 ); req->output = output; req->access = access; req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle); SetLastError(0); if (!server_call( REQ_OPEN_CONSOLE )) ret = req->handle; } SERVER_END_REQ; return ret; } /************************************************************************* * CONSOLE_make_complex [internal] * * Turns a CONSOLE kernel object into a complex one. * (switches from output/input using the terminal where WINE was started to * its own xterm). * * This makes simple commandline tools pipeable, while complex commandline * tools work without getting messed up by debugoutput. * * All other functions should work indedependend from this call. * * To test for complex console: pid == 0 -> simple, otherwise complex. */ static BOOL CONSOLE_make_complex(HANDLE handle) { struct termios term; char buf[256]; char c = '\0'; int i,xpid,master,slave,pty_handle; if (CONSOLE_GetPid( handle )) return TRUE; /* already complex */ MESSAGE("Console: Making console complex (creating an xterm)...\n"); if (tcgetattr(0, &term) < 0) { /* ignore failure, or we can't run from a script */ } term.c_lflag = ~(ECHO|ICANON); if (wine_openpty(&master, &slave, NULL, &term, NULL) < 0) return FALSE; if ((xpid=fork()) == 0) { tcsetattr(slave, TCSADRAIN, &term); close( slave ); sprintf(buf, "-Sxx%d", master); /* "-fn vga" for VGA font. Harmless if vga is not present: * xterm: unable to open font "vga", trying "fixed".... */ execlp("xterm", "xterm", buf, "-fn","vga",NULL); ERR("error creating AllocConsole xterm\n"); exit(1); } pty_handle = FILE_DupUnixHandle( slave, GENERIC_READ | GENERIC_WRITE ); close( master ); close( slave ); if (pty_handle == -1) return FALSE; /* most xterms like to print their window ID when used with -S; * read it and continue before the user has a chance... */ for (i = 0; i < 10000; i++) { BOOL ok = ReadFile( pty_handle, &c, 1, NULL, NULL ); if (!ok && !c) usleep(100); /* wait for xterm to be created */ else if (c == '\n') break; } if (i == 10000) { ERR("can't read xterm WID\n"); CloseHandle( pty_handle ); return FALSE; } SERVER_START_REQ { struct set_console_fd_request *req = server_alloc_req( sizeof(*req), 0 ); req->handle = handle; req->file_handle = pty_handle; req->pid = xpid; server_call( REQ_SET_CONSOLE_FD ); } SERVER_END_REQ; CloseHandle( pty_handle ); /* enable mouseclicks */ strcpy( buf, "\033[?1002h" ); WriteFile(handle,buf,strlen(buf),NULL,NULL); strcpy( buf, "\033]2;" ); if (GetConsoleTitleA( buf + 4, sizeof(buf) - 5 )) { strcat( buf, "\a" ); WriteFile(handle,buf,strlen(buf),NULL,NULL); } return TRUE; } /*********************************************************************** * AllocConsole (KERNEL32.103) * * creates an xterm with a pty to our program */ BOOL WINAPI AllocConsole(VOID) { BOOL ret; HANDLE hStderr; int handle_in, handle_out; TRACE("()\n"); SERVER_START_REQ { struct alloc_console_request *req = server_alloc_req( sizeof(*req), 0 ); req->access = GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE; req->inherit = FALSE; ret = !server_call( REQ_ALLOC_CONSOLE ); handle_in = req->handle_in; handle_out = req->handle_out; } SERVER_END_REQ; if (!ret) return FALSE; if (!DuplicateHandle( GetCurrentProcess(), handle_out, GetCurrentProcess(), &hStderr, 0, TRUE, DUPLICATE_SAME_ACCESS )) { CloseHandle( handle_in ); CloseHandle( handle_out ); FreeConsole(); return FALSE; } /* NT resets the STD_*_HANDLEs on console alloc */ SetStdHandle( STD_INPUT_HANDLE, handle_in ); SetStdHandle( STD_OUTPUT_HANDLE, handle_out ); SetStdHandle( STD_ERROR_HANDLE, hStderr ); SetLastError(ERROR_SUCCESS); SetConsoleTitleA("Wine Console"); return TRUE; } /****************************************************************************** * GetConsoleCP [KERNEL32.295] Returns the OEM code page for the console * * RETURNS * Code page code */ UINT WINAPI GetConsoleCP(VOID) { return GetACP(); } /*********************************************************************** * GetConsoleOutputCP (KERNEL32.189) */ UINT WINAPI GetConsoleOutputCP(VOID) { return GetConsoleCP(); } /*********************************************************************** * GetConsoleMode (KERNEL32.188) */ BOOL WINAPI GetConsoleMode(HANDLE hcon,LPDWORD mode) { BOOL ret; SERVER_START_REQ { struct get_console_mode_request *req = server_alloc_req( sizeof(*req), 0 ); req->handle = hcon; ret = !server_call( REQ_GET_CONSOLE_MODE ); if (ret && mode) *mode = req->mode; } SERVER_END_REQ; return ret; } /****************************************************************************** * SetConsoleMode [KERNEL32.628] Sets input mode of console's input buffer * * PARAMS * hcon [I] Handle to console input or screen buffer * mode [I] Input or output mode to set * * RETURNS * Success: TRUE * Failure: FALSE */ BOOL WINAPI SetConsoleMode( HANDLE hcon, DWORD mode ) { BOOL ret; SERVER_START_REQ { struct set_console_mode_request *req = server_alloc_req( sizeof(*req), 0 ); req->handle = hcon; req->mode = mode; ret = !server_call( REQ_SET_CONSOLE_MODE ); } SERVER_END_REQ; return ret; } /****************************************************************************** * SetConsoleOutputCP [KERNEL32.629] Set the output codepage used by the console * * PARAMS * cp [I] code page to set * * RETURNS * Success: TRUE * Failure: FALSE */ BOOL WINAPI SetConsoleOutputCP( UINT cp ) { FIXME("stub\n"); return TRUE; } /*********************************************************************** * GetConsoleTitleA (KERNEL32.191) */ DWORD WINAPI GetConsoleTitleA(LPSTR title,DWORD size) { DWORD ret = 0; HANDLE hcon; if ((hcon = CreateFileA( "CONOUT$", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0 )) == INVALID_HANDLE_VALUE) return 0; SERVER_START_REQ { struct get_console_info_request *req = server_alloc_req( sizeof(*req), REQUEST_MAX_VAR_SIZE ); req->handle = hcon; if (!server_call( REQ_GET_CONSOLE_INFO )) { ret = server_data_size(req); size = min( size-1, ret ); memcpy( title, server_data_ptr(req), size ); title[size] = 0; } } SERVER_END_REQ; CloseHandle( hcon ); return ret; } /****************************************************************************** * GetConsoleTitleW [KERNEL32.192] Retrieves title string for console * * PARAMS * title [O] Address of buffer for title * size [I] Size of buffer * * RETURNS * Success: Length of string copied * Failure: 0 */ DWORD WINAPI GetConsoleTitleW( LPWSTR title, DWORD size ) { char *tmp; DWORD ret; if (!(tmp = HeapAlloc( GetProcessHeap(), 0, size ))) return 0; ret = GetConsoleTitleA( tmp, size ); lstrcpyAtoW( title, tmp ); HeapFree( GetProcessHeap(), 0, tmp ); return ret; } /*********************************************************************** * WriteConsoleA (KERNEL32.729) */ BOOL WINAPI WriteConsoleA( HANDLE hConsoleOutput, LPCVOID lpBuffer, DWORD nNumberOfCharsToWrite, LPDWORD lpNumberOfCharsWritten, LPVOID lpReserved ) { /* FIXME: should I check if this is a console handle? */ return WriteFile(hConsoleOutput, lpBuffer, nNumberOfCharsToWrite, lpNumberOfCharsWritten, NULL); } #define CADD(c) \ if (bufused==curbufsize-1) \ buffer = HeapReAlloc(GetProcessHeap(),0,buffer,(curbufsize+=100));\ buffer[bufused++]=c; #define SADD(s) { char *x=s;while (*x) {CADD(*x);x++;}} /*********************************************************************** * WriteConsoleOutputA (KERNEL32.732) */ BOOL WINAPI WriteConsoleOutputA( HANDLE hConsoleOutput, LPCHAR_INFO lpBuffer, COORD dwBufferSize, COORD dwBufferCoord, LPSMALL_RECT lpWriteRegion) { int i,j,off=0,lastattr=-1; int offbase; char sbuf[20],*buffer=NULL; int bufused=0,curbufsize = 100; DWORD res; CONSOLE_SCREEN_BUFFER_INFO csbi; const int colormap[8] = { 0,4,2,6, 1,5,3,7, }; CONSOLE_make_complex(hConsoleOutput); buffer = HeapAlloc(GetProcessHeap(),0,curbufsize); offbase = (dwBufferCoord.Y - 1) * dwBufferSize.X + (dwBufferCoord.X - lpWriteRegion->Left); TRACE("orig rect top = %d, bottom=%d, left=%d, right=%d\n", lpWriteRegion->Top, lpWriteRegion->Bottom, lpWriteRegion->Left, lpWriteRegion->Right ); GetConsoleScreenBufferInfo(hConsoleOutput, &csbi); sprintf(sbuf,"%c7",27);SADD(sbuf); /* Step 1. Make (Bottom,Right) offset of intersection with Screen Buffer */ lpWriteRegion->Bottom = min(lpWriteRegion->Bottom, csbi.dwSize.Y-1) - lpWriteRegion->Top; lpWriteRegion->Right = min(lpWriteRegion->Right, csbi.dwSize.X-1) - lpWriteRegion->Left; /* Step 2. If either offset is negative, then no action should be performed. (Implies that requested rectangle is outside the current screen buffer rectangle.) */ if ((lpWriteRegion->Bottom < 0) || (lpWriteRegion->Right < 0)) { /* readjust (Bottom Right) for rectangle */ lpWriteRegion->Bottom += lpWriteRegion->Top; lpWriteRegion->Right += lpWriteRegion->Left; TRACE("invisible rect top = %d, bottom=%d, left=%d, right=%d\n", lpWriteRegion->Top, lpWriteRegion->Bottom, lpWriteRegion->Left, lpWriteRegion->Right ); HeapFree(GetProcessHeap(),0,buffer); return TRUE; } /* Step 3. Intersect with source rectangle */ lpWriteRegion->Bottom = lpWriteRegion->Top - dwBufferCoord.Y + min(lpWriteRegion->Bottom + dwBufferCoord.Y, dwBufferSize.Y-1); lpWriteRegion->Right = lpWriteRegion->Left - dwBufferCoord.X + min(lpWriteRegion->Right + dwBufferCoord.X, dwBufferSize.X-1); TRACE("clipped rect top = %d, bottom=%d, left=%d,right=%d\n", lpWriteRegion->Top, lpWriteRegion->Bottom, lpWriteRegion->Left, lpWriteRegion->Right ); /* Validate above computations made sense, if not then issue error and fudge to single character rectangle */ if ((lpWriteRegion->Bottom < lpWriteRegion->Top) || (lpWriteRegion->Right < lpWriteRegion->Left)) { ERR("Invalid clipped rectangle top = %d, bottom=%d, left=%d,right=%d\n", lpWriteRegion->Top, lpWriteRegion->Bottom, lpWriteRegion->Left, lpWriteRegion->Right ); lpWriteRegion->Bottom = lpWriteRegion->Top; lpWriteRegion->Right = lpWriteRegion->Left; } /* Now do the real processing and move the characters */ for (i=lpWriteRegion->Top;i<=lpWriteRegion->Bottom;i++) { offbase += dwBufferSize.X; sprintf(sbuf,"%c[%d;%dH",27,i+1,lpWriteRegion->Left+1); SADD(sbuf); for (j=lpWriteRegion->Left;j<=lpWriteRegion->Right;j++) { off = j + offbase; if (lastattr!=lpBuffer[off].Attributes) { lastattr = lpBuffer[off].Attributes; sprintf(sbuf,"%c[0;%s3%d;4%dm", 27, (lastattr & FOREGROUND_INTENSITY)?"1;":"", colormap[lastattr&7], colormap[(lastattr&0x70)>>4] ); /* FIXME: BACKGROUND_INTENSITY */ SADD(sbuf); } CADD(lpBuffer[off].Char.AsciiChar); } } sprintf(sbuf,"%c[0m%c8",27,27);SADD(sbuf); WriteFile(hConsoleOutput,buffer,bufused,&res,NULL); HeapFree(GetProcessHeap(),0,buffer); return TRUE; } /*********************************************************************** * WriteConsoleOutputW (KERNEL32.734) */ BOOL WINAPI WriteConsoleOutputW( HANDLE hConsoleOutput, LPCHAR_INFO lpBuffer, COORD dwBufferSize, COORD dwBufferCoord, LPSMALL_RECT lpWriteRegion) { FIXME("(%d,%p,%dx%d,%dx%d,%p): stub\n", hConsoleOutput, lpBuffer, dwBufferSize.X,dwBufferSize.Y,dwBufferCoord.X,dwBufferCoord.Y,lpWriteRegion); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } /*********************************************************************** * WriteConsoleW (KERNEL32.577) */ BOOL WINAPI WriteConsoleW( HANDLE hConsoleOutput, LPCVOID lpBuffer, DWORD nNumberOfCharsToWrite, LPDWORD lpNumberOfCharsWritten, LPVOID lpReserved ) { BOOL ret; LPSTR xstring; DWORD n; n = WideCharToMultiByte(CP_ACP,0,lpBuffer,nNumberOfCharsToWrite,NULL,0,NULL,NULL); xstring=HeapAlloc( GetProcessHeap(), 0, n ); n = WideCharToMultiByte(CP_ACP,0,lpBuffer,nNumberOfCharsToWrite,xstring,n,NULL,NULL); /* FIXME: should I check if this is a console handle? */ ret= WriteFile(hConsoleOutput, xstring, n, lpNumberOfCharsWritten, NULL); /* FIXME: lpNumberOfCharsWritten should be converted to numofchars in UNICODE */ HeapFree( GetProcessHeap(), 0, xstring ); return ret; } /*********************************************************************** * ReadConsoleA (KERNEL32.419) */ BOOL WINAPI ReadConsoleA( HANDLE hConsoleInput, LPVOID lpBuffer, DWORD nNumberOfCharsToRead, LPDWORD lpNumberOfCharsRead, LPVOID lpReserved ) { int charsread = 0; LPSTR xbuf = (LPSTR)lpBuffer; TRACE("(%d,%p,%ld,%p,%p)\n", hConsoleInput,lpBuffer,nNumberOfCharsToRead, lpNumberOfCharsRead,lpReserved ); CONSOLE_get_input(hConsoleInput,FALSE); /* FIXME: should we read at least 1 char? The SDK does not say */ while (charsreadhandle = handle; memcpy( server_data_ptr(req), buffer, len * sizeof(INPUT_RECORD) ); if ((ret = !server_call( REQ_WRITE_CONSOLE_INPUT ))) { if (written) *written += req->written; count -= len; buffer += len; } } SERVER_END_REQ; } return ret; } /****************************************************************************** * WriteConsoleInputW [KERNEL32.731] Write data to a console input buffer * */ BOOL WINAPI WriteConsoleInputW( HANDLE handle, INPUT_RECORD *buffer, DWORD count, LPDWORD written ) { FIXME("(%d,%p,%ld,%p): stub!\n", handle, buffer, count, written); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } /*********************************************************************** * SetConsoleTitleA (KERNEL32.476) * * Sets the console title. * * We do not necessarily need to create a complex console for that, * but should remember the title and set it on creation of the latter. * (not fixed at this time). */ BOOL WINAPI SetConsoleTitleA(LPCSTR title) { size_t len = strlen(title); HANDLE hcon; DWORD written; BOOL ret; if ((hcon = CreateFileA( "CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0 )) == INVALID_HANDLE_VALUE) return FALSE; len = min( len, REQUEST_MAX_VAR_SIZE ); SERVER_START_REQ { struct set_console_info_request *req = server_alloc_req( sizeof(*req), len ); req->handle = hcon; req->mask = SET_CONSOLE_INFO_TITLE; memcpy( server_data_ptr(req), title, len ); ret = !server_call( REQ_SET_CONSOLE_INFO ); } SERVER_END_REQ; if (ret && CONSOLE_GetPid( hcon )) { /* only set title for complex console (own xterm) */ WriteFile( hcon, "\033]2;", 4, &written, NULL ); WriteFile( hcon, title, strlen(title), &written, NULL ); WriteFile( hcon, "\a", 1, &written, NULL ); } CloseHandle( hcon ); return ret; } /****************************************************************************** * SetConsoleTitleW [KERNEL32.477] Sets title bar string for console * * PARAMS * title [I] Address of new title * * NOTES * This should not be calling the A version * * RETURNS * Success: TRUE * Failure: FALSE */ BOOL WINAPI SetConsoleTitleW( LPCWSTR title ) { BOOL ret; LPSTR titleA = HEAP_strdupWtoA( GetProcessHeap(), 0, title ); ret = SetConsoleTitleA(titleA); HeapFree( GetProcessHeap(), 0, titleA ); return ret; } /****************************************************************************** * SetConsoleCursorPosition [KERNEL32.627] * Sets the cursor position in console * * PARAMS * hConsoleOutput [I] Handle of console screen buffer * dwCursorPosition [I] New cursor position coordinates * * RETURNS STD */ BOOL WINAPI SetConsoleCursorPosition( HANDLE hcon, COORD pos ) { char xbuf[20]; DWORD xlen; /* make console complex only if we change lines, not just in the line */ if (pos.Y) CONSOLE_make_complex(hcon); TRACE("%d (%dx%d)\n", hcon, pos.X , pos.Y ); /* x are columns, y rows */ if (pos.Y) /* full screen cursor absolute positioning */ sprintf(xbuf,"%c[%d;%dH", 0x1B, pos.Y+1, pos.X+1); else /* relative cursor positioning in line (\r to go to 0) */ sprintf(xbuf,"\r%c[%dC", 0x1B, pos.X); /* FIXME: store internal if we start using own console buffers */ WriteFile(hcon,xbuf,strlen(xbuf),&xlen,NULL); return TRUE; } /*********************************************************************** * GetNumberOfConsoleInputEvents (KERNEL32.246) */ BOOL WINAPI GetNumberOfConsoleInputEvents(HANDLE hcon,LPDWORD nrofevents) { return read_console_input( hcon, NULL, 0, nrofevents, FALSE ); } /*********************************************************************** * GetNumberOfConsoleMouseButtons (KERNEL32.358) */ BOOL WINAPI GetNumberOfConsoleMouseButtons(LPDWORD nrofbuttons) { FIXME("(%p): stub\n", nrofbuttons); *nrofbuttons = 2; return TRUE; } /****************************************************************************** * GetConsoleCursorInfo [KERNEL32.296] Gets size and visibility of console * * PARAMS * hcon [I] Handle to console screen buffer * cinfo [O] Address of cursor information * * RETURNS * Success: TRUE * Failure: FALSE */ BOOL WINAPI GetConsoleCursorInfo( HANDLE hcon, LPCONSOLE_CURSOR_INFO cinfo ) { BOOL ret; SERVER_START_REQ { struct get_console_info_request *req = server_alloc_req( sizeof(*req), 0 ); req->handle = hcon; ret = !server_call( REQ_GET_CONSOLE_INFO ); if (ret && cinfo) { cinfo->dwSize = req->cursor_size; cinfo->bVisible = req->cursor_visible; } } SERVER_END_REQ; return ret; } /****************************************************************************** * SetConsoleCursorInfo [KERNEL32.626] Sets size and visibility of cursor * * RETURNS * Success: TRUE * Failure: FALSE */ BOOL WINAPI SetConsoleCursorInfo( HANDLE hcon, /* [in] Handle to console screen buffer */ LPCONSOLE_CURSOR_INFO cinfo) /* [in] Address of cursor information */ { char buf[8]; DWORD xlen; BOOL ret; CONSOLE_make_complex(hcon); sprintf(buf,"\033[?25%c",cinfo->bVisible?'h':'l'); WriteFile(hcon,buf,strlen(buf),&xlen,NULL); SERVER_START_REQ { struct set_console_info_request *req = server_alloc_req( sizeof(*req), 0 ); req->handle = hcon; req->cursor_size = cinfo->dwSize; req->cursor_visible = cinfo->bVisible; req->mask = SET_CONSOLE_INFO_CURSOR; ret = !server_call( REQ_SET_CONSOLE_INFO ); } SERVER_END_REQ; return ret; } /****************************************************************************** * SetConsoleWindowInfo [KERNEL32.634] Sets size and position of console * * RETURNS * Success: TRUE * Failure: FALSE */ BOOL WINAPI SetConsoleWindowInfo( HANDLE hcon, /* [in] Handle to console screen buffer */ BOOL bAbsolute, /* [in] Coordinate type flag */ LPSMALL_RECT window) /* [in] Address of new window rectangle */ { FIXME("(%x,%d,%p): stub\n", hcon, bAbsolute, window); return TRUE; } /****************************************************************************** * SetConsoleTextAttribute [KERNEL32.631] Sets colors for text * * Sets the foreground and background color attributes of characters * written to the screen buffer. * * RETURNS * Success: TRUE * Failure: FALSE */ BOOL WINAPI SetConsoleTextAttribute(HANDLE hConsoleOutput,WORD wAttr) { const int colormap[8] = { 0,4,2,6, 1,5,3,7, }; DWORD xlen; char buffer[20]; TRACE("(%d,%d)\n",hConsoleOutput,wAttr); sprintf(buffer,"%c[0;%s3%d;4%dm", 27, (wAttr & FOREGROUND_INTENSITY)?"1;":"", colormap[wAttr&7], colormap[(wAttr&0x70)>>4] ); WriteFile(hConsoleOutput,buffer,strlen(buffer),&xlen,NULL); return TRUE; } /****************************************************************************** * SetConsoleScreenBufferSize [KERNEL32.630] Changes size of console * * PARAMS * hConsoleOutput [I] Handle to console screen buffer * dwSize [I] New size in character rows and cols * * RETURNS * Success: TRUE * Failure: FALSE */ BOOL WINAPI SetConsoleScreenBufferSize( HANDLE hConsoleOutput, COORD dwSize ) { FIXME("(%d,%dx%d): stub\n",hConsoleOutput,dwSize.X,dwSize.Y); return TRUE; } /****************************************************************************** * FillConsoleOutputCharacterA [KERNEL32.242] * * PARAMS * hConsoleOutput [I] Handle to screen buffer * cCharacter [I] Character to write * nLength [I] Number of cells to write to * dwCoord [I] Coords of first cell * lpNumCharsWritten [O] Pointer to number of cells written * * RETURNS * Success: TRUE * Failure: FALSE */ BOOL WINAPI FillConsoleOutputCharacterA( HANDLE hConsoleOutput, BYTE cCharacter, DWORD nLength, COORD dwCoord, LPDWORD lpNumCharsWritten) { long count; DWORD xlen; SetConsoleCursorPosition(hConsoleOutput,dwCoord); for(count=0;count