diff --git a/Makefile.in b/Makefile.in index 1ea3e0433c7..40f2aa89df0 100644 --- a/Makefile.in +++ b/Makefile.in @@ -28,7 +28,8 @@ PROGRAMS = \ # Programs that link with libwine LIBPROGRAMS = \ - debugger/winedbg + debugger/winedbg \ + programs/wineconsole/wineconsole # Libraries (not dlls) to build LIBRARIES = \ @@ -59,6 +60,7 @@ INSTALLSUBDIRS = \ include \ library \ ole \ + programs/wineconsole \ server \ tools \ tsx11 \ diff --git a/configure b/configure index 3fffe36a9b9..52f9d8248f9 100755 --- a/configure +++ b/configure @@ -7166,6 +7166,7 @@ programs/regtest/Makefile programs/uninstaller/Makefile programs/view/Makefile programs/wcmd/Makefile +programs/wineconsole/Makefile programs/winemine/Makefile programs/winetest/Makefile programs/winhelp/Makefile @@ -7426,6 +7427,7 @@ programs/regtest/Makefile programs/uninstaller/Makefile programs/view/Makefile programs/wcmd/Makefile +programs/wineconsole/Makefile programs/winemine/Makefile programs/winetest/Makefile programs/winhelp/Makefile diff --git a/configure.in b/configure.in index 333b38aacdf..aaee28082c4 100644 --- a/configure.in +++ b/configure.in @@ -1336,6 +1336,7 @@ programs/regtest/Makefile programs/uninstaller/Makefile programs/view/Makefile programs/wcmd/Makefile +programs/wineconsole/Makefile programs/winemine/Makefile programs/winetest/Makefile programs/winhelp/Makefile diff --git a/dlls/x11drv/x11drv_main.c b/dlls/x11drv/x11drv_main.c index 476eba62f33..202e92116fe 100644 --- a/dlls/x11drv/x11drv_main.c +++ b/dlls/x11drv/x11drv_main.c @@ -444,7 +444,7 @@ struct x11drv_thread_data *x11drv_init_thread_data(void) if (synchronous) XSynchronize( data->display, True ); wine_tsx11_unlock(); data->display_fd = FILE_DupUnixHandle( ConnectionNumber(data->display), - GENERIC_READ | SYNCHRONIZE ); + GENERIC_READ | SYNCHRONIZE, FALSE ); data->process_event_count = 0; NtCurrentTeb()->driver_data = data; return data; diff --git a/documentation/running.sgml b/documentation/running.sgml index 4dd950e1b42..e1277c6360d 100644 --- a/documentation/running.sgml +++ b/documentation/running.sgml @@ -93,6 +93,17 @@ Options: options that affect the Windows program should come after it. + + + If you want to run a console program (aka a CUI executable), use + wineconsole instead of wine + to start it. It will display the program in a separate Window + (this requires X11 to be run). If you don't, you'll still be able + to run able your program, in the Unix console were you're started + your program, but with very limited capacities (so, your program + might work, but your mileage may vary). This shall be improved + in the future. + diff --git a/documentation/wine.man.in b/documentation/wine.man.in index e87c1561476..88da2db0f26 100644 --- a/documentation/wine.man.in +++ b/documentation/wine.man.in @@ -1,5 +1,5 @@ .\" -*- nroff -*- -.TH WINE 1 "Aug 5, 2001" "Version 20010731" "Windows On Unix" +.TH WINE 1 "Oct 13, 2001" "Version 20011004" "Windows On Unix" .SH NAME wine \- run Windows programs on Unix .SH SYNOPSIS @@ -20,6 +20,16 @@ For debugging wine, use .I program instead. .PP +For running CUI executables (Windows console programs), use +.B wineconsole +instead of +.B wine +. This will display all the output in a separate windows (this requires X11 to +run). Not using +.B wineconsole +for CUI programs will only provide very limited console support, and your +program might not function properly. +.PP .B wine currently runs a growing list of applications written for all kinds of Windows versions >= Win2.0, e.g. Win3.1, Win95/98, NT. @@ -350,6 +360,11 @@ The .B wine program loader. .TP +.I @prefix@/bin/wineconsole +The +.B wine +program loader for CUI (console) applications. +.TP .I @prefix@/bin/dosmod The DOS program loader. .TP diff --git a/files/file.c b/files/file.c index b9e3fb00d6d..42f69e7509f 100644 --- a/files/file.c +++ b/files/file.c @@ -182,7 +182,7 @@ void FILE_SetDosError(void) * Duplicate a Unix handle into a task handle. * Returns 0 on failure. */ -HANDLE FILE_DupUnixHandle( int fd, DWORD access ) +HANDLE FILE_DupUnixHandle( int fd, DWORD access, BOOL inherit ) { HANDLE ret; @@ -191,6 +191,7 @@ HANDLE FILE_DupUnixHandle( int fd, DWORD access ) SERVER_START_REQ( alloc_file_handle ) { req->access = access; + req->inherit = inherit; req->fd = fd; SERVER_CALL(); ret = req->handle; @@ -255,14 +256,15 @@ int FILE_GetUnixHandle( HANDLE handle, DWORD access ) * Open a handle to the current process console. * Returns 0 on failure. */ -static HANDLE FILE_OpenConsole( BOOL output, DWORD access, LPSECURITY_ATTRIBUTES sa ) +static HANDLE FILE_OpenConsole( BOOL output, DWORD access, DWORD sharing, LPSECURITY_ATTRIBUTES sa ) { HANDLE ret; SERVER_START_REQ( open_console ) { - req->output = output; + req->from = output; req->access = access; + req->share = sharing; req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle); SetLastError(0); SERVER_CALL_ERR(); @@ -477,12 +479,12 @@ HANDLE WINAPI CreateFileA( LPCSTR filename, DWORD access, DWORD sharing, /* Open a console for CONIN$ or CONOUT$ */ if (!strcasecmp(filename, "CONIN$")) { - ret = FILE_OpenConsole( FALSE, access, sa ); + ret = FILE_OpenConsole( FALSE, access, sharing, sa ); goto done; } if (!strcasecmp(filename, "CONOUT$")) { - ret = FILE_OpenConsole( TRUE, access, sa ); + ret = FILE_OpenConsole( TRUE, access, sharing, sa ); goto done; } @@ -1452,15 +1454,14 @@ BOOL WINAPI ReadFile( HANDLE hFile, LPVOID buffer, DWORD bytesToRead, if (!bytesToRead) return TRUE; unix_handle = FILE_GetUnixHandleType( hFile, GENERIC_READ, &type ); - if (unix_handle == -1) - return FALSE; - switch(type) + switch (type) { case FD_TYPE_OVERLAPPED: - if(!overlapped) + if (unix_handle == -1) return FALSE; + if (!overlapped) { - close(unix_handle); + close(unix_handle); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } @@ -1493,23 +1494,28 @@ BOOL WINAPI ReadFile( HANDLE hFile, LPVOID buffer, DWORD bytesToRead, SetLastError(ERROR_IO_PENDING); return FALSE; + case FD_TYPE_CONSOLE: + return ReadConsoleA(hFile, buffer, bytesToRead, bytesRead, NULL); default: - if(overlapped) - { - close(unix_handle); - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - break; + /* normal unix files */ + if (unix_handle == -1) + return FALSE; + if (overlapped) + { + close(unix_handle); + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + break; } /* code for synchronous reads */ while ((result = read( unix_handle, buffer, bytesToRead )) == -1) { - if ((errno == EAGAIN) || (errno == EINTR)) continue; - if ((errno == EFAULT) && !IsBadWritePtr( buffer, bytesToRead )) continue; - FILE_SetDosError(); - break; + if ((errno == EAGAIN) || (errno == EINTR)) continue; + if ((errno == EFAULT) && !IsBadWritePtr( buffer, bytesToRead )) continue; + FILE_SetDosError(); + break; } close( unix_handle ); if (result == -1) return FALSE; @@ -1648,6 +1654,7 @@ BOOL WINAPI WriteFile( HANDLE hFile, LPCVOID buffer, DWORD bytesToWrite, LPDWORD bytesWritten, LPOVERLAPPED overlapped ) { int unix_handle, result; + DWORD type; TRACE("%d %p %ld %p %p\n", hFile, buffer, bytesToWrite, bytesWritten, overlapped ); @@ -1659,8 +1666,18 @@ BOOL WINAPI WriteFile( HANDLE hFile, LPCVOID buffer, DWORD bytesToWrite, if ( overlapped ) return WriteFileEx(hFile, buffer, bytesToWrite, overlapped, NULL); - unix_handle = FILE_GetUnixHandle( hFile, GENERIC_WRITE ); - if (unix_handle == -1) return FALSE; + unix_handle = FILE_GetUnixHandleType( hFile, GENERIC_WRITE, &type ); + + switch (type) + { + case FD_TYPE_CONSOLE: + TRACE("%d %s %ld %p %p\n", hFile, debugstr_an(buffer, bytesToWrite), bytesToWrite, + bytesWritten, overlapped ); + return WriteConsoleA(hFile, buffer, bytesToWrite, bytesWritten, NULL); + default: + if (unix_handle == -1) + return FALSE; + } /* synchronous file write */ while ((result = write( unix_handle, buffer, bytesToWrite )) == -1) diff --git a/include/file.h b/include/file.h index 260f8dd113a..5efc9a5c5b6 100644 --- a/include/file.h +++ b/include/file.h @@ -71,7 +71,7 @@ inline static int FILE_contains_path (LPCSTR name) extern int FILE_strcasecmp( const char *str1, const char *str2 ); extern int FILE_strncasecmp( const char *str1, const char *str2, int len ); extern void FILE_SetDosError(void); -extern HANDLE FILE_DupUnixHandle( int fd, DWORD access ); +extern HANDLE FILE_DupUnixHandle( int fd, DWORD access, BOOL inherit ); extern int FILE_GetUnixHandle( HANDLE handle, DWORD access ); extern BOOL FILE_Stat( LPCSTR unixName, BY_HANDLE_FILE_INFORMATION *info ); extern HFILE16 FILE_Dup2( HFILE16 hFile1, HFILE16 hFile2 ); diff --git a/include/wincon.h b/include/wincon.h index 016e0d10020..233acda2005 100644 --- a/include/wincon.h +++ b/include/wincon.h @@ -17,6 +17,8 @@ extern "C" { #define ENABLE_ECHO_INPUT 0x04 #define ENABLE_WINDOW_INPUT 0x08 #define ENABLE_MOUSE_INPUT 0x10 +/* Wine only code (extension) */ +#define WINE_ENABLE_LINE_INPUT_EMACS 0x80 #define ENABLE_PROCESSED_OUTPUT 0x01 #define ENABLE_WRAP_AT_EOL_OUTPUT 0x02 diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index b83ca78eeae..94f40d6927d 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -577,6 +577,7 @@ struct alloc_file_handle_request { struct request_header __header; unsigned int access; + int inherit; int fd; handle_t handle; }; @@ -755,8 +756,9 @@ struct alloc_console_request struct request_header __header; unsigned int access; int inherit; + void* pid; handle_t handle_in; - handle_t handle_out; + handle_t event; }; @@ -767,25 +769,69 @@ struct free_console_request }; +#define CONSOLE_RENDERER_NONE_EVENT 0x00 +#define CONSOLE_RENDERER_TITLE_EVENT 0x01 +#define CONSOLE_RENDERER_ACTIVE_SB_EVENT 0x02 +#define CONSOLE_RENDERER_SB_RESIZE_EVENT 0x03 +#define CONSOLE_RENDERER_UPDATE_EVENT 0x04 +#define CONSOLE_RENDERER_CURSOR_POS_EVENT 0x05 +#define CONSOLE_RENDERER_CURSOR_GEOM_EVENT 0x06 +#define CONSOLE_RENDERER_DISPLAY_EVENT 0x07 +#define CONSOLE_RENDERER_EXIT_EVENT 0x08 +struct console_renderer_event +{ + short event; + union + { + struct update + { + short top; + short bottom; + } update; + struct resize + { + short width; + short height; + } resize; + struct cursor_pos + { + short x; + short y; + } cursor_pos; + struct cursor_geom + { + short visible; + short size; + } cursor_geom; + struct display + { + short left; + short top; + short width; + short height; + } display; + } u; +}; -struct open_console_request + +struct get_console_renderer_events_request { struct request_header __header; - int output; - unsigned int access; - int inherit; handle_t handle; + /* VARARG(data,bytes); */ }; -struct set_console_fd_request +struct open_console_request { struct request_header __header; + int from; + + unsigned int access; + int inherit; + int share; handle_t handle; - int fd_in; - int fd_out; - int pid; }; @@ -808,31 +854,114 @@ struct set_console_mode_request -struct set_console_info_request +struct set_console_input_info_request { struct request_header __header; handle_t handle; int mask; - int cursor_size; - int cursor_visible; - /* VARARG(title,string); */ + handle_t active_sb; + int history_mode; + int history_size; + /* VARARG(title,unicode_str); */ }; -#define SET_CONSOLE_INFO_CURSOR 0x01 -#define SET_CONSOLE_INFO_TITLE 0x02 +#define SET_CONSOLE_INPUT_INFO_ACTIVE_SB 0x01 +#define SET_CONSOLE_INPUT_INFO_TITLE 0x02 +#define SET_CONSOLE_INPUT_INFO_HISTORY_MODE 0x04 +#define SET_CONSOLE_INPUT_INFO_HISTORY_SIZE 0x08 -struct get_console_info_request + +struct get_console_input_info_request { struct request_header __header; handle_t handle; - int cursor_size; - int cursor_visible; - int pid; - /* VARARG(title,string); */ + int history_mode; + int history_size; + int history_index; + /* VARARG(title,unicode_str); */ }; +struct append_console_input_history_request +{ + struct request_header __header; + handle_t handle; + /* VARARG(line,unicode_str); */ +}; + + + +struct get_console_input_history_request +{ + struct request_header __header; + handle_t handle; + int index; + /* VARARG(line,unicode_str); */ +}; + + + +struct create_console_output_request +{ + struct request_header __header; + handle_t handle_in; + int access; + int share; + int inherit; + handle_t handle_out; +}; + + + +struct set_console_output_info_request +{ + struct request_header __header; + handle_t handle; + int mask; + short int cursor_size; + short int cursor_visible; + short int cursor_x; + short int cursor_y; + short int width; + short int height; + short int attr; + short int win_left; + short int win_top; + short int win_right; + short int win_bottom; + short int max_width; + short int max_height; +}; +#define SET_CONSOLE_OUTPUT_INFO_CURSOR_GEOM 0x01 +#define SET_CONSOLE_OUTPUT_INFO_CURSOR_POS 0x02 +#define SET_CONSOLE_OUTPUT_INFO_SIZE 0x04 +#define SET_CONSOLE_OUTPUT_INFO_ATTR 0x08 +#define SET_CONSOLE_OUTPUT_INFO_DISPLAY_WINDOW 0x10 +#define SET_CONSOLE_OUTPUT_INFO_MAX_SIZE 0x20 + + + +struct get_console_output_info_request +{ + struct request_header __header; + handle_t handle; + short int cursor_size; + short int cursor_visible; + short int cursor_x; + short int cursor_y; + short int width; + short int height; + short int attr; + short int win_left; + short int win_top; + short int win_right; + short int win_bottom; + short int max_width; + short int max_height; +}; + + struct write_console_input_request { struct request_header __header; @@ -842,6 +971,7 @@ struct write_console_input_request }; + struct read_console_input_request { struct request_header __header; @@ -853,6 +983,53 @@ struct read_console_input_request +struct write_console_output_request +{ + struct request_header __header; + handle_t handle; + int mode; + + short int x; + short int y; + /* VARARG(data,bytes); */ + int written; +}; +#define WRITE_CONSOLE_MODE_TEXT 0x00 +#define WRITE_CONSOLE_MODE_ATTR 0x01 +#define WRITE_CONSOLE_MODE_TEXTATTR 0x02 +#define WRITE_CONSOLE_MODE_TEXTSTDATTR 0x03 +#define WRITE_CONSOLE_MODE_UNIFORM 0x04 + + + +struct read_console_output_request +{ + struct request_header __header; + handle_t handle; + short int x; + short int y; + short int w; + short int h; + short int eff_w; + short int eff_h; + /* VARARG(data,bytes); */ +}; + + +struct move_console_output_request +{ + struct request_header __header; + handle_t handle; + short int x_src; + short int y_src; + short int x_dst; + short int y_dst; + short int w; + short int h; +}; + + + struct create_change_notification_request { struct request_header __header; @@ -1843,14 +2020,22 @@ enum request REQ_enable_socket_event, REQ_alloc_console, REQ_free_console, + REQ_get_console_renderer_events, REQ_open_console, - REQ_set_console_fd, REQ_get_console_mode, REQ_set_console_mode, - REQ_set_console_info, - REQ_get_console_info, + REQ_set_console_input_info, + REQ_get_console_input_info, + REQ_append_console_input_history, + REQ_get_console_input_history, + REQ_create_console_output, + REQ_set_console_output_info, + REQ_get_console_output_info, REQ_write_console_input, REQ_read_console_input, + REQ_write_console_output, + REQ_read_console_output, + REQ_move_console_output, REQ_create_change_notification, REQ_create_mapping, REQ_open_mapping, @@ -1990,14 +2175,22 @@ union generic_request struct enable_socket_event_request enable_socket_event; struct alloc_console_request alloc_console; struct free_console_request free_console; + struct get_console_renderer_events_request get_console_renderer_events; struct open_console_request open_console; - struct set_console_fd_request set_console_fd; struct get_console_mode_request get_console_mode; struct set_console_mode_request set_console_mode; - struct set_console_info_request set_console_info; - struct get_console_info_request get_console_info; + struct set_console_input_info_request set_console_input_info; + struct get_console_input_info_request get_console_input_info; + struct append_console_input_history_request append_console_input_history; + struct get_console_input_history_request get_console_input_history; + struct create_console_output_request create_console_output; + struct set_console_output_info_request set_console_output_info; + struct get_console_output_info_request get_console_output_info; struct write_console_input_request write_console_input; struct read_console_input_request read_console_input; + struct write_console_output_request write_console_output; + struct read_console_output_request read_console_output; + struct move_console_output_request move_console_output; struct create_change_notification_request create_change_notification; struct create_mapping_request create_mapping; struct open_mapping_request open_mapping; @@ -2080,6 +2273,6 @@ union generic_request struct get_window_properties_request get_window_properties; }; -#define SERVER_PROTOCOL_VERSION 64 +#define SERVER_PROTOCOL_VERSION 65 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/loader/module.c b/loader/module.c index 2e9d8872a43..fa6fc0fe31b 100644 --- a/loader/module.c +++ b/loader/module.c @@ -1052,10 +1052,6 @@ BOOL WINAPI CreateProcessA( LPCSTR lpApplicationName, LPSTR lpCommandLine, /* Warn if unsupported features are used */ - if (dwCreationFlags & DETACHED_PROCESS) - FIXME("(%s,...): DETACHED_PROCESS ignored\n", name); - if (dwCreationFlags & CREATE_NEW_CONSOLE) - FIXME("(%s,...): CREATE_NEW_CONSOLE ignored\n", name); if (dwCreationFlags & NORMAL_PRIORITY_CLASS) FIXME("(%s,...): NORMAL_PRIORITY_CLASS ignored\n", name); if (dwCreationFlags & IDLE_PRIORITY_CLASS) diff --git a/programs/Makefile.in b/programs/Makefile.in index a0c47c7c68c..5a73a09e244 100644 --- a/programs/Makefile.in +++ b/programs/Makefile.in @@ -17,6 +17,7 @@ SUBDIRS = \ uninstaller \ view \ wcmd \ + wineconsole \ winemine \ winetest \ winhelp \ diff --git a/programs/wineconsole/.cvsignore b/programs/wineconsole/.cvsignore new file mode 100644 index 00000000000..ad904a9bb32 --- /dev/null +++ b/programs/wineconsole/.cvsignore @@ -0,0 +1,3 @@ +Makefile +wineconsole.spec.c +wineconsole_res.res diff --git a/programs/wineconsole/Makefile.in b/programs/wineconsole/Makefile.in new file mode 100644 index 00000000000..672de8f1590 --- /dev/null +++ b/programs/wineconsole/Makefile.in @@ -0,0 +1,19 @@ +EXTRADEFS = -DSTRICT -DUNICODE +TOPSRCDIR = @top_srcdir@ +TOPOBJDIR = ../.. +SRCDIR = @srcdir@ +VPATH = @srcdir@ +MODULE = wineconsole + +C_SRCS = \ + dialog.c \ + user.c \ + wineconsole.c + +RC_SRCS = \ + wineconsole_res.rc + +@MAKE_PROG_RULES@ + + +### Dependencies: diff --git a/programs/wineconsole/dialog.c b/programs/wineconsole/dialog.c new file mode 100644 index 00000000000..04220ac40f5 --- /dev/null +++ b/programs/wineconsole/dialog.c @@ -0,0 +1,515 @@ +/* dialog management for wineconsole + * (c) 2001 Eric Pouech + */ + +#include +#include "winecon_private.h" +#include "commctrl.h" +#include "prsht.h" + +/* FIXME: so far, part of the code is made in ASCII because the Uncode property sheet functions + * are not implemented yet + */ +struct dialog_info +{ + struct inner_data* data; /* pointer to current winecon info */ + HWND hDlg; /* handle to window dialog */ + int nFont; /* number of font size in size LB */ + struct font_info + { + TEXTMETRIC tm; + LOGFONT lf; + } *font; /* array of nFont. index sync'ed with SIZE LB */ +}; + +/****************************************************************** + * WCUSER_OptionDlgProc + * + * Dialog prop for the option property sheet + */ +static BOOL WINAPI WCUSER_OptionDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + struct dialog_info* di; + unsigned idc; + + switch (msg) + { + case WM_INITDIALOG: + di = (struct dialog_info*)((PROPSHEETPAGEA*)lParam)->lParam; + di->hDlg = hDlg; + SetWindowLongA(hDlg, DWL_USER, (DWORD)di); + if (di->data->cursor_size < 33) idc = IDC_OPT_CURSOR_SMALL; + else if (di->data->cursor_size < 66) idc = IDC_OPT_CURSOR_MEDIUM; + else idc = IDC_OPT_CURSOR_LARGE; + SendDlgItemMessage(hDlg, idc, BM_SETCHECK, BST_CHECKED, 0L); + SetDlgItemInt(hDlg, IDC_OPT_HIST_SIZE, WINECON_GetHistorySize(di->data->hConIn), FALSE); + if (WINECON_GetHistoryMode(di->data->hConIn)) + SendDlgItemMessage(hDlg, IDC_OPT_HIST_DOUBLE, BM_SETCHECK, BST_CHECKED, 0L); + return FALSE; /* because we set the focus */ + case WM_COMMAND: + break; + case WM_NOTIFY: + { + NMHDR* nmhdr = (NMHDR*)lParam; + + di = (struct dialog_info*)GetWindowLongA(hDlg, DWL_USER); + switch (nmhdr->code) + { + case PSN_SETACTIVE: + /* needed in propsheet to keep properly the selected radio button + * otherwise, the focus would be set to the first tab stop in the + * propsheet, which would always activate the first radio button + */ + if (IsDlgButtonChecked(hDlg, IDC_OPT_CURSOR_SMALL) == BST_CHECKED) + idc = IDC_OPT_CURSOR_SMALL; + else if (IsDlgButtonChecked(hDlg, IDC_OPT_CURSOR_MEDIUM) == BST_CHECKED) + idc = IDC_OPT_CURSOR_MEDIUM; + else + idc = IDC_OPT_CURSOR_LARGE; + PostMessage(hDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hDlg, idc), TRUE); + break; + case PSN_APPLY: + { + int curs_size; + int hist_size; + BOOL done; + + if (IsDlgButtonChecked(hDlg, IDC_OPT_CURSOR_SMALL) == BST_CHECKED) curs_size = 33; + else if (IsDlgButtonChecked(hDlg, IDC_OPT_CURSOR_MEDIUM) == BST_CHECKED) curs_size = 66; + else curs_size = 99; + if (curs_size != di->data->cursor_size) + { + CONSOLE_CURSOR_INFO cinfo; + cinfo.dwSize = curs_size; + cinfo.bVisible = di->data->cursor_visible; + SetConsoleCursorInfo(di->data->hConOut, &cinfo); + } + hist_size = GetDlgItemInt(hDlg, IDC_OPT_HIST_SIZE, &done, FALSE); + if (done) WINECON_SetHistorySize(di->data->hConIn, hist_size); + SetWindowLong(hDlg, DWL_MSGRESULT, PSNRET_NOERROR); + WINECON_SetHistoryMode(di->data->hConIn, + IsDlgButtonChecked(hDlg, IDC_OPT_HIST_DOUBLE) & BST_CHECKED); + return TRUE; + } + default: + return FALSE; + } + break; + } + default: + return FALSE; + } + return TRUE; +} + +/****************************************************************** + * WCUSER_FontPreviewProc + * + * Window proc for font previewer in font property sheet + */ +static LRESULT WINAPI WCUSER_FontPreviewProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_PAINT: + { + PAINTSTRUCT ps; + int font_idx; + int size_idx; + struct dialog_info* di; + + di = (struct dialog_info*)GetWindowLong(GetParent(hWnd), DWL_USER); + BeginPaint(hWnd, &ps); + + font_idx = SendDlgItemMessage(di->hDlg, IDC_FNT_LIST_FONT, LB_GETCURSEL, 0L, 0L); + size_idx = SendDlgItemMessage(di->hDlg, IDC_FNT_LIST_SIZE, LB_GETCURSEL, 0L, 0L); + + if (font_idx >= 0 && size_idx >= 0 && size_idx < di->nFont) + { + HFONT hFont, hOldFont; + WCHAR buf1[256]; + WCHAR buf2[256]; + int len1, len2; + + hFont = CreateFontIndirect(&di->font[size_idx].lf); + len1 = LoadString(GetModuleHandle(NULL), IDS_FNT_PREVIEW_1, + buf1, sizeof(buf1) / sizeof(WCHAR)); + len2 = LoadString(GetModuleHandle(NULL), IDS_FNT_PREVIEW_2, + buf2, sizeof(buf2) / sizeof(WCHAR)); + if (hFont && len1) + { + hOldFont = SelectObject(ps.hdc, hFont); + SetBkColor(ps.hdc, RGB(0x00, 0x00, 0x00)); + SetTextColor(ps.hdc, RGB(0xFF, 0xFF, 0xFF)); + TextOut(ps.hdc, 0, 0, buf1, len1); + if (len2) + TextOut(ps.hdc, 0, di->font[size_idx].tm.tmHeight, buf2, len2); + SelectObject(ps.hdc, hOldFont); + DeleteObject(hFont); + } + } + EndPaint(hWnd, &ps); + break; + } + default: + return DefWindowProc(hWnd, msg, wParam, lParam); + } + return 0L; +} + +/****************************************************************** + * font_enum + * + * + */ +static int CALLBACK font_enum_size2(const LOGFONT* lf, const TEXTMETRIC* tm, + DWORD FontType, LPARAM lParam) +{ + struct dialog_info* di = (struct dialog_info*)lParam; + + if (WCUSER_ValidateFontMetric(di->data, tm)) + { + di->nFont++; + } + return 1; +} + +static int CALLBACK font_enum(const LOGFONT* lf, const TEXTMETRIC* tm, + DWORD FontType, LPARAM lParam) +{ + struct dialog_info* di = (struct dialog_info*)lParam; + HDC hdc; + + if (WCUSER_ValidateFont(di->data, lf) && (hdc = GetDC(di->hDlg))) + { + di->nFont = 0; + EnumFontFamilies(hdc, lf->lfFaceName, font_enum_size2, (LPARAM)di); + if (di->nFont) + { + int idx; + idx = SendDlgItemMessage(di->hDlg, IDC_FNT_LIST_FONT, LB_ADDSTRING, + 0, (LPARAM)lf->lfFaceName); + } + ReleaseDC(di->hDlg, hdc); + } + return 1; +} + +/****************************************************************** + * font_enum_size + * + * + */ +static int CALLBACK font_enum_size(const LOGFONT* lf, const TEXTMETRIC* tm, + DWORD FontType, LPARAM lParam) +{ + struct dialog_info* di = (struct dialog_info*)lParam; + + if (WCUSER_ValidateFontMetric(di->data, tm)) + { + WCHAR buf[32]; + WCHAR fmt[] = {'%','l','d',0}; + int idx; + + /* we want the string to be sorted with a numeric order, not a lexicographic... + * do the job by hand... get where to insert the new string + */ + for (idx = 0; idx < di->nFont && tm->tmHeight > di->font[idx].tm.tmHeight; idx++); + wsprintfW(buf, fmt, tm->tmHeight); + SendDlgItemMessage(di->hDlg, IDC_FNT_LIST_SIZE, LB_INSERTSTRING, idx, (LPARAM)buf); + + /* now grow our arrays and insert to values at the same index than in the list box */ + di->font = HeapReAlloc(GetProcessHeap(), 0, di->font, sizeof(*di->font) * (di->nFont + 1)); + if (idx != di->nFont) + memmove(&di->font[idx + 1], &di->font[idx], (di->nFont - idx) * sizeof(*di->font)); + di->font[idx].tm = *tm; + di->font[idx].lf = *lf; + di->nFont++; + + } + return 1; +} + +/****************************************************************** + * select_font + * + * + */ +static BOOL select_font(struct dialog_info* di) +{ + int idx = SendDlgItemMessage(di->hDlg, IDC_FNT_LIST_SIZE, LB_GETCURSEL, 0L, 0L); + WCHAR buf[256]; + WCHAR fmt[128]; + + if (idx < 0 || idx >= di->nFont) + return FALSE; + + LoadString(GetModuleHandle(NULL), IDS_FNT_DISPLAY, fmt, sizeof(fmt) / sizeof(WCHAR)); + wsprintfW(buf, fmt, di->font[idx].tm.tmMaxCharWidth, di->font[idx].tm.tmHeight); + + SendDlgItemMessage(di->hDlg, IDC_FNT_FONT_INFO, WM_SETTEXT, 0, (LPARAM)buf); + InvalidateRect(GetDlgItem(di->hDlg, IDC_FNT_PREVIEW), NULL, TRUE); + UpdateWindow(GetDlgItem(di->hDlg, IDC_FNT_PREVIEW)); + return TRUE; +} + +/****************************************************************** + * fill_list_size + * + * fills the size list box according to selected family in font LB + */ +static BOOL fill_list_size(struct dialog_info* di, BOOL doInit) +{ + HDC hdc; + int idx; + WCHAR lfFaceName[LF_FACESIZE]; + + idx = SendDlgItemMessage(di->hDlg, IDC_FNT_LIST_FONT, LB_GETCURSEL, 0L, 0L); + if (idx < 0) return FALSE; + + hdc = GetDC(di->hDlg); + if (!hdc) return FALSE; + + SendDlgItemMessage(di->hDlg, IDC_FNT_LIST_FONT, LB_GETTEXT, idx, (LPARAM)lfFaceName); + SendDlgItemMessage(di->hDlg, IDC_FNT_LIST_SIZE, LB_RESETCONTENT, 0L, 0L); + if (di->font) HeapFree(GetProcessHeap(), 0, di->font); + di->nFont = 0; + di->font = NULL; + + EnumFontFamilies(hdc, lfFaceName, font_enum_size, (LPARAM)di); + ReleaseDC(di->hDlg, hdc); + + if (doInit) + { + for (idx = 0; idx < di->nFont; idx++) + { + if (memcmp(&di->data->logFont, &di->font[idx].lf, sizeof(LOGFONT)) == 0) + break; + } + if (idx == di->nFont) idx = 0; + } + else + idx = 0; + SendDlgItemMessage(di->hDlg, IDC_FNT_LIST_SIZE, LB_SETCURSEL, idx, 0L); + select_font(di); + return TRUE; +} + +/****************************************************************** + * fill_list_font + * + * Fills the font LB + */ +static BOOL fill_list_font(struct dialog_info* di) +{ + HDC hdc; + + hdc = GetDC(di->hDlg); + if (!hdc) return FALSE; + + SendDlgItemMessage(di->hDlg, IDC_FNT_LIST_FONT, LB_RESETCONTENT, 0L, 0L); + EnumFontFamilies(hdc, NULL, font_enum, (LPARAM)di); + ReleaseDC(di->hDlg, hdc); + if (SendDlgItemMessage(di->hDlg, IDC_FNT_LIST_FONT, LB_SELECTSTRING, + (WPARAM)-1, (LPARAM)di->data->logFont.lfFaceName) == LB_ERR) + SendDlgItemMessage(di->hDlg, IDC_FNT_LIST_FONT, LB_SETCURSEL, 0L, 0L); + fill_list_size(di, TRUE); + return TRUE; +} + +/****************************************************************** + * WCUSER_FontDlgProc + * + * Dialog proc for the Font property sheet + */ +static BOOL WINAPI WCUSER_FontDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + struct dialog_info* di; + + switch (msg) + { + case WM_INITDIALOG: + di = (struct dialog_info*)((PROPSHEETPAGEA*)lParam)->lParam; + di->hDlg = hDlg; + SetWindowLong(hDlg, DWL_USER, (DWORD)di); + fill_list_font(di); + break; + case WM_COMMAND: + di = (struct dialog_info*)GetWindowLong(hDlg, DWL_USER); + switch (LOWORD(wParam)) + { + case IDC_FNT_LIST_FONT: + if (HIWORD(wParam) == LBN_SELCHANGE) + { + fill_list_size(di, FALSE); + } + break; + case IDC_FNT_LIST_SIZE: + if (HIWORD(wParam) == LBN_SELCHANGE) + { + select_font(di); + } + break; + } + break; + case WM_NOTIFY: + { + NMHDR* nmhdr = (NMHDR*)lParam; + + di = (struct dialog_info*)GetWindowLong(hDlg, DWL_USER); + switch (nmhdr->code) + { + case PSN_APPLY: + { + int idx = SendDlgItemMessage(di->hDlg, IDC_FNT_LIST_SIZE, LB_GETCURSEL, 0L, 0L); + + if (idx >= 0 && idx < di->nFont) + { + WCUSER_SetFont(di->data, &di->font[idx].lf, &di->font[idx].tm); + } + SetWindowLong(hDlg, DWL_MSGRESULT, PSNRET_NOERROR); + return TRUE; + } + default: + return FALSE; + } + break; + } + default: + return FALSE; + } + return TRUE; +} + +/****************************************************************** + * WCUSER_ConfigDlgProc + * + * Dialog proc for the config property sheet + */ +static BOOL WINAPI WCUSER_ConfigDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + struct dialog_info* di; + + switch (msg) + { + case WM_INITDIALOG: + di = (struct dialog_info*)((PROPSHEETPAGEA*)lParam)->lParam; + di->hDlg = hDlg; + SetWindowLong(hDlg, DWL_USER, (DWORD)di); + SetDlgItemInt(hDlg, IDC_CNF_SB_WIDTH, di->data->sb_width, FALSE); + SetDlgItemInt(hDlg, IDC_CNF_SB_HEIGHT, di->data->sb_height, FALSE); + SetDlgItemInt(hDlg, IDC_CNF_WIN_WIDTH, di->data->win_width, FALSE); + SetDlgItemInt(hDlg, IDC_CNF_WIN_HEIGHT, di->data->win_height, FALSE); + break; + case WM_COMMAND: + di = (struct dialog_info*)GetWindowLong(hDlg, DWL_USER); + switch (LOWORD(wParam)) + { + } + break; + case WM_NOTIFY: + { + NMHDR* nmhdr = (NMHDR*)lParam; + + di = (struct dialog_info*)GetWindowLong(hDlg, DWL_USER); + switch (nmhdr->code) + { + case PSN_APPLY: + { + COORD sb; + SMALL_RECT pos; + BOOL st_w, st_h; + + sb.X = GetDlgItemInt(hDlg, IDC_CNF_SB_WIDTH, &st_w, FALSE); + sb.Y = GetDlgItemInt(hDlg, IDC_CNF_SB_HEIGHT, &st_h, FALSE); + if (st_w && st_h && (sb.X != di->data->sb_width || sb.Y != di->data->sb_height)) + { + SetConsoleScreenBufferSize(di->data->hConOut, sb); + } + + pos.Right = GetDlgItemInt(hDlg, IDC_CNF_WIN_WIDTH, &st_w, FALSE); + pos.Bottom = GetDlgItemInt(hDlg, IDC_CNF_WIN_HEIGHT, &st_h, FALSE); + if (st_w && st_h && + (pos.Right != di->data->win_width || pos.Bottom != di->data->win_height)) + { + pos.Left = pos.Top = 0; + pos.Right--; pos.Bottom--; + SetConsoleWindowInfo(di->data->hConOut, FALSE, &pos); + } + + SetWindowLong(hDlg, DWL_MSGRESULT, PSNRET_NOERROR); + return TRUE; + } + default: + return FALSE; + } + break; + } + default: + return FALSE; + } + return TRUE; +} + +/****************************************************************** + * WCUSER_GetProperties + * + * Runs the dialog box to set up the winconsole options + */ +BOOL WCUSER_GetProperties(struct inner_data* data) +{ + HPROPSHEETPAGE psPage[3]; + PROPSHEETPAGEA psp; + PROPSHEETHEADERA psHead; + WNDCLASS wndclass; + static WCHAR szFntPreview[] = {'W','i','n','e','C','o','n','F','o','n','t','P','r','e','v','i','e','w',0}; + struct dialog_info di; + + InitCommonControls(); + + di.data = data; + di.nFont = 0; + di.font = NULL; + + wndclass.style = 0; + wndclass.lpfnWndProc = WCUSER_FontPreviewProc; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = 0; + wndclass.hInstance = GetModuleHandle(NULL); + wndclass.hIcon = 0; + wndclass.hCursor = LoadCursor(0, IDC_ARROW); + wndclass.hbrBackground = GetStockObject(BLACK_BRUSH); + wndclass.lpszMenuName = NULL; + wndclass.lpszClassName = szFntPreview; + RegisterClass(&wndclass); + + memset(&psp, 0, sizeof(psp)); + psp.dwSize = sizeof(psp); + psp.dwFlags = 0; + psp.hInstance = wndclass.hInstance; + psp.lParam = (LPARAM)&di; + + psp.u.pszTemplate = MAKEINTRESOURCEA(IDD_OPTION); + psp.pfnDlgProc = WCUSER_OptionDlgProc; + psPage[0] = CreatePropertySheetPageA(&psp); + + psp.u.pszTemplate = MAKEINTRESOURCEA(IDD_FONT); + psp.pfnDlgProc = WCUSER_FontDlgProc; + psPage[1] = CreatePropertySheetPageA(&psp); + + psp.u.pszTemplate = MAKEINTRESOURCEA(IDD_CONFIG); + psp.pfnDlgProc = WCUSER_ConfigDlgProc; + psPage[2] = CreatePropertySheetPageA(&psp); + + memset(&psHead, 0, sizeof(psHead)); + psHead.dwSize = sizeof(psHead); + psHead.pszCaption = "Setup"; + psHead.nPages = 3; + psHead.hwndParent = data->hWnd; + psHead.u3.phpage = psPage; + + PropertySheetA(&psHead); + + return TRUE; +} + diff --git a/programs/wineconsole/user.c b/programs/wineconsole/user.c new file mode 100644 index 00000000000..2d25c664701 --- /dev/null +++ b/programs/wineconsole/user.c @@ -0,0 +1,957 @@ +/* + * a GUI application for displaying a console + * USER32 back end + * Copyright 2001 Eric Pouech + */ + +#include +#include "winecon_private.h" + +/* mapping console colors to RGB values */ +static COLORREF color_map[16] = +{ + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x80), RGB(0x00, 0x80, 0x00), RGB(0x00, 0x80, 0x80), + RGB(0x80, 0x00, 0x00), RGB(0x80, 0x00, 0x80), RGB(0x80, 0x80, 0x00), RGB(0x80, 0x80, 0x80), + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0xFF), RGB(0x00, 0xFF, 0x00), RGB(0x00, 0xFF, 0xFF), + RGB(0xFF, 0x00, 0x00), RGB(0xFF, 0x00, 0xFF), RGB(0xFF, 0xFF, 0x00), RGB(0xFF, 0xFF, 0xFF), +}; + +/****************************************************************** + * WCUSER_FillMemDC + * + * Fills the Mem DC with current cells values + */ +static void WCUSER_FillMemDC(const struct inner_data* data, int upd_tp, int upd_bm) +{ + unsigned i, j, k; + CHAR_INFO* cell; + HFONT hOldFont; + WORD attr; + WCHAR* line; + + if (!(line = HeapAlloc(GetProcessHeap(), 0, data->sb_width * sizeof(WCHAR)))) + {Trace(0, "OOM\n"); return;} + + hOldFont = SelectObject(data->hMemDC, data->hFont); + for (j = upd_tp; j <= upd_bm; j++) + { + cell = &data->cells[j * data->sb_width]; + for (i = 0; i < data->win_width; i++) + { + attr = cell[i].Attributes; + SetBkColor(data->hMemDC, color_map[attr & 0x0F]); + SetTextColor(data->hMemDC, color_map[(attr >> 4) & 0x0F]); + for (k = i; k < data->win_width && cell[k].Attributes == attr; k++) + { + line[k - i] = cell[k].Char.UnicodeChar; + } + TextOut(data->hMemDC, i * data->cell_width, j * data->cell_height, + line, k - i); + i = k - 1; + } + } + SelectObject(data->hMemDC, hOldFont); + HeapFree(GetProcessHeap(), 0, line); +} + +/****************************************************************** + * WCUSER_NewBitmap + * + * Either the font geometry or the sb geometry has changed. we need to recreate the + * bitmap geometry + */ +static void WCUSER_NewBitmap(struct inner_data* data, BOOL fill) +{ + HBITMAP hnew, hold; + + if (!data->sb_width || !data->sb_height) + return; + hnew = CreateCompatibleBitmap(data->hMemDC, + data->sb_width * data->cell_width, + data->sb_height * data->cell_height); + hold = SelectObject(data->hMemDC, hnew); + + if (data->hBitmap) + { + if (hold == data->hBitmap) + DeleteObject(data->hBitmap); + else + Trace(0, "leak\n"); + } + data->hBitmap = hnew; + if (fill) + WCUSER_FillMemDC(data, 0, data->sb_height - 1); +} + +/****************************************************************** + * WCUSER_ResizeScreenBuffer + * + * + */ +static void WCUSER_ResizeScreenBuffer(struct inner_data* data) +{ + WCUSER_NewBitmap(data, FALSE); +} + +/****************************************************************** + * WCUSER_PosCursor + * + * Set a new position for the cursor + */ +static void WCUSER_PosCursor(const struct inner_data* data) +{ + if (data->hWnd != GetFocus() || !data->cursor_visible) return; + + SetCaretPos((data->cursor.X - data->win_pos.X) * data->cell_width, + (data->cursor.Y - data->win_pos.Y) * data->cell_height); + ShowCaret(data->hWnd); +} + +/****************************************************************** + * WCUSER_ShapeCursor + * + * Sets a new shape for the cursor + */ +void WCUSER_ShapeCursor(struct inner_data* data, int size, int vis, BOOL force) +{ + if (force || size != data->cursor_size) + { + if (data->cursor_visible && data->hWnd == GetFocus()) DestroyCaret(); + if (data->cursor_bitmap) DeleteObject(data->cursor_bitmap); + data->cursor_bitmap = (HBITMAP)0; + if (size != 100) + { + int w16b; /* number of byets per row, aligned on word size */ + BYTE* ptr; + int i, j, nbl; + + w16b = ((data->cell_width + 15) & ~15) / 8; + ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, w16b * data->cell_height); + if (!ptr) {Trace(0, "OOM\n"); return;} + nbl = max((data->cell_height * size) / 100, 1); + for (j = data->cell_height - nbl; j < data->cell_height; j++) + { + for (i = 0; i < data->cell_width; i++) + { + ptr[w16b * j + (i / 8)] |= 0x80 >> (i & 7); + } + } + data->cursor_bitmap = CreateBitmap(data->cell_width, data->cell_height, 1, 1, ptr); + HeapFree(GetProcessHeap(), 0, ptr); + } + data->cursor_size = size; + data->cursor_visible = -1; + } + + vis = (vis) ? TRUE : FALSE; + if (force || vis != data->cursor_visible) + { + data->cursor_visible = vis; + if (data->hWnd == GetFocus()) + { + if (vis) + { + CreateCaret(data->hWnd, data->cursor_bitmap, data->cell_width, data->cell_height); + WCUSER_PosCursor(data); + } + else + { + DestroyCaret(); + } + } + } +} + +/****************************************************************** + * INECON_ComputePositions + * + * Recomputes all the components (mainly scroll bars) positions + */ +void WCUSER_ComputePositions(struct inner_data* data) +{ + RECT r; + int dx, dy; + + /* compute window size from desired client size */ + r.left = r.top = 0; + r.right = data->win_width * data->cell_width; + r.bottom = data->win_height * data->cell_height; + + if (IsRectEmpty(&r)) + { + ShowWindow(data->hWnd, SW_HIDE); + return; + } + + AdjustWindowRect(&r, GetWindowLong(data->hWnd, GWL_STYLE), FALSE); + + dx = dy = 0; + if (data->sb_width > data->win_width) + { + dy = GetSystemMetrics(SM_CYHSCROLL); + SetScrollRange(data->hWnd, SB_HORZ, 0, data->sb_width - data->win_width, FALSE); + SetScrollPos(data->hWnd, SB_HORZ, 0, FALSE); /* FIXME */ + ShowScrollBar(data->hWnd, SB_HORZ, TRUE); + } + else + { + ShowScrollBar(data->hWnd, SB_HORZ, FALSE); + } + + if (data->sb_height > data->win_height) + { + dx = GetSystemMetrics(SM_CXVSCROLL); + SetScrollRange(data->hWnd, SB_VERT, 0, data->sb_height - data->win_height, FALSE); + SetScrollPos(data->hWnd, SB_VERT, 0, FALSE); /* FIXME */ + ShowScrollBar(data->hWnd, SB_VERT, TRUE); + } + else + { + ShowScrollBar(data->hWnd, SB_VERT, FALSE); + } + + SetWindowPos(data->hWnd, 0, 0, 0, r.right - r.left + dx, r.bottom - r.top + dy, + SWP_NOMOVE|SWP_NOZORDER|SWP_SHOWWINDOW); + WCUSER_ShapeCursor(data, data->cursor_size, data->cursor_visible, TRUE); + WCUSER_PosCursor(data); +} + +/****************************************************************** + * WCUSER_SetTitle + * + * Sets the title to the wine console + */ +static void WCUSER_SetTitle(const struct inner_data* data) +{ + WCHAR buffer[256]; + + if (WINECON_GetConsoleTitle(data->hConIn, buffer, sizeof(buffer))) + SetWindowText(data->hWnd, buffer); +} + +/****************************************************************** + * WCUSER_SetFont + * + * + */ +BOOL WCUSER_SetFont(struct inner_data* data, const LOGFONT* logfont, const TEXTMETRIC* tm) +{ + if (!memcmp(logfont, &data->logFont, sizeof(LOGFONT))) return TRUE; + if (data->hFont) DeleteObject(data->hFont); + data->hFont = CreateFontIndirect(logfont); + if (!data->hFont) {Trace(0, "wrong font\n");return FALSE;} + data->cell_width = tm->tmMaxCharWidth; /* font is fixed Avg == Max */ + data->cell_height = tm->tmHeight; + data->logFont = *logfont; + WCUSER_ComputePositions(data); + WCUSER_NewBitmap(data, TRUE); + InvalidateRect(data->hWnd, NULL, FALSE); + UpdateWindow(data->hWnd); + return TRUE; +} + +/****************************************************************** + * WCUSER_GetCell + * + * Get a cell from the a relative coordinate in window (takes into + * account the scrolling) + */ +static COORD WCUSER_GetCell(const struct inner_data* data, LPARAM lParam) +{ + COORD c; + + c.X = data->win_pos.X + (short)LOWORD(lParam) / data->cell_width; + c.Y = data->win_pos.Y + (short)HIWORD(lParam) / data->cell_height; + + return c; +} + +/****************************************************************** + * WCUSER_GetSelectionRect + * + * Get the selection rectangle + */ +static void WCUSER_GetSelectionRect(const struct inner_data* data, LPRECT r) +{ + r->left = (min(data->selectPt1.X, data->selectPt2.X) ) * data->cell_width; + r->top = (min(data->selectPt1.Y, data->selectPt2.Y) ) * data->cell_height; + r->right = (max(data->selectPt1.X, data->selectPt2.X) + 1) * data->cell_width; + r->bottom = (max(data->selectPt1.Y, data->selectPt2.Y) + 1) * data->cell_height; +} + +/****************************************************************** + * WCUSER_SetSelection + * + * + */ +static void WCUSER_SetSelection(const struct inner_data* data, HDC hRefDC) +{ + HDC hDC; + RECT r; + + WCUSER_GetSelectionRect(data, &r); + hDC = hRefDC ? hRefDC : GetDC(data->hWnd); + if (hDC) + { + if (data->hWnd == GetFocus() && data->cursor_visible) + HideCaret(data->hWnd); + InvertRect(hDC, &r); + if (hDC != hRefDC) + ReleaseDC(data->hWnd, hDC); + if (data->hWnd == GetFocus() && data->cursor_visible) + ShowCaret(data->hWnd); + } +} + +/****************************************************************** + * WCUSER_MoveSelection + * + * + */ +static void WCUSER_MoveSelection(struct inner_data* data, COORD dst, BOOL final) +{ + RECT r; + HDC hDC; + + WCUSER_GetSelectionRect(data, &r); + hDC = GetDC(data->hWnd); + if (hDC) + { + if (data->hWnd == GetFocus() && data->cursor_visible) + HideCaret(data->hWnd); + InvertRect(hDC, &r); + } + data->selectPt2 = dst; + if (hDC) + { + WCUSER_GetSelectionRect(data, &r); + InvertRect(hDC, &r); + ReleaseDC(data->hWnd, hDC); + if (data->hWnd == GetFocus() && data->cursor_visible) + ShowCaret(data->hWnd); + } + if (final) + { + ReleaseCapture(); + data->hasSelection = TRUE; + } +} + +/****************************************************************** + * WCUSER_CopySelectionToClipboard + * + * Copies the current selection into the clipboard + */ +static void WCUSER_CopySelectionToClipboard(const struct inner_data* data) +{ + HANDLE hMem; + LPWSTR p; + unsigned w, h; + + w = abs(data->selectPt1.X - data->selectPt2.X) + 2; + h = abs(data->selectPt1.Y - data->selectPt2.Y) + 1; + + if (!OpenClipboard(data->hWnd)) return; + EmptyClipboard(); + + hMem = GlobalAlloc(GMEM_MOVEABLE, (w * h - 1) * sizeof(WCHAR)); + if (hMem && (p = GlobalLock(hMem))) + { + COORD c; + int y; + + c.X = data->win_pos.X + min(data->selectPt1.X, data->selectPt2.X); + c.Y = data->win_pos.Y + min(data->selectPt1.Y, data->selectPt2.Y); + + for (y = 0; y < h; y++, c.Y++) + { + ReadConsoleOutputCharacter(data->hConOut, &p[y * w], w - 1, c, NULL); + if (y < h - 1) p[y * w + w - 1] = '\n'; + } + GlobalUnlock(hMem); + SetClipboardData(CF_UNICODETEXT, hMem); + } + CloseClipboard(); +} + +/****************************************************************** + * WCUSER_PasteFromClipboard + * + * + */ +static void WCUSER_PasteFromClipboard(struct inner_data* data) +{ + HANDLE h; + WCHAR* ptr; + + if (!OpenClipboard(data->hWnd)) return; + h = GetClipboardData(CF_UNICODETEXT); + if (h && (ptr = GlobalLock(h))) + { + int i, len = GlobalSize(h) / sizeof(WCHAR); + INPUT_RECORD ir[2]; + DWORD n; + SHORT sh; + + ir[0].EventType = KEY_EVENT; + ir[0].Event.KeyEvent.wRepeatCount = 0; + ir[0].Event.KeyEvent.dwControlKeyState = 0; + ir[0].Event.KeyEvent.bKeyDown = TRUE; + + /* generate the corresponding input records */ + for (i = 0; i < len; i++) + { + /* FIXME: the modifying keys are not generated (shift, ctrl...) */ + sh = VkKeyScan(ptr[i]); + ir[0].Event.KeyEvent.wVirtualKeyCode = LOBYTE(sh); + ir[0].Event.KeyEvent.wVirtualScanCode = MapVirtualKey(LOBYTE(sh), 0); + ir[0].Event.KeyEvent.uChar.UnicodeChar = ptr[i]; + + ir[1] = ir[0]; + ir[1].Event.KeyEvent.bKeyDown = FALSE; + + WriteConsoleInput(data->hConIn, ir, 2, &n); + } + GlobalUnlock(h); + } + CloseClipboard(); +} + +static void WCUSER_Refresh(const struct inner_data* data, int tp, int bm) +{ + if (data->win_pos.Y <= bm && data->win_pos.Y + data->win_height >= tp) + { + RECT r; + + r.left = 0; + r.right = data->win_width * data->cell_width; + r.top = (tp - data->win_pos.Y) * data->cell_height; + r.bottom = (bm - data->win_pos.Y + 1) * data->cell_height; + InvalidateRect(data->hWnd, &r, FALSE); + WCUSER_FillMemDC(data, tp, bm); + UpdateWindow(data->hWnd); + } +} + +/****************************************************************** + * WCUSER_Paint + * + * + */ +static void WCUSER_Paint(const struct inner_data* data) +{ + PAINTSTRUCT ps; + + BeginPaint(data->hWnd, &ps); + BitBlt(ps.hdc, 0, 0, data->win_width * data->cell_width, data->win_height * data->cell_height, + data->hMemDC, data->win_pos.X * data->cell_width, data->win_pos.Y * data->cell_height, + SRCCOPY); + if (data->hasSelection) + WCUSER_SetSelection(data, ps.hdc); + EndPaint(data->hWnd, &ps); +} + +/****************************************************************** + * WCUSER_Scroll + * + * + */ +static void WCUSER_Scroll(struct inner_data* data, int pos, BOOL horz) +{ + if (horz) + { + SetScrollPos(data->hWnd, SB_HORZ, pos, TRUE); + data->win_pos.X = pos; + InvalidateRect(data->hWnd, NULL, FALSE); + } + else + { + SetScrollPos(data->hWnd, SB_VERT, pos, TRUE); + data->win_pos.Y = pos; + } + InvalidateRect(data->hWnd, NULL, FALSE); +} + +struct font_chooser { + struct inner_data* data; + int done; +}; + +/****************************************************************** + * WCUSER_ValidateFontMetric + * + * Returns true if the font described in tm is usable as a font for the renderer + */ +BOOL WCUSER_ValidateFontMetric(const struct inner_data* data, const TEXTMETRIC* tm) +{ + return tm->tmMaxCharWidth * data->win_width < GetSystemMetrics(SM_CXSCREEN) && + tm->tmHeight * data->win_height < GetSystemMetrics(SM_CYSCREEN) && + !tm->tmItalic && !tm->tmUnderlined && !tm->tmStruckOut; +} + +/****************************************************************** + * WCUSER_ValidateFont + * + * Returns true if the font family described in lf is usable as a font for the renderer + */ +BOOL WCUSER_ValidateFont(const struct inner_data* data, const LOGFONT* lf) +{ + return (lf->lfPitchAndFamily & 3) == FIXED_PITCH && (lf->lfPitchAndFamily & 0xF0) == FF_MODERN; +} + +/****************************************************************** + * get_first_font_enum_2 + * get_first_font_enum + * + * Helper functions to get a decent font for the renderer + */ +static int CALLBACK get_first_font_enum_2(const LOGFONT* lf, const TEXTMETRIC* tm, + DWORD FontType, LPARAM lParam) +{ + struct font_chooser* fc = (struct font_chooser*)lParam; + + if (WCUSER_ValidateFontMetric(fc->data, tm)) + { + WCUSER_SetFont(fc->data, lf, tm); + fc->done = 1; + return 0; + } + return 1; +} + +static int CALLBACK get_first_font_enum(const LOGFONT* lf, const TEXTMETRIC* tm, + DWORD FontType, LPARAM lParam) +{ + struct font_chooser* fc = (struct font_chooser*)lParam; + + if (WCUSER_ValidateFont(fc->data, lf)) + { + EnumFontFamilies(fc->data->hMemDC, lf->lfFaceName, get_first_font_enum_2, lParam); + return !fc->done; /* we just need the first matching one... */ + } + return 1; +} + +/****************************************************************** + * WCUSER_Create + * + * Creates the window for the rendering + */ +static LRESULT WCUSER_Create(HWND hWnd, LPCREATESTRUCT lpcs) +{ + struct inner_data* data; + HMENU hMenu; + HMENU hSubMenu; + WCHAR buff[256]; + HINSTANCE hInstance = GetModuleHandle(NULL); + + data = lpcs->lpCreateParams; + SetWindowLong(hWnd, 0L, (DWORD)data); + data->hWnd = hWnd; + + data->cursor_size = 101; /* invalid value, will trigger a complete cleanup */ + /* FIXME: error handling & memory cleanup */ + hSubMenu = CreateMenu(); + if (!hSubMenu) return 0; + + hMenu = GetSystemMenu(hWnd, FALSE); + if (!hMenu) return 0; + + LoadString(hInstance, IDS_MARK, buff, sizeof(buff) / sizeof(WCHAR)); + InsertMenu(hSubMenu, -1, MF_BYPOSITION|MF_STRING, IDS_MARK, buff); + LoadString(hInstance, IDS_COPY, buff, sizeof(buff) / sizeof(WCHAR)); + InsertMenu(hSubMenu, -1, MF_BYPOSITION|MF_STRING, IDS_COPY, buff); + LoadString(hInstance, IDS_PASTE, buff, sizeof(buff) / sizeof(WCHAR)); + InsertMenu(hSubMenu, -1, MF_BYPOSITION|MF_STRING, IDS_PASTE, buff); + LoadString(hInstance, IDS_SELECTALL, buff, sizeof(buff) / sizeof(WCHAR)); + InsertMenu(hSubMenu, -1, MF_BYPOSITION|MF_STRING, IDS_SELECTALL, buff); + LoadString(hInstance, IDS_SCROLL, buff, sizeof(buff) / sizeof(WCHAR)); + InsertMenu(hSubMenu, -1, MF_BYPOSITION|MF_STRING, IDS_SCROLL, buff); + LoadString(hInstance, IDS_SEARCH, buff, sizeof(buff) / sizeof(WCHAR)); + InsertMenu(hSubMenu, -1, MF_BYPOSITION|MF_STRING, IDS_SEARCH, buff); + + InsertMenu(hMenu, -1, MF_BYPOSITION|MF_SEPARATOR, 0, NULL); + LoadString(hInstance, IDS_EDIT, buff, sizeof(buff) / sizeof(WCHAR)); + InsertMenu(hMenu, -1, MF_BYPOSITION|MF_STRING|MF_POPUP, (UINT_PTR)hSubMenu, buff); + LoadString(hInstance, IDS_DEFAULT, buff, sizeof(buff) / sizeof(WCHAR)); + InsertMenu(hMenu, -1, MF_BYPOSITION|MF_STRING, IDS_DEFAULT, buff); + LoadString(hInstance, IDS_PROPERTY, buff, sizeof(buff) / sizeof(WCHAR)); + InsertMenu(hMenu, -1, MF_BYPOSITION|MF_STRING, IDS_PROPERTY, buff); + + data->hMemDC = CreateCompatibleDC(0); + if (!data->hMemDC) {Trace(0, "no mem dc\n");return 0;} + + return 0; +} + +/****************************************************************** + * WCUSER_SetMenuDetails + * + * Grays / ungrays the menu items according to their state + */ +static void WCUSER_SetMenuDetails(const struct inner_data* data) +{ + HMENU hMenu = GetSystemMenu(data->hWnd, FALSE); + + if (!hMenu) {Trace(0, "Issue in getting menu bits\n");return;} + + /* FIXME: set the various menu items to their state (if known) */ + EnableMenuItem(hMenu, IDS_DEFAULT, MF_BYCOMMAND|MF_GRAYED); + + EnableMenuItem(hMenu, IDS_MARK, MF_BYCOMMAND|MF_GRAYED); + EnableMenuItem(hMenu, IDS_COPY, MF_BYCOMMAND|(data->hasSelection ? MF_ENABLED : MF_GRAYED)); + EnableMenuItem(hMenu, IDS_PASTE, + MF_BYCOMMAND|(IsClipboardFormatAvailable(CF_UNICODETEXT) + ? MF_ENABLED : MF_GRAYED)); + /* Select all: always active */ + EnableMenuItem(hMenu, IDS_SCROLL, MF_BYCOMMAND|MF_GRAYED); + EnableMenuItem(hMenu, IDS_SEARCH, MF_BYCOMMAND|MF_GRAYED); +} + +/****************************************************************** + * WCUSER_GenerateInputRecord + * + * generates input_record from windows WM_KEYUP/WM_KEYDOWN messages + */ +static void WCUSER_GenerateInputRecord(struct inner_data* data, BOOL down, + WPARAM wParam, LPARAM lParam, BOOL sys) +{ + INPUT_RECORD ir; + DWORD n; + WCHAR buf[2]; + BYTE keyState[256]; + static WCHAR last; /* keep last char seen as feed for key up message */ + + ir.EventType = KEY_EVENT; + ir.Event.KeyEvent.bKeyDown = down; + ir.Event.KeyEvent.wRepeatCount = LOWORD(lParam); + ir.Event.KeyEvent.wVirtualKeyCode = wParam; + + ir.Event.KeyEvent.wVirtualScanCode = HIWORD(lParam) & 0xFF; + GetKeyboardState(keyState); + + ir.Event.KeyEvent.uChar.UnicodeChar = 0; + ir.Event.KeyEvent.dwControlKeyState = 0; + if (lParam & (1L << 24)) ir.Event.KeyEvent.dwControlKeyState |= ENHANCED_KEY; + if (keyState[VK_SHIFT] & 0x80) ir.Event.KeyEvent.dwControlKeyState |= SHIFT_PRESSED; + if (keyState[VK_CONTROL] & 0x80) ir.Event.KeyEvent.dwControlKeyState |= LEFT_CTRL_PRESSED; /* FIXME: gotta choose one */ + if (keyState[VK_LCONTROL] & 0x80) ir.Event.KeyEvent.dwControlKeyState |= LEFT_CTRL_PRESSED; + if (keyState[VK_RCONTROL] & 0x80) ir.Event.KeyEvent.dwControlKeyState |= RIGHT_CTRL_PRESSED; + if (sys) ir.Event.KeyEvent.dwControlKeyState |= LEFT_ALT_PRESSED; /* FIXME: gotta choose one */ + if (keyState[VK_LMENU] & 0x80) ir.Event.KeyEvent.dwControlKeyState |= LEFT_ALT_PRESSED; + if (keyState[VK_RMENU] & 0x80) ir.Event.KeyEvent.dwControlKeyState |= RIGHT_ALT_PRESSED; + if (keyState[VK_CAPITAL] & 0x01) ir.Event.KeyEvent.dwControlKeyState |= CAPSLOCK_ON; + if (keyState[VK_NUMLOCK] & 0x01) ir.Event.KeyEvent.dwControlKeyState |= NUMLOCK_ON; + if (keyState[VK_SCROLL] & 0x01) ir.Event.KeyEvent.dwControlKeyState |= SCROLLLOCK_ON; + + if (data->hasSelection && ir.Event.KeyEvent.dwControlKeyState == 0 && + ir.Event.KeyEvent.wVirtualKeyCode == VK_RETURN) + { + data->hasSelection = FALSE; + WCUSER_SetSelection(data, 0); + WCUSER_CopySelectionToClipboard(data); + return; + } + + if (!(ir.Event.KeyEvent.dwControlKeyState & ENHANCED_KEY)) + { + if (down) + { + switch (ToUnicode(wParam, HIWORD(lParam), keyState, buf, 2, 0)) + { + case 2: + /* FIXME... should generate two events... */ + /* fall thru */ + case 1: + last = buf[0]; + break; + default: + last = 0; + break; + } + } + ir.Event.KeyEvent.uChar.UnicodeChar = last; /* FIXME HACKY... and buggy 'coz it should be a stack, not a single value */ + if (!down) last = 0; + } + + WriteConsoleInput(data->hConIn, &ir, 1, &n); +} + +/****************************************************************** + * WCUSER_Proc + * + * + */ +static LRESULT CALLBACK WCUSER_Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + struct inner_data* data = (struct inner_data*)GetWindowLong(hWnd, 0); + + switch (uMsg) + { + case WM_CREATE: + return WCUSER_Create(hWnd, (LPCREATESTRUCT)lParam); + case WM_DESTROY: + data->hWnd = 0; + PostQuitMessage(0); + break; + case WM_PAINT: + WCUSER_Paint(data); + break; + case WM_KEYDOWN: + case WM_KEYUP: + WCUSER_GenerateInputRecord(data, uMsg == WM_KEYDOWN, wParam, lParam, FALSE); + break; + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + WCUSER_GenerateInputRecord(data, uMsg == WM_SYSKEYDOWN, wParam, lParam, TRUE); + break; + case WM_LBUTTONDOWN: + /* EPP if (wParam != MK_LBUTTON) */ + if (data->hasSelection) + { + data->hasSelection = FALSE; + } + else + { + data->selectPt1 = data->selectPt2 = WCUSER_GetCell(data, lParam); + SetCapture(data->hWnd); + } + WCUSER_SetSelection(data, 0); + break; + case WM_MOUSEMOVE: + /* EPP if (wParam != MK_LBUTTON) */ + if (GetCapture() == data->hWnd) + { + WCUSER_MoveSelection(data, WCUSER_GetCell(data, lParam), FALSE); + } + break; + case WM_LBUTTONUP: + /* EPP if (wParam != MK_LBUTTON) */ + if (GetCapture() == data->hWnd) + { + WCUSER_MoveSelection(data, WCUSER_GetCell(data, lParam), TRUE); + } + break; + case WM_SETFOCUS: + if (data->cursor_visible) + { + CreateCaret(data->hWnd, data->cursor_bitmap, data->cell_width, data->cell_height); + WCUSER_PosCursor(data); + } + break; + case WM_KILLFOCUS: + if (data->cursor_visible) + DestroyCaret(); + break; + case WM_HSCROLL: + { + int pos = data->win_pos.X; + + switch (LOWORD(wParam)) + { + case SB_PAGEUP: pos -= 8; break; + case SB_PAGEDOWN: pos += 8; break; + case SB_LINEUP: pos--; break; + case SB_LINEDOWN: pos++; break; + case SB_THUMBTRACK: pos = HIWORD(wParam); break; + default: break; + } + if (pos < 0) pos = 0; + if (pos > data->sb_width - data->win_width) pos = data->sb_width - data->win_width; + if (pos != data->win_pos.X) + { + ScrollWindow(hWnd, (data->win_pos.X - pos) * data->cell_width, 0, NULL, NULL); + data->win_pos.X = pos; + SetScrollPos(hWnd, SB_HORZ, pos, TRUE); + UpdateWindow(hWnd); + WCUSER_PosCursor(data); + WINECON_NotifyWindowChange(data); + } + } + break; + case WM_VSCROLL: + { + int pos = data->win_pos.Y; + + switch (LOWORD(wParam)) + { + case SB_PAGEUP: pos -= 8; break; + case SB_PAGEDOWN: pos += 8; break; + case SB_LINEUP: pos--; break; + case SB_LINEDOWN: pos++; break; + case SB_THUMBTRACK: pos = HIWORD(wParam); break; + default: break; + } + if (pos < 0) pos = 0; + if (pos > data->sb_height - data->win_height) pos = data->sb_height - data->win_height; + if (pos != data->win_pos.Y) + { + ScrollWindow(hWnd, 0, (data->win_pos.Y - pos) * data->cell_height, NULL, NULL); + data->win_pos.Y = pos; + SetScrollPos(hWnd, SB_VERT, pos, TRUE); + UpdateWindow(hWnd); + WCUSER_PosCursor(data); + WINECON_NotifyWindowChange(data); + } + } + break; + case WM_SYSCOMMAND: + switch (wParam) + { + case IDS_DEFAULT: + Trace(0, "unhandled yet command: %x\n", wParam); + break; + case IDS_PROPERTY: + WCUSER_GetProperties(data); + break; + default: + return DefWindowProc(hWnd, uMsg, wParam, lParam); + } + break; + case WM_RBUTTONDOWN: + WCUSER_GetProperties(data); + break; + case WM_COMMAND: + switch (wParam) + { + case IDS_MARK: + goto niy; + case IDS_COPY: + data->hasSelection = FALSE; + WCUSER_SetSelection(data, 0); + WCUSER_CopySelectionToClipboard(data); + break; + case IDS_PASTE: + WCUSER_PasteFromClipboard(data); + break; + case IDS_SELECTALL: + data->selectPt1.X = data->selectPt1.Y = 0; + data->selectPt2.X = (data->sb_width - 1) * data->cell_width; + data->selectPt2.Y = (data->sb_height - 1) * data->cell_height; + WCUSER_SetSelection(data, 0); + data->hasSelection = TRUE; + break; + case IDS_SCROLL: + case IDS_SEARCH: + niy: + Trace(0, "unhandled yet command: %x\n", wParam); + break; + default: + return DefWindowProc(hWnd, uMsg, wParam, lParam); + } + break; + case WM_INITMENUPOPUP: + if (!HIWORD(lParam)) return DefWindowProc(hWnd, uMsg, wParam, lParam); + WCUSER_SetMenuDetails(data); + break; + default: + return DefWindowProc(hWnd, uMsg, wParam, lParam); + } + return 0; +} + +/****************************************************************** + * WCUSER_DeleteBackend + * + * + */ +void WCUSER_DeleteBackend(struct inner_data* data) +{ + if (data->hWnd) DestroyWindow(data->hWnd); + if (data->hFont) DeleteObject(data->hFont); + if (data->cursor_bitmap) DeleteObject(data->cursor_bitmap); + if (data->hMemDC) DeleteDC(data->hMemDC); + if (data->hBitmap) DeleteObject(data->hBitmap); +} + +/****************************************************************** + * WCUSER_MainLoop + * + * + */ +static int WCUSER_MainLoop(struct inner_data* data) +{ + MSG msg; + + for (;;) + { + switch (MsgWaitForMultipleObjects(1, &data->hSynchro, FALSE, INFINITE, QS_ALLINPUT)) + { + case WAIT_OBJECT_0: + if (!WINECON_GrabChanges(data)) + PostQuitMessage(0); + break; + case WAIT_OBJECT_0+1: + switch (GetMessage(&msg, 0, 0, 0)) + { + case -1: /* the event handle became invalid, so exit */ + return -1; + case 0: /* WM_QUIT has been posted */ + return 0; + default: + DispatchMessage(&msg); + break; + } + break; + default: + Trace(0, "got pb\n"); + /* err */ + break; + } + } +} + +/****************************************************************** + * WCUSER_InitBackend + * + * Initialisation part II: creation of window. + * + */ +BOOL WCUSER_InitBackend(struct inner_data* data) +{ + static WCHAR wClassName[] = {'W','i','n','e','C','o','n','s','o','l','e','C','l','a','s','s',0}; + + WNDCLASS wndclass; + struct font_chooser fc; + + data->fnMainLoop = WCUSER_MainLoop; + data->fnPosCursor = WCUSER_PosCursor; + data->fnShapeCursor = WCUSER_ShapeCursor; + data->fnComputePositions = WCUSER_ComputePositions; + data->fnRefresh = WCUSER_Refresh; + data->fnResizeScreenBuffer = WCUSER_ResizeScreenBuffer; + data->fnSetTitle = WCUSER_SetTitle; + data->fnScroll = WCUSER_Scroll; + data->fnDeleteBackend = WCUSER_DeleteBackend; + + wndclass.style = 0; + wndclass.lpfnWndProc = WCUSER_Proc; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = sizeof(DWORD); + wndclass.hInstance = GetModuleHandle(NULL); + wndclass.hIcon = LoadIcon(0, IDI_WINLOGO); + wndclass.hCursor = LoadCursor(0, IDC_ARROW); + wndclass.hbrBackground = GetStockObject(BLACK_BRUSH); + wndclass.lpszMenuName = NULL; + wndclass.lpszClassName = wClassName; + + RegisterClass(&wndclass); + + CreateWindow(wndclass.lpszClassName, NULL, + WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_THICKFRAME|WS_MINIMIZEBOX|WS_HSCROLL|WS_VSCROLL, + CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, 0, 0, wndclass.hInstance, data); + if (!data->hWnd) return FALSE; + + /* force update of current data */ + WINECON_GrabChanges(data); + + /* try to find an acceptable font */ + fc.data = data; + fc.done = 0; + EnumFontFamilies(data->hMemDC, NULL, get_first_font_enum, (LPARAM)&fc); + + return fc.done; +} + + + diff --git a/programs/wineconsole/winecon_private.h b/programs/wineconsole/winecon_private.h new file mode 100644 index 00000000000..b444164dba7 --- /dev/null +++ b/programs/wineconsole/winecon_private.h @@ -0,0 +1,75 @@ +#include +#include +#include +#include + +#include "wineconsole_res.h" + +struct inner_data { + unsigned sb_width; /* active screen buffer width */ + unsigned sb_height; /* active screen buffer height */ + CHAR_INFO* cells; /* local copy of cells (sb_width * sb_height) */ + COORD win_pos; /* position (in cells) of visible part of screen buffer in window */ + unsigned win_width; /* size (in cells) of visible part of window (width & height) */ + unsigned win_height; + + COORD cursor; /* position in cells of cursor */ + int cursor_visible; + int cursor_size; /* in % of cell height */ + + HANDLE hConIn; /* console input handle */ + HANDLE hConOut; /* screen buffer handle: has to be changed when active sb changes */ + HANDLE hSynchro; /* waitable handle signalled by server when something in server has been modified */ + + int (*fnMainLoop)(struct inner_data* data); + void (*fnPosCursor)(const struct inner_data* data); + void (*fnShapeCursor)(struct inner_data* data, int size, int vis, BOOL force); + void (*fnComputePositions)(struct inner_data* data); + void (*fnRefresh)(const struct inner_data* data, int tp, int bm); + void (*fnResizeScreenBuffer)(struct inner_data* data); + void (*fnSetTitle)(const struct inner_data* data); + void (*fnScroll)(struct inner_data* data, int pos, BOOL horz); + void (*fnDeleteBackend)(struct inner_data* data); + + /* the following fields are only user by the USER backend (should be hidden in user) */ + HWND hWnd; /* handle to windows for rendering */ + HFONT hFont; /* font used for rendering, usually fixed */ + LOGFONT logFont; /* logFont dscription for used hFont */ + unsigned cell_width; /* width in pixels of a character */ + unsigned cell_height; /* height in pixels of a character */ + HDC hMemDC; /* memory DC holding the bitmap below */ + HBITMAP hBitmap; /* bitmap of display window content */ + + HBITMAP cursor_bitmap; /* bitmap used for the caret */ + BOOL hasSelection; /* a rectangular mouse selection has taken place */ + COORD selectPt1; /* start (and end) point of a mouse selection */ + COORD selectPt2; +}; + +# ifdef __GNUC__ +extern void XTracer(int level, const char* format, ...) __attribute__((format (printf,2,3))); +# else +extern void XTracer(int level, const char* format, ...); +# endif +#if 0 +/* Trace mode */ +# define Trace XTracer +#else +/* non trace mode */ +# define Trace (1) ? (void)0 : XTracer +#endif + +extern void WINECON_NotifyWindowChange(struct inner_data* data); +extern int WINECON_GetHistorySize(HANDLE hConIn); +extern BOOL WINECON_SetHistorySize(HANDLE hConIn, int size); +extern int WINECON_GetHistoryMode(HANDLE hConIn); +extern BOOL WINECON_SetHistoryMode(HANDLE hConIn, int mode); +extern BOOL WINECON_GetConsoleTitle(HANDLE hConIn, WCHAR* buffer, size_t len); +extern void WINECON_FetchCells(struct inner_data* data, int upd_tp, int upd_bm); +extern int WINECON_GrabChanges(struct inner_data* data); + +extern BOOL WCUSER_GetProperties(struct inner_data*); +extern BOOL WCUSER_SetFont(struct inner_data* data, const LOGFONT* font, const TEXTMETRIC* tm); +extern BOOL WCUSER_ValidateFont(const struct inner_data* data, const LOGFONT* lf); +extern BOOL WCUSER_ValidateFontMetric(const struct inner_data* data, const TEXTMETRIC* tm); +extern BOOL WCUSER_InitBackend(struct inner_data* data); diff --git a/programs/wineconsole/wineconsole.c b/programs/wineconsole/wineconsole.c new file mode 100644 index 00000000000..38ba58d424a --- /dev/null +++ b/programs/wineconsole/wineconsole.c @@ -0,0 +1,479 @@ +/* + * an application for displaying Win32 console + * + * Copyright 2001 Eric Pouech + */ + +#include +#include +#include "winecon_private.h" + +static int trace_level = 1; +void XTracer(int level, const char* format, ...) +{ + char buf[1024]; + va_list valist; + int len; + + if (level > trace_level) return; + + va_start(valist, format); + len = wvsnprintfA(buf, sizeof(buf), format, valist); + va_end(valist); + + if (len <= -1) + { + len = sizeof(buf) - 1; + buf[len] = 0; + buf[len - 1] = buf[len - 2] = buf[len - 3] = '.'; + } + fprintf(stderr, buf); +} + +/****************************************************************** + * WINECON_FetchCells + * + * updates the local copy of cells (band to update) + */ +void WINECON_FetchCells(struct inner_data* data, int upd_tp, int upd_bm) +{ + int step; + int j, nr; + + step = REQUEST_MAX_VAR_SIZE / (data->sb_width * 4); + + for (j = upd_tp; j <= upd_bm; j += step) + { + nr = min(step, upd_bm - j + 1); + SERVER_START_VAR_REQ( read_console_output, 4 * nr * data->sb_width ) + { + req->handle = (handle_t)data->hConOut; + req->x = 0; + req->y = j; + req->w = data->sb_width; + req->h = nr; + if (!SERVER_CALL_ERR()) + { + if (data->sb_width != req->eff_w || nr != req->eff_h) + Trace(0, "pb here... wrong eff_w %d/%d or eff_h %d/%d\n", + req->eff_w, data->sb_width, req->eff_h, nr); + memcpy(&data->cells[j * data->sb_width], server_data_ptr(req), + 4 * nr * data->sb_width); + } + } + SERVER_END_VAR_REQ; + } + data->fnRefresh(data, upd_tp, upd_bm); +} + +/****************************************************************** + * WINECON_NotifyWindowChange + * + * Inform server that visible window on sb has changed + */ +void WINECON_NotifyWindowChange(struct inner_data* data) +{ + SERVER_START_REQ( set_console_output_info ) + { + req->handle = (handle_t)data->hConOut; + req->win_left = data->win_pos.X; + req->win_top = data->win_pos.Y; + req->win_right = data->win_pos.X + data->win_width - 1; + req->win_bottom = data->win_pos.Y + data->win_height - 1; + req->mask = SET_CONSOLE_OUTPUT_INFO_DISPLAY_WINDOW; + if (!SERVER_CALL_ERR()) + { + } + } + SERVER_END_REQ; +} + +/****************************************************************** + * WINECON_GetHistorySize + * + * + */ +int WINECON_GetHistorySize(HANDLE hConIn) +{ + int ret = 0; + + SERVER_START_REQ(get_console_input_info) + { + req->handle = (handle_t)hConIn; + if (!SERVER_CALL_ERR()) ret = req->history_size; + } + SERVER_END_REQ; + return ret; +} + +/****************************************************************** + * WINECON_SetHistorySize + * + * + */ +BOOL WINECON_SetHistorySize(HANDLE hConIn, int size) +{ + BOOL ret; + + SERVER_START_REQ(set_console_input_info) + { + req->handle = (handle_t)hConIn; + req->mask = SET_CONSOLE_INPUT_INFO_HISTORY_SIZE; + req->history_size = size; + ret = !SERVER_CALL_ERR(); + } + SERVER_END_REQ; + return ret; +} + + +/****************************************************************** + * WINECON_GetHistoryMode + * + * + */ +int WINECON_GetHistoryMode(HANDLE hConIn) +{ + int ret = 0; + + SERVER_START_REQ(get_console_input_info) + { + req->handle = (handle_t)hConIn; + if (!SERVER_CALL_ERR()) ret = req->history_mode; + } + SERVER_END_REQ; + return ret; +} + +/****************************************************************** + * WINECON_SetHistoryMode + * + * + */ +BOOL WINECON_SetHistoryMode(HANDLE hConIn, int mode) +{ + BOOL ret; + + SERVER_START_REQ(set_console_input_info) + { + req->handle = (handle_t)hConIn; + req->mask = SET_CONSOLE_INPUT_INFO_HISTORY_MODE; + req->history_mode = mode; + ret = !SERVER_CALL_ERR(); + } + SERVER_END_REQ; + return ret; +} + +/****************************************************************** + * WINECON_GetConsoleTitle + * + * + */ +BOOL WINECON_GetConsoleTitle(HANDLE hConIn, WCHAR* buffer, size_t len) +{ + BOOL ret; + DWORD size = 0; + + SERVER_START_VAR_REQ(get_console_input_info, sizeof(buffer)) + { + req->handle = (handle_t)hConIn; + if ((ret = !SERVER_CALL_ERR())) + { + size = min(len - sizeof(WCHAR), server_data_size(req)); + memcpy(buffer, server_data_ptr(req), size); + buffer[size / sizeof(WCHAR)] = 0; + } + } + SERVER_END_VAR_REQ; + return ret; +} + +/****************************************************************** + * WINECON_GrabChanges + * + * A change occurs, try to figure out which + */ +int WINECON_GrabChanges(struct inner_data* data) +{ + struct console_renderer_event evts[16]; + int i, num; + HANDLE h; + + SERVER_START_VAR_REQ( get_console_renderer_events, sizeof(evts) ) + { + req->handle = (handle_t)data->hSynchro; + if (!SERVER_CALL_ERR()) + { + num = server_data_size(req); + memcpy(evts, server_data_ptr(req), num); + num /= sizeof(evts[0]); + } + else num = 0; + } + SERVER_END_VAR_REQ; + if (!num) {Trace(0, "hmm renderer signaled but no events available\n"); return 1;} + + /* FIXME: should do some event compression here (cursor pos, update) */ + Trace(1, "Change notification:"); + for (i = 0; i < num; i++) + { + switch (evts[i].event) + { + case CONSOLE_RENDERER_TITLE_EVENT: + data->fnSetTitle(data); + break; + case CONSOLE_RENDERER_ACTIVE_SB_EVENT: + SERVER_START_REQ( open_console ) + { + req->from = (int)data->hConIn; + req->access = GENERIC_READ | GENERIC_WRITE; + req->share = FILE_SHARE_READ | FILE_SHARE_WRITE; + req->inherit = FALSE; + h = SERVER_CALL_ERR() ? 0 : (HANDLE)req->handle; + } + SERVER_END_REQ; + Trace(1, " active(%d)", (int)h); + if (h) + { + CloseHandle(data->hConOut); + data->hConOut = h; + } + break; + case CONSOLE_RENDERER_SB_RESIZE_EVENT: + if (data->sb_width != evts[i].u.resize.width || + data->sb_height != evts[i].u.resize.height) + { + Trace(1, " resize(%d,%d)", evts[i].u.resize.width, evts[i].u.resize.height); + data->sb_width = evts[i].u.resize.width; + data->sb_height = evts[i].u.resize.height; + + data->cells = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, data->cells, + data->sb_width * data->sb_height * sizeof(CHAR_INFO)); + if (!data->cells) {Trace(0, "OOM\n"); exit(0);} + data->fnResizeScreenBuffer(data); + data->fnComputePositions(data); + } + break; + case CONSOLE_RENDERER_UPDATE_EVENT: + Trace(1, " update(%d,%d)", evts[i].u.update.top, evts[i].u.update.bottom); + WINECON_FetchCells(data, evts[i].u.update.top, evts[i].u.update.bottom); + break; + case CONSOLE_RENDERER_CURSOR_POS_EVENT: + if (evts[i].u.cursor_pos.x != data->cursor.X || evts[i].u.cursor_pos.y != data->cursor.Y) + { + data->cursor.X = evts[i].u.cursor_pos.x; + data->cursor.Y = evts[i].u.cursor_pos.y; + data->fnPosCursor(data); + Trace(1, " curs-pos(%d,%d)",evts[i].u.cursor_pos.x, evts[i].u.cursor_pos.y); + } + break; + case CONSOLE_RENDERER_CURSOR_GEOM_EVENT: + if (evts[i].u.cursor_geom.size != data->cursor_size || + evts[i].u.cursor_geom.visible != data->cursor_visible) + { + data->fnShapeCursor(data, evts[i].u.cursor_geom.size, + evts[i].u.cursor_geom.visible, FALSE); + Trace(1, " curs-geom(%d,%d)", + evts[i].u.cursor_geom.size, evts[i].u.cursor_geom.visible); + } + break; + case CONSOLE_RENDERER_DISPLAY_EVENT: + if (evts[i].u.display.left != data->win_pos.X) + { + data->fnScroll(data, evts[i].u.display.left, TRUE); + data->fnPosCursor(data); + Trace(1, " h-scroll(%d)", evts[i].u.display.left); + } + if (evts[i].u.display.top != data->win_pos.Y) + { + data->fnScroll(data, evts[i].u.display.top, FALSE); + data->fnPosCursor(data); + Trace(1, " v-scroll(%d)", evts[i].u.display.top); + } + if (evts[i].u.display.width != data->win_width || + evts[i].u.display.height != data->win_height) + { + Trace(1, " win-size(%d,%d)", evts[i].u.display.width, evts[i].u.display.height); + data->win_width = evts[i].u.display.width; + data->win_height = evts[i].u.display.height; + data->fnComputePositions(data); + } + break; + case CONSOLE_RENDERER_EXIT_EVENT: + Trace(1, ". Exit!!\n"); + return 0; + default: + Trace(0, "Unknown event type (%d)\n", evts[i].event); + } + } + + Trace(1, ". Done\n"); + return 1; +} + +/****************************************************************** + * WINECON_Delete + * + * Destroy wineconsole internal data + */ +static void WINECON_Delete(struct inner_data* data) +{ + if (!data) return; + + if (data->hConIn) CloseHandle(data->hConIn); + if (data->hConOut) CloseHandle(data->hConOut); + if (data->hSynchro) CloseHandle(data->hSynchro); + if (data->cells) HeapFree(GetProcessHeap(), 0, data->cells); + data->fnDeleteBackend(data); + HeapFree(GetProcessHeap(), 0, data); +} + +/****************************************************************** + * WINECON_Init + * + * Initialisation part I. Creation of server object (console input and + * active screen buffer) + */ +static struct inner_data* WINECON_Init(HINSTANCE hInst, void* pid) +{ + struct inner_data* data = NULL; + DWORD ret; + WCHAR szTitle[] = {'W','i','n','e',' ','c','o','n','s','o','l','e',0}; + size_t len; + + data = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data)); + if (!data) return 0; + + /* the handles here are created without the whistles and bells required by console + * (mainly because wineconsole doesn't need it) + * - there are not inheritable + * - hConIn is not synchronizable + */ + SERVER_START_REQ(alloc_console) + { + req->access = GENERIC_READ | GENERIC_WRITE; + req->inherit = FALSE; + req->pid = pid; + ret = !SERVER_CALL_ERR(); + data->hConIn = (HANDLE)req->handle_in; + data->hSynchro = (HANDLE)req->event; + } + SERVER_END_REQ; + if (!ret) goto error; + + len = lstrlenW(szTitle) * sizeof(WCHAR); + len = min(len, REQUEST_MAX_VAR_SIZE); + + SERVER_START_VAR_REQ(set_console_input_info, len) + { + req->handle = (handle_t)data->hConIn; + req->mask = SET_CONSOLE_INPUT_INFO_TITLE; + memcpy(server_data_ptr(req), szTitle, len); + ret = !SERVER_CALL_ERR(); + } + SERVER_END_VAR_REQ; + + if (ret) + { + SERVER_START_REQ(create_console_output) + { + req->handle_in = (handle_t)data->hConIn; + req->access = GENERIC_WRITE|GENERIC_READ; + req->share = FILE_SHARE_READ|FILE_SHARE_WRITE; + req->inherit = FALSE; + data->hConOut = (HANDLE)(SERVER_CALL_ERR() ? 0 : req->handle_out); + } + SERVER_END_REQ; + if (data->hConOut) return data; + } + + error: + WINECON_Delete(data); + return NULL; +} + +/****************************************************************** + * WINECON_Spawn + * + * Spawn the child processus when invoked with wineconsole foo bar + */ +static BOOL WINECON_Spawn(struct inner_data* data, LPCSTR lpCmdLine) +{ + PROCESS_INFORMATION info; + STARTUPINFO startup; + LPWSTR ptr = GetCommandLine(); /* we're unicode... */ + BOOL done; + + /* we're in the case wineconsole ... spawn the new process */ + memset(&startup, 0, sizeof(startup)); + startup.cb = sizeof(startup); + startup.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES; + startup.wShowWindow = SW_SHOWNORMAL; + + /* the attributes of wineconsole's handles are not adequate for inheritance, so + * get them with the correct attributes before process creation + */ + if (!DuplicateHandle(GetCurrentProcess(), data->hConIn, GetCurrentProcess(), + &startup.hStdInput, GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE, TRUE, 0) || + !DuplicateHandle(GetCurrentProcess(), data->hConOut, GetCurrentProcess(), + &startup.hStdOutput, GENERIC_READ|GENERIC_WRITE, TRUE, 0) || + !DuplicateHandle(GetCurrentProcess(), data->hConOut, GetCurrentProcess(), + &startup.hStdError, GENERIC_READ|GENERIC_WRITE, TRUE, 0)) + { + Trace(0, "can't dup handles\n"); + /* no need to delete handles, we're exiting the programm anyway */ + return FALSE; + } + + /* we could have several ' ' in process command line... so try first space... + * FIXME: + * the correct way would be to check the existence of the left part of ptr + * (to be a file) + */ + while (*ptr && *ptr++ != ' '); + + done = *ptr && CreateProcess(NULL, ptr, NULL, NULL, TRUE, 0L, NULL, NULL, &startup, &info); + + /* we no longer need the handles passed to the child for the console */ + CloseHandle(startup.hStdInput); + CloseHandle(startup.hStdOutput); + CloseHandle(startup.hStdError); + + return done; +} + +/****************************************************************** + * WINECON_WinMain + * + * wineconsole can either be started as: + * wineconsole used when a new console is created (AllocConsole) + * wineconsole used to start the program from the command line in + * a freshly created console + */ +int PASCAL WINECON_WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPCSTR lpCmdLine, UINT nCmdShow) +{ + struct inner_data* data; + int ret = 1; + unsigned evt; + + /* case of wineconsole , signal process that created us that we're up and running */ + if (sscanf(lpCmdLine, "%d", &evt) == 1) + { + if (!(data = WINECON_Init(hInst, 0))) return 0; + ret = SetEvent((HANDLE)evt); + } + else + { + if (!(data = WINECON_Init(hInst, (void*)GetCurrentProcessId()))) return 0; + ret = WINECON_Spawn(data, lpCmdLine); + } + + if (ret && WCUSER_InitBackend(data)) + { + ret = data->fnMainLoop(data); + } + WINECON_Delete(data); + + return ret; +} diff --git a/programs/wineconsole/wineconsole.spec b/programs/wineconsole/wineconsole.spec new file mode 100644 index 00000000000..a5c6544203b --- /dev/null +++ b/programs/wineconsole/wineconsole.spec @@ -0,0 +1,12 @@ +name wineconsole +mode guiexe +type win32 +init WINECON_WinMain +rsrc wineconsole_res.res + +import -delay comctl32 +import gdi32.dll +import user32.dll +#import advapi32.dll +import kernel32.dll +import ntdll.dll diff --git a/programs/wineconsole/wineconsole_En.rc b/programs/wineconsole/wineconsole_En.rc new file mode 100644 index 00000000000..50b6f6dbd7e --- /dev/null +++ b/programs/wineconsole/wineconsole_En.rc @@ -0,0 +1,70 @@ +LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT + +STRINGTABLE LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL +BEGIN +IDS_EDIT, "&Edit" +IDS_DEFAULT, "&Default" +IDS_PROPERTY, "&Property" +IDS_MARK, "&Mark" +IDS_COPY, "&Copy" +IDS_PASTE, "&Paste" +IDS_SELECTALL, "&Select all" +IDS_SCROLL, "Sc&roll" +IDS_SEARCH, "S&earch" +IDS_FNT_DISPLAY, "Each character is %ld pixels wide on %ld pixels high" +IDS_FNT_PREVIEW_1, "This is a test" +IDS_FNT_PREVIEW_2, "" +END + +IDD_OPTION DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 140, 105 LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION " Options " +FONT 8, "Helv" +{ + GROUPBOX "Cursor size", -1, 10, 11, 120, 44, BS_GROUPBOX + AUTORADIOBUTTON "&Small", IDC_OPT_CURSOR_SMALL, 14, 23, 84, 10, WS_TABSTOP + AUTORADIOBUTTON "&Medium", IDC_OPT_CURSOR_MEDIUM, 14, 33, 84, 10, WS_TABSTOP + AUTORADIOBUTTON "&Large", IDC_OPT_CURSOR_LARGE, 14, 43, 84, 10, WS_TABSTOP + + GROUPBOX "Command history", -1, 10, 57, 180, 35, BS_GROUPBOX + LTEXT "&Numbers of recalled commands :", -1, 14, 67, 78, 18 + EDITTEXT IDC_OPT_HIST_SIZE, 92, 69, 31, 12, WS_TABSTOP|WS_BORDER|ES_NUMBER + CONTROL "", IDC_OPT_HIST_SIZE_UD, "msctls_updown32", UDS_SETBUDDYINT|UDS_ALIGNRIGHT|UDS_AUTOBUDDY|UDS_ARROWKEYS|UDS_NOTHOUSANDS, 0, 0, 0, 0 + AUTOCHECKBOX "&Remove doubles", IDC_OPT_HIST_DOUBLE, 130, 67, 50, 18, WS_TABSTOP|BS_MULTILINE +} + +IDD_FONT DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 140, 105 LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION " Font " +FONT 8, "Helv" +{ + LTEXT "&Font", -1, 5, 5, 24, 8 + LISTBOX IDC_FNT_LIST_FONT, 5,18,109,42, LBS_SORT|WS_VSCROLL + LTEXT "&Size", -1, 128, 5, 60, 8 + LISTBOX IDC_FNT_LIST_SIZE, 128, 18, 50, 60, WS_VSCROLL + CONTROL "", IDC_FNT_PREVIEW, "WineConFontPreview", 0L, 5,60,109,40 + LTEXT "", IDC_FNT_FONT_INFO, 128, 76, 80, 18 +} + +IDD_CONFIG DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 140, 105 LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION " Configuration " +FONT 8, "Helv" +{ + GROUPBOX "Buffer zone", -1, 10, 11, 110, 42, BS_GROUPBOX + LTEXT "&Width :", -1, 14, 25, 54, 9 + EDITTEXT IDC_CNF_SB_WIDTH, 78, 23, 36, 12, WS_TABSTOP|WS_BORDER|ES_NUMBER + CONTROL "", IDC_CNF_SB_WIDTH_UD, "msctls_updown32", UDS_SETBUDDYINT|UDS_ALIGNRIGHT|UDS_AUTOBUDDY|UDS_ARROWKEYS|UDS_NOTHOUSANDS, 0, 0, 0, 0 + LTEXT "&Height :", -1, 14, 39, 54, 9 + EDITTEXT IDC_CNF_SB_HEIGHT, 78, 37, 36, 12, WS_TABSTOP|WS_BORDER|ES_NUMBER + CONTROL "", IDC_CNF_SB_HEIGHT_UD, "msctls_updown32", UDS_SETBUDDYINT|UDS_ALIGNRIGHT|UDS_AUTOBUDDY|UDS_ARROWKEYS|UDS_NOTHOUSANDS, 0, 0, 0, 0 + + GROUPBOX "Window size", -1, 10, 55, 110, 42 + LTEXT "W&idth :", -1, 14, 69, 54, 9 + EDITTEXT IDC_CNF_WIN_WIDTH, 78, 67, 36, 12, WS_TABSTOP|WS_BORDER|ES_NUMBER + CONTROL "", IDC_CNF_WIN_WIDTH_UD, "msctls_updown32", UDS_SETBUDDYINT|UDS_ALIGNRIGHT|UDS_AUTOBUDDY|UDS_ARROWKEYS|UDS_NOTHOUSANDS, 0, 0, 0, 0 + LTEXT "H&eight :", -1, 14, 83, 54, 9 + EDITTEXT IDC_CNF_WIN_HEIGHT, 78, 81, 36, 12, WS_TABSTOP|WS_BORDER|ES_NUMBER + CONTROL "", IDC_CNF_WIN_HEIGHT_UD, "msctls_updown32", UDS_SETBUDDYINT|UDS_ALIGNRIGHT|UDS_AUTOBUDDY|UDS_ARROWKEYS|UDS_NOTHOUSANDS, 0, 0, 0, 0 +} + diff --git a/programs/wineconsole/wineconsole_Fr.rc b/programs/wineconsole/wineconsole_Fr.rc new file mode 100644 index 00000000000..d5a78417acd --- /dev/null +++ b/programs/wineconsole/wineconsole_Fr.rc @@ -0,0 +1,72 @@ +LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT + +STRINGTABLE LANGUAGE LANG_FRENCH, SUBLANG_NEUTRAL +BEGIN +IDS_EDIT, "&Editer" +IDS_DEFAULT, "Par &défaut" +IDS_PROPERTY, "&Propriétés" +IDS_MARK, "&Marquer" +IDS_COPY, "&Copier" +IDS_PASTE, "C&oller" +IDS_SELECTALL, "&Sélectionner tout" +IDS_SCROLL, "&Défiler" +IDS_SEARCH, "C&hercher" +IDS_FNT_DISPLAY, "Chaque caractère a %ld points en largeur et %ld points en hauteur" +IDS_FNT_PREVIEW_1, "Ceci est un test" +IDS_FNT_PREVIEW_2, "éèàôë" +END + +IDD_OPTION DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 140, 105 LANGUAGE LANG_FRENCH, SUBLANG_NEUTRAL +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION " Options " +FONT 8, "Helv" +{ + GROUPBOX "Taille du curseur", -1, 10, 11, 120, 44, BS_GROUPBOX + AUTORADIOBUTTON "&Petit", IDC_OPT_CURSOR_SMALL, 14, 23, 84, 10, WS_TABSTOP + AUTORADIOBUTTON "&Moyen", IDC_OPT_CURSOR_MEDIUM, 14, 33, 84, 10, WS_TABSTOP + AUTORADIOBUTTON "&Grand", IDC_OPT_CURSOR_LARGE, 14, 43, 84, 10, WS_TABSTOP + + GROUPBOX "Historique des commandes", -1, 10, 57, 180, 35, BS_GROUPBOX + LTEXT "&Taille de la mémoire tampon :", -1, 14, 67, 78, 18 + EDITTEXT IDC_OPT_HIST_SIZE, 92, 69, 31, 12, WS_TABSTOP|WS_BORDER|ES_NUMBER + CONTROL "", IDC_OPT_HIST_SIZE_UD, "msctls_updown32", UDS_SETBUDDYINT|UDS_ALIGNRIGHT|UDS_AUTOBUDDY|UDS_ARROWKEYS|UDS_NOTHOUSANDS, 0, 0, 0, 0 + AUTOCHECKBOX "&Supprimer les doublons", IDC_OPT_HIST_DOUBLE, 130, 67, 50, 18, WS_TABSTOP|BS_MULTILINE +} + +IDD_FONT DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 140, 105 LANGUAGE LANG_FRENCH, SUBLANG_NEUTRAL +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION " Police " +FONT 8, "Helv" +{ + LTEXT "&Police", -1, 5, 5, 24, 8 + LISTBOX IDC_FNT_LIST_FONT, 5, 18, 109, 42, LBS_SORT|WS_VSCROLL + LTEXT "&Taille", -1, 128, 5, 60, 8 + LISTBOX IDC_FNT_LIST_SIZE, 128, 18, 50, 60, WS_VSCROLL + CONTROL "", IDC_FNT_PREVIEW, "WineConFontPreview", 0L, 5, 60, 109, 40 + LTEXT "", IDC_FNT_FONT_INFO, 128, 76, 80, 18 +} + +IDD_CONFIG DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 140, 105 LANGUAGE LANG_FRENCH, SUBLANG_NEUTRAL +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION " Configuration " +FONT 8, "Helv" +{ + GROUPBOX "Taille mémoire tampon écran", -1, 10, 11, 110, 42, BS_GROUPBOX + LTEXT "&Largeur :", -1, 14, 25, 54, 9 + EDITTEXT IDC_CNF_SB_WIDTH, 78, 23, 36, 12, WS_TABSTOP|WS_BORDER|ES_NUMBER + CONTROL "", IDC_CNF_SB_WIDTH_UD, "msctls_updown32", UDS_SETBUDDYINT|UDS_ALIGNRIGHT|UDS_AUTOBUDDY|UDS_ARROWKEYS|UDS_NOTHOUSANDS, 0, 0, 0, 0 + LTEXT "Ha&uteur :", -1, 14, 39, 54, 9 + EDITTEXT IDC_CNF_SB_HEIGHT, 78, 37, 36, 12, WS_TABSTOP|WS_BORDER|ES_NUMBER + CONTROL "", IDC_CNF_SB_HEIGHT_UD, "msctls_updown32", UDS_SETBUDDYINT|UDS_ALIGNRIGHT|UDS_AUTOBUDDY|UDS_ARROWKEYS|UDS_NOTHOUSANDS, 0, 0, 0, 0 + + GROUPBOX "Taille de la fenêtre", -1, 10, 55, 110, 42 + LTEXT "La&rgeur :", -1, 14, 69, 54, 9 + EDITTEXT IDC_CNF_WIN_WIDTH, 78, 67, 36, 12, WS_TABSTOP|WS_BORDER|ES_NUMBER + CONTROL "", IDC_CNF_WIN_WIDTH_UD, "msctls_updown32", UDS_SETBUDDYINT|UDS_ALIGNRIGHT|UDS_AUTOBUDDY|UDS_ARROWKEYS|UDS_NOTHOUSANDS, 0, 0, 0, 0 + LTEXT "Hau&teur :", -1, 14, 83, 54, 9 + EDITTEXT IDC_CNF_WIN_HEIGHT, 78, 81, 36, 12, WS_TABSTOP|WS_BORDER|ES_NUMBER + CONTROL "", IDC_CNF_WIN_HEIGHT_UD, "msctls_updown32", UDS_SETBUDDYINT|UDS_ALIGNRIGHT|UDS_AUTOBUDDY|UDS_ARROWKEYS|UDS_NOTHOUSANDS, 0, 0, 0, 0 +} + + + diff --git a/programs/wineconsole/wineconsole_res.h b/programs/wineconsole/wineconsole_res.h new file mode 100644 index 00000000000..17c8cbaee86 --- /dev/null +++ b/programs/wineconsole/wineconsole_res.h @@ -0,0 +1,44 @@ +/* wineconsole resource definitions */ + +/* strings */ +#define IDS_EDIT 0x100 +#define IDS_DEFAULT 0x101 +#define IDS_PROPERTY 0x102 + +#define IDS_MARK 0x110 +#define IDS_COPY 0x111 +#define IDS_PASTE 0x112 +#define IDS_SELECTALL 0x113 +#define IDS_SCROLL 0x114 +#define IDS_SEARCH 0x115 + +#define IDS_FNT_DISPLAY 0x200 +#define IDS_FNT_PREVIEW_1 0x201 +#define IDS_FNT_PREVIEW_2 0x202 + +#define IDD_OPTION 0x0100 +#define IDD_FONT 0x0200 +#define IDD_CONFIG 0x0300 + +/* dialog boxes */ +#define IDC_OPT_CURSOR_SMALL 0x0101 +#define IDC_OPT_CURSOR_MEDIUM 0x0102 +#define IDC_OPT_CURSOR_LARGE 0x0103 +#define IDC_OPT_HIST_SIZE 0x0104 +#define IDC_OPT_HIST_SIZE_UD 0x0105 +#define IDC_OPT_HIST_DOUBLE 0x0106 + +#define IDC_FNT_LIST_FONT 0x0201 +#define IDC_FNT_LIST_SIZE 0x0202 +#define IDC_FNT_FONT_INFO 0x0203 +#define IDC_FNT_PREVIEW 0x0204 + +#define IDC_CNF_SB_WIDTH 0x0301 +#define IDC_CNF_SB_WIDTH_UD 0x0302 +#define IDC_CNF_SB_HEIGHT 0x0303 +#define IDC_CNF_SB_HEIGHT_UD 0x0304 +#define IDC_CNF_WIN_WIDTH 0x0305 +#define IDC_CNF_WIN_WIDTH_UD 0x0306 +#define IDC_CNF_WIN_HEIGHT 0x0307 +#define IDC_CNF_WIN_HEIGHT_UD 0x0308 + diff --git a/programs/wineconsole/wineconsole_res.rc b/programs/wineconsole/wineconsole_res.rc new file mode 100644 index 00000000000..6ebc40c8add --- /dev/null +++ b/programs/wineconsole/wineconsole_res.rc @@ -0,0 +1,8 @@ +#include "windef.h" +#include "winuser.h" +#include "winnls.h" +#include "commctrl.h" +#include "wineconsole_res.h" + +#include "wineconsole_En.rc" +#include "wineconsole_Fr.rc" diff --git a/scheduler/process.c b/scheduler/process.c index ab5db91ef72..755dd396cda 100644 --- a/scheduler/process.c +++ b/scheduler/process.c @@ -20,6 +20,7 @@ #include "file.h" #include "thread.h" #include "winerror.h" +#include "wincon.h" #include "wine/server.h" #include "options.h" #include "callback.h" @@ -216,29 +217,6 @@ void PROCESS_CallUserSignalProc( UINT uCode, HMODULE16 hModule ) Callout.UserSignalProc( uCode, GetCurrentProcessId(), dwFlags, hModule ); } - -/*********************************************************************** - * set_console_handles - * - * Set the console handles to use stdin/stdout. - */ -static void set_console_handles( HANDLE console ) -{ - wine_server_send_fd( 0 ); - wine_server_send_fd( 1 ); - - SERVER_START_REQ( set_console_fd ) - { - req->handle = console; - req->fd_in = 0; - req->fd_out = 1; - req->pid = 0; - SERVER_CALL(); - } - SERVER_END_REQ; -} - - /*********************************************************************** * process_init * @@ -287,15 +265,28 @@ static BOOL process_init( char *argv[] ) SERVER_END_VAR_REQ; if (!ret) return FALSE; - SetStdHandle( STD_INPUT_HANDLE, current_startupinfo.hStdInput ); - SetStdHandle( STD_OUTPUT_HANDLE, current_startupinfo.hStdOutput ); - SetStdHandle( STD_ERROR_HANDLE, current_startupinfo.hStdError ); - if (create_flags & CREATE_NEW_CONSOLE) - set_console_handles( current_startupinfo.hStdOutput ); - /* Create the process heap */ current_process.heap = HeapCreate( HEAP_GROWABLE, 0, 0 ); + if (create_flags == 0 && + current_startupinfo.hStdInput == 0 && + current_startupinfo.hStdOutput == 0 && + current_startupinfo.hStdError == 0) + { + /* no parent, and no new console requested, create a simple console with bare handles to + * unix stdio input & output streams (aka simple console) + */ + SetStdHandle( STD_INPUT_HANDLE, FILE_DupUnixHandle( 0, GENERIC_READ, TRUE )); + SetStdHandle( STD_OUTPUT_HANDLE, FILE_DupUnixHandle( 1, GENERIC_WRITE, TRUE )); + SetStdHandle( STD_ERROR_HANDLE, FILE_DupUnixHandle( 1, GENERIC_WRITE, TRUE )); + } + else if (!(create_flags & (DETACHED_PROCESS|CREATE_NEW_CONSOLE))) + { + SetStdHandle( STD_INPUT_HANDLE, current_startupinfo.hStdInput ); + SetStdHandle( STD_OUTPUT_HANDLE, current_startupinfo.hStdOutput ); + SetStdHandle( STD_ERROR_HANDLE, current_startupinfo.hStdError ); + } + /* Now we can use the pthreads routines */ PTHREAD_init_done(); @@ -305,7 +296,11 @@ static BOOL process_init( char *argv[] ) /* Parse command line arguments */ OPTIONS_ParseOptions( argv ); - return MAIN_MainInit(); + ret = MAIN_MainInit(); + + if (create_flags & CREATE_NEW_CONSOLE) + AllocConsole(); + return ret; } diff --git a/server/console.c b/server/console.c index 6aab56367ac..bd6dd39e869 100644 --- a/server/console.c +++ b/server/console.c @@ -2,22 +2,15 @@ * Server-side console management * * Copyright (C) 1998 Alexandre Julliard + * 2001 Eric Pouech * - * FIXME: all this stuff is a hack to avoid breaking - * the client-side console support. */ #include "config.h" #include -#include -#include #include #include -#include -#include -#include -#include #include #include "winnt.h" @@ -26,18 +19,60 @@ #include "handle.h" #include "process.h" -#include "thread.h" #include "request.h" +#include "unicode.h" +#include "console.h" -struct screen_buffer; -struct console_input +static void console_input_dump( struct object *obj, int verbose ); +static void console_input_destroy( struct object *obj ); +static int console_input_signaled( struct object *obj, struct thread *thread ); + +/* common routine */ +static int console_get_file_info( struct object *obj, struct get_file_info_request *req ); + +static const struct object_ops console_input_ops = { - struct object obj; /* object header */ - int mode; /* input mode */ - struct screen_buffer *output; /* associated screen buffer */ - int recnum; /* number of input records */ - INPUT_RECORD *records; /* input records */ + sizeof(struct console_input), /* size */ + console_input_dump, /* dump */ + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + console_input_signaled, /* signaled */ + no_satisfied, /* satisfied */ + NULL, /* get_poll_events */ + NULL, /* poll_event */ + no_get_fd, /* get_fd */ + no_flush, /* flush */ + console_get_file_info, /* get_file_info */ + console_input_destroy /* destroy */ +}; + +static void console_input_events_dump( struct object *obj, int verbose ); +static void console_input_events_destroy( struct object *obj ); +static int console_input_events_signaled( struct object *obj, struct thread *thread ); + +struct console_input_events +{ + struct object obj; /* object header */ + int num_alloc; /* number of allocated events */ + int num_used; /* number of actually used events */ + struct console_renderer_event* events; +}; + +static const struct object_ops console_input_events_ops = +{ + sizeof(struct console_input_events), /* size */ + console_input_events_dump, /* dump */ + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + console_input_events_signaled, /* signaled */ + no_satisfied, /* satisfied */ + NULL, /* get_poll_events */ + NULL, /* poll_event */ + no_get_fd, /* get_fd */ + no_flush, /* flush */ + no_get_file_info, /* get_file_info */ + console_input_events_destroy /* destroy */ }; struct screen_buffer @@ -45,161 +80,292 @@ struct screen_buffer struct object obj; /* object header */ int mode; /* output mode */ struct console_input *input; /* associated console input */ - int cursor_size; /* size of cursor (percentage filled) */ - int cursor_visible;/* cursor visibility flag */ - int pid; /* xterm pid (hack) */ - char *title; /* console title */ + struct screen_buffer *next; /* linked list of all screen buffers */ + short int cursor_size; /* size of cursor (percentage filled) */ + short int cursor_visible;/* cursor visibility flag */ + COORD cursor; /* position of cursor */ + short int width; /* size (w-h) of the screen buffer */ + short int height; + short int max_width; /* size (w-h) of the window given font size */ + short int max_height; + unsigned *data; /* the data for each cell - a width x height matrix */ + unsigned short attr; /* default attribute for screen buffer */ + SMALL_RECT win; /* current visible window on the screen buffer * + * as seen in wineconsole */ }; - -static void console_input_dump( struct object *obj, int verbose ); -static int console_input_get_poll_events( struct object *obj ); -static int console_input_get_fd( struct object *obj ); -static void console_input_destroy( struct object *obj ); - static void screen_buffer_dump( struct object *obj, int verbose ); -static int screen_buffer_get_poll_events( struct object *obj ); -static int screen_buffer_get_fd( struct object *obj ); static void screen_buffer_destroy( struct object *obj ); -/* common routine */ -static int console_get_info( struct object *obj, struct get_file_info_request *req ); - -static const struct object_ops console_input_ops = -{ - sizeof(struct console_input), /* size */ - console_input_dump, /* dump */ - default_poll_add_queue, /* add_queue */ - default_poll_remove_queue, /* remove_queue */ - default_poll_signaled, /* signaled */ - no_satisfied, /* satisfied */ - console_input_get_poll_events, /* get_poll_events */ - default_poll_event, /* poll_event */ - console_input_get_fd, /* get_fd */ - no_flush, /* flush */ - console_get_info, /* get_file_info */ - console_input_destroy /* destroy */ -}; - static const struct object_ops screen_buffer_ops = { sizeof(struct screen_buffer), /* size */ screen_buffer_dump, /* dump */ - default_poll_add_queue, /* add_queue */ - default_poll_remove_queue, /* remove_queue */ - default_poll_signaled, /* signaled */ - no_satisfied, /* satisfied */ - screen_buffer_get_poll_events, /* get_poll_events */ - default_poll_event, /* poll_event */ - screen_buffer_get_fd, /* get_fd */ + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* satisfied */ + NULL, /* get_poll_events */ + NULL, /* poll_event */ + no_get_fd, /* get_fd */ no_flush, /* flush */ - console_get_info, /* get_file_info */ + console_get_file_info, /* get_file_info */ screen_buffer_destroy /* destroy */ }; +static struct screen_buffer *screen_buffer_list; -static struct object *create_console_input( int fd ) +/* dumps the renderer events of a console */ +static void console_input_events_dump( struct object *obj, int verbose ) +{ + struct console_input_events *evts = (struct console_input_events *)obj; + assert( obj->ops == &console_input_events_ops ); + fprintf( stderr, "Console input events: %d/%d events\n", + evts->num_used, evts->num_alloc ); +} + +/* destroys the renderer events of a console */ +static void console_input_events_destroy( struct object *obj ) +{ + struct console_input_events *evts = (struct console_input_events *)obj; + assert( obj->ops == &console_input_events_ops ); + free( evts->events ); +} + +/* the rendere events list is signaled when it's not empty */ +static int console_input_events_signaled( struct object *obj, struct thread *thread ) +{ + struct console_input_events *evts = (struct console_input_events *)obj; + assert( obj->ops == &console_input_events_ops ); + return evts->num_used ? 1 : 0; +} + +/* add an event to the console's renderer events list */ +static void console_input_events_append( struct console_input_events* evts, + struct console_renderer_event* evt) +{ + if (!evt) return; + + /* to be done even when the renderer generates the events ? */ + if (evts->num_used == evts->num_alloc) + { + evts->num_alloc += 16; + evts->events = realloc( evts->events, evts->num_alloc * sizeof(*evt) ); + assert(evts->events); + } + evts->events[evts->num_used++] = *evt; + wake_up( &evts->obj, 0 ); +} + +/* retrieves events from the console's renderer events list */ +static size_t console_input_events_get( struct console_input_events* evts, + struct console_renderer_event* evt, size_t num ) +{ + if (num % sizeof(*evt) != 0) + { + set_error( STATUS_INVALID_PARAMETER ); + return 0; + } + num /= sizeof(*evt); + if (num > evts->num_used) + num = evts->num_used; + memcpy( evt, evts->events, num * sizeof(*evt) ); + if (num < evts->num_used) + { + memmove( &evts->events[0], &evts->events[num], + (evts->num_used - num) * sizeof(*evt) ); + } + evts->num_used -= num; + return num * sizeof(struct console_renderer_event); +} + +static struct console_input_events *create_console_input_events(void) +{ + struct console_input_events* evt; + + if (!(evt = alloc_object( &console_input_events_ops, -1 ))) return NULL; + evt->num_alloc = evt->num_used = 0; + evt->events = NULL; + return evt; +} + +static struct object *create_console_input( struct process* renderer ) { struct console_input *console_input; - if ((fd = (fd != -1) ? dup(fd) : dup(0)) == -1) + if (!(console_input = alloc_object( &console_input_ops, -1 ))) return NULL; + console_input->renderer = renderer; + console_input->mode = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | + ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT; + console_input->num_proc = 0; + console_input->active = NULL; + console_input->recnum = 0; + console_input->records = NULL; + console_input->evt = create_console_input_events(); + console_input->title = NULL; + console_input->history_size = 50; + console_input->history = calloc( console_input->history_size, sizeof(WCHAR*) ); + console_input->history_index = 0; + console_input->history_mode = 0; + + if (!console_input->history || !console_input->evt) { - file_set_error(); - return NULL; + release_object( console_input ); + return NULL; } - if (!(console_input = alloc_object( &console_input_ops, fd ))) return NULL; - console_input->mode = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | - ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT; - console_input->output = NULL; - console_input->recnum = 0; - console_input->records = NULL; return &console_input->obj; } -static struct object *create_console_output( int fd, struct object *input ) +static struct object *create_console_output( struct console_input *console_input ) { - struct console_input *console_input = (struct console_input *)input; struct screen_buffer *screen_buffer; + struct console_renderer_event evt; + int i; - if ((fd = (fd != -1) ? dup(fd) : dup(1)) == -1) - { - file_set_error(); - return NULL; - } - if (!(screen_buffer = alloc_object( &screen_buffer_ops, fd ))) return NULL; + if (!(screen_buffer = alloc_object( &screen_buffer_ops, -1 ))) return NULL; screen_buffer->mode = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT; screen_buffer->input = console_input; screen_buffer->cursor_size = 100; screen_buffer->cursor_visible = 1; - screen_buffer->pid = 0; - screen_buffer->title = strdup( "Wine console" ); - console_input->output = screen_buffer; - return &screen_buffer->obj; -} + screen_buffer->width = 80; + screen_buffer->height = 150; + screen_buffer->max_width = 80; + screen_buffer->max_height = 25; + screen_buffer->data = malloc( 4 * screen_buffer->width * screen_buffer->height ); + /* fill the buffer with white on black spaces */ + for (i = 0; i < screen_buffer->width * screen_buffer->height; i++) + { + screen_buffer->data[i] = 0x00F00020; + } + screen_buffer->cursor.X = 0; + screen_buffer->cursor.Y = 0; + screen_buffer->attr = 0xF0; + screen_buffer->win.Left = 0; + screen_buffer->win.Right = screen_buffer->max_width - 1; + screen_buffer->win.Top = 0; + screen_buffer->win.Bottom = screen_buffer->max_height - 1; -/* allocate a console for this process */ -int alloc_console( struct process *process ) -{ - if (process->console_in || process->console_out) + screen_buffer->next = screen_buffer_list; + screen_buffer_list = screen_buffer; + + if (!console_input->active) { - set_error( STATUS_ACCESS_DENIED ); - return 0; + console_input->active = (struct screen_buffer*)grab_object( screen_buffer ); + + /* generate the fist events */ + evt.event = CONSOLE_RENDERER_ACTIVE_SB_EVENT; + console_input_events_append( console_input->evt, &evt ); + + evt.event = CONSOLE_RENDERER_SB_RESIZE_EVENT; + evt.u.resize.width = screen_buffer->width; + evt.u.resize.height = screen_buffer->height; + console_input_events_append( console_input->evt, &evt ); + + evt.event = CONSOLE_RENDERER_DISPLAY_EVENT; + evt.u.display.left = screen_buffer->win.Left; + evt.u.display.top = screen_buffer->win.Top; + evt.u.display.width = screen_buffer->win.Right - screen_buffer->win.Left + 1; + evt.u.display.height = screen_buffer->win.Bottom - screen_buffer->win.Top + 1; + console_input_events_append( console_input->evt, &evt ); + + evt.event = CONSOLE_RENDERER_UPDATE_EVENT; + evt.u.update.top = 0; + evt.u.update.bottom = screen_buffer->height - 1; + console_input_events_append( console_input->evt, &evt ); + + evt.event = CONSOLE_RENDERER_CURSOR_GEOM_EVENT; + evt.u.cursor_geom.size = screen_buffer->cursor_size; + evt.u.cursor_geom.visible = screen_buffer->cursor_visible; + console_input_events_append( console_input->evt, &evt ); + + evt.event = CONSOLE_RENDERER_CURSOR_POS_EVENT; + evt.u.cursor_pos.x = screen_buffer->cursor.X; + evt.u.cursor_pos.y = screen_buffer->cursor.Y; + console_input_events_append( console_input->evt, &evt ); } - if ((process->console_in = create_console_input( -1 ))) - { - if ((process->console_out = create_console_output( -1, process->console_in ))) - return 1; - release_object( process->console_in ); - } - return 0; + + return &screen_buffer->obj; } /* free the console for this process */ int free_console( struct process *process ) { - if (process->console_in) release_object( process->console_in ); - if (process->console_out) release_object( process->console_out ); - process->console_in = process->console_out = NULL; + struct console_input* console = process->console; + + if (!console || !console->renderer) return 0; + + process->console = NULL; + if (--console->num_proc == 0) + { + /* all processes have terminated... tell the renderer to terminate too */ + struct console_renderer_event evt; + evt.event = CONSOLE_RENDERER_EXIT_EVENT; + console_input_events_append( console->evt, &evt ); + } + release_object( console ); + return 1; } -static int set_console_fd( handle_t handle, int fd_in, int fd_out, int pid ) +/* let process inherit the console from parent... this handle two cases : + * 1/ generic console inheritance + * 2/ parent is a renderer which launches process, and process should attach to the console + * renderered by parent + */ +void inherit_console(struct process *parent, struct process *process, handle_t hconin) { - struct console_input *input; - struct screen_buffer *output; - struct object *obj; + int done = 0; - if (!(obj = get_handle_obj( current->process, handle, 0, NULL ))) - return 0; - if (obj->ops == &console_input_ops) + /* if parent is a renderer, then attach current process to its console + * a bit hacky.... + */ + if (hconin) { - input = (struct console_input *)obj; - output = input->output; - grab_object( output ); + struct console_input* console; + + if ((console = (struct console_input*)get_handle_obj( parent, hconin, 0, NULL ))) + { + if (console->renderer == parent) + { + process->console = (struct console_input*)grab_object( console ); + process->console->num_proc++; + done = 1; + } + release_object( console ); + } } - else if (obj->ops == &screen_buffer_ops) + /* otherwise, if parent has a console, attach child to this console */ + if (!done && parent->console) { - output = (struct screen_buffer *)obj; - input = output->input; - grab_object( input ); + assert(parent->console->renderer); + process->console = (struct console_input*)grab_object( parent->console ); + process->console->num_proc++; } - else +} + +static struct console_input* console_input_get( handle_t handle, unsigned access ) +{ + struct console_input* console = 0; + + if (handle) + console = (struct console_input *)get_handle_obj( current->process, handle, + access, &console_input_ops ); + else if (current->process->console) { - set_error( STATUS_OBJECT_TYPE_MISMATCH ); - release_object( obj ); - return 0; + assert( current->process->console->renderer ); + console = (struct console_input *)grab_object( current->process->console ); } - /* can't change the fd if someone is waiting on it */ - assert( !input->obj.head ); - assert( !output->obj.head ); + if (!console && !get_error()) set_error(STATUS_INVALID_PARAMETER); + return console; +} - change_select_fd( &input->obj, fd_in ); - change_select_fd( &output->obj, fd_out ); - output->pid = pid; - release_object( input ); - release_object( output ); - return 1; +/* check if a console input is signaled: yes if non read input records */ +static int console_input_signaled( struct object *obj, struct thread *thread ) +{ + struct console_input *console = (struct console_input *)obj; + assert( obj->ops == &console_input_ops ); + return console->recnum ? 1 : 0; } static int get_console_mode( handle_t handle ) @@ -220,15 +386,17 @@ static int get_console_mode( handle_t handle ) return ret; } +/* changes the mode of either a console input or a screen buffer */ static int set_console_mode( handle_t handle, int mode ) { struct object *obj; int ret = 0; - if (!(obj = get_handle_obj( current->process, handle, GENERIC_READ, NULL ))) + if (!(obj = get_handle_obj( current->process, handle, GENERIC_WRITE, NULL ))) return 0; if (obj->ops == &console_input_ops) { + /* FIXME: if we remove the edit mode bits, we need (???) to clean up the history */ ((struct console_input *)obj)->mode = mode; ret = 1; } @@ -242,43 +410,12 @@ static int set_console_mode( handle_t handle, int mode ) return ret; } -/* set misc console information (output handle only) */ -static int set_console_info( handle_t handle, struct set_console_info_request *req, - const char *title, size_t len ) -{ - struct screen_buffer *console; - if (!(console = (struct screen_buffer *)get_handle_obj( current->process, handle, - GENERIC_WRITE, &screen_buffer_ops ))) - return 0; - if (req->mask & SET_CONSOLE_INFO_CURSOR) - { - console->cursor_size = req->cursor_size; - console->cursor_visible = req->cursor_visible; - } - if (req->mask & SET_CONSOLE_INFO_TITLE) - { - char *new_title = mem_alloc( len + 1 ); - if (new_title) - { - memcpy( new_title, title, len ); - new_title[len] = 0; - if (console->title) free( console->title ); - console->title = new_title; - } - } - release_object( console ); - return 1; -} - /* add input events to a console input queue */ -static int write_console_input( handle_t handle, int count, INPUT_RECORD *records ) +static int write_console_input( struct console_input* console, int count, INPUT_RECORD *records ) { INPUT_RECORD *new_rec; - struct console_input *console; - if (!(console = (struct console_input *)get_handle_obj( current->process, handle, - GENERIC_WRITE, &console_input_ops ))) - return -1; + assert(count); if (!(new_rec = realloc( console->records, (console->recnum + count) * sizeof(INPUT_RECORD) ))) { @@ -289,7 +426,9 @@ static int write_console_input( handle_t handle, int count, INPUT_RECORD *record console->records = new_rec; memcpy( new_rec + console->recnum, records, count * sizeof(INPUT_RECORD) ); console->recnum += count; - release_object( console ); + + /* wake up all waiters */ + wake_up( &console->obj, 0 ); return count; } @@ -317,7 +456,7 @@ static int read_console_input( handle_t handle, int count, INPUT_RECORD *rec, in { int i; for (i = count; i < console->recnum; i++) - console->records[i-count] = console->records[i]; + ((INPUT_RECORD*)console->records)[i-count] = ((INPUT_RECORD*)console->records)[i]; if ((console->recnum -= count) > 0) { INPUT_RECORD *new_rec = realloc( console->records, @@ -334,26 +473,283 @@ static int read_console_input( handle_t handle, int count, INPUT_RECORD *rec, in return count; } +/* set misc console input information */ +static int set_console_input_info( struct set_console_input_info_request *req, + const WCHAR *title, size_t len ) +{ + struct console_input *console; + struct console_renderer_event evt; + + if (!(console = console_input_get( req->handle, GENERIC_WRITE ))) goto error; + + if (req->mask & SET_CONSOLE_INPUT_INFO_ACTIVE_SB) + { + struct screen_buffer *screen_buffer; + + screen_buffer = (struct screen_buffer *)get_handle_obj( current->process, req->active_sb, + GENERIC_READ, &screen_buffer_ops ); + if (!screen_buffer || screen_buffer->input != console) + { + set_error( STATUS_INVALID_PARAMETER ); + if (screen_buffer) release_object( screen_buffer ); + goto error; + } + + if (screen_buffer != console->active) + { + if (console->active) release_object( console->active ); + console->active = screen_buffer; + evt.event = CONSOLE_RENDERER_ACTIVE_SB_EVENT; + console_input_events_append( console->evt, &evt ); + } + else + release_object( screen_buffer ); + } + if (req->mask & SET_CONSOLE_INPUT_INFO_TITLE) + { + WCHAR *new_title = mem_alloc( len + sizeof(WCHAR) ); + if (new_title) + { + memcpy( new_title, title, len + sizeof(WCHAR) ); + new_title[len / sizeof(WCHAR)] = 0; + if (console->title) free( console->title ); + console->title = new_title; + } + } + if (req->mask & SET_CONSOLE_INPUT_INFO_HISTORY_MODE) + { + console->history_mode = req->history_mode; + } + if ((req->mask & SET_CONSOLE_INPUT_INFO_HISTORY_SIZE) && + console->history_size != req->history_size) + { + WCHAR** mem = NULL; + int i; + int delta; + + if (req->history_size) + { + mem = mem_alloc( req->history_size * sizeof(WCHAR*) ); + if (!mem) goto error; + memset( mem, 0, req->history_size * sizeof(WCHAR*) ); + } + + delta = (console->history_index > req->history_size) ? + (console->history_index - req->history_size) : 0; + + for (i = delta; i < console->history_index; i++) + { + mem[i - delta] = console->history[i]; + console->history[i] = NULL; + } + console->history_index -= delta; + + for (i = 0; i < console->history_size; i++) + if (console->history[i]) free( console->history[i] ); + free( console->history ); + console->history = mem; + console->history_size = req->history_size; + } + release_object( console ); + return 1; + error: + if (console) release_object( console ); + return 0; +} + +/* set misc screen buffer information */ +static int set_console_output_info( struct screen_buffer *screen_buffer, + struct set_console_output_info_request *req ) +{ + struct console_renderer_event evt; + + if (req->mask & SET_CONSOLE_OUTPUT_INFO_CURSOR_GEOM) + { + if (req->cursor_size < 1 || req->cursor_size > 100) + { + set_error( STATUS_INVALID_PARAMETER ); + return 0; + } + if (screen_buffer->cursor_size != req->cursor_size || + screen_buffer->cursor_visible != req->cursor_visible) + { + screen_buffer->cursor_size = req->cursor_size; + screen_buffer->cursor_visible = req->cursor_visible; + evt.event = CONSOLE_RENDERER_CURSOR_GEOM_EVENT; + evt.u.cursor_geom.size = req->cursor_size; + evt.u.cursor_geom.visible = req->cursor_visible; + console_input_events_append( screen_buffer->input->evt, &evt ); + } + } + if (req->mask & SET_CONSOLE_OUTPUT_INFO_CURSOR_POS) + { + if (req->cursor_x < 0 || req->cursor_x >= screen_buffer->width || + req->cursor_y < 0 || req->cursor_y >= screen_buffer->height) + { + set_error( STATUS_INVALID_PARAMETER ); + return 0; + } + if (screen_buffer->cursor.X != req->cursor_x || screen_buffer->cursor.Y != req->cursor_y) + { + screen_buffer->cursor.X = req->cursor_x; + screen_buffer->cursor.Y = req->cursor_y; + evt.event = CONSOLE_RENDERER_CURSOR_POS_EVENT; + evt.u.cursor_pos.x = req->cursor_x; + evt.u.cursor_pos.y = req->cursor_y; + console_input_events_append( screen_buffer->input->evt, &evt ); + } + } + if (req->mask & SET_CONSOLE_OUTPUT_INFO_SIZE) + { + int i, j; + /* FIXME: there are also some basic minimum and max size to deal with */ + unsigned* new_data = mem_alloc( 4 * req->width * req->height ); + + if (!new_data) return 0; + + /* fill the buffer with either the old buffer content or white on black spaces */ + for (j = 0; j < req->height; j++) + { + for (i = 0; i < req->width; i++) + { + new_data[j * req->width + i] = + (i < screen_buffer->width && j < screen_buffer->height) ? + screen_buffer->data[j * screen_buffer->width + i] : 0x00F00020; + } + } + free( screen_buffer->data ); + screen_buffer->data = new_data; + screen_buffer->width = req->width; + screen_buffer->height = req->height; + evt.event = CONSOLE_RENDERER_SB_RESIZE_EVENT; + evt.u.resize.width = req->width; + evt.u.resize.height = req->height; + console_input_events_append( screen_buffer->input->evt, &evt ); + + if (screen_buffer == screen_buffer->input->active && + screen_buffer->input->mode & ENABLE_WINDOW_INPUT) + { + INPUT_RECORD ir; + ir.EventType = WINDOW_BUFFER_SIZE_EVENT; + ir.Event.WindowBufferSizeEvent.dwSize.X = req->width; + ir.Event.WindowBufferSizeEvent.dwSize.Y = req->height; + write_console_input( screen_buffer->input, 1, &ir ); + } + } + if (req->mask & SET_CONSOLE_OUTPUT_INFO_ATTR) + { + screen_buffer->attr = req->attr; + } + if (req->mask & SET_CONSOLE_OUTPUT_INFO_DISPLAY_WINDOW) + { + if (req->win_left < 0 || req->win_left > req->win_right || + req->win_right >= screen_buffer->width || + req->win_top < 0 || req->win_top > req->win_bottom || + req->win_bottom >= screen_buffer->height) + { + set_error( STATUS_INVALID_PARAMETER ); + return 0; + } + if (screen_buffer->win.Left != req->win_left || screen_buffer->win.Top != req->win_top || + screen_buffer->win.Right != req->win_right || screen_buffer->win.Bottom != req->win_bottom) + { + screen_buffer->win.Left = req->win_left; + screen_buffer->win.Top = req->win_top; + screen_buffer->win.Right = req->win_right; + screen_buffer->win.Bottom = req->win_bottom; + evt.event = CONSOLE_RENDERER_DISPLAY_EVENT; + evt.u.display.left = req->win_left; + evt.u.display.top = req->win_top; + evt.u.display.width = req->win_right - req->win_left + 1; + evt.u.display.height = req->win_bottom - req->win_top + 1; + console_input_events_append( screen_buffer->input->evt, &evt ); + } + } + if (req->mask & SET_CONSOLE_OUTPUT_INFO_MAX_SIZE) + { + /* can only be done by renderer */ + if (current->process->console != screen_buffer->input) + { + set_error( STATUS_INVALID_PARAMETER ); + return 0; + } + + screen_buffer->max_width = req->max_width; + screen_buffer->max_height = req->max_height; + } + + return 1; +} + +/* appends a new line to history (history is a fixed size array) */ +static void console_input_append_hist( struct console_input* console, const WCHAR* buf, size_t len ) +{ + WCHAR* ptr = mem_alloc( (len + 1) * sizeof(WCHAR) ); + + if (!ptr) + { + set_error( STATUS_NO_MEMORY ); + return; + } + if (!console || !console->history_size) + { + set_error( STATUS_INVALID_PARAMETER ); /* FIXME */ + return; + } + + memcpy( ptr, buf, len * sizeof(WCHAR) ); + ptr[len] = 0; + + if (console->history_mode && console->history_index && + strncmpW( console->history[console->history_index - 1], ptr, len * sizeof(WCHAR) ) == 0) + { + /* ok, mode ask us to not use twice the same string... + * so just free mem and returns + */ + set_error( STATUS_ALIAS_EXISTS ); + free(ptr); + return; + } + + if (console->history_index < console->history_size) + { + console->history[console->history_index++] = ptr; + } + else + { + free( console->history[0]) ; + memmove( &console->history[0], &console->history[1], + (console->history_size - 1) * sizeof(WCHAR*) ); + console->history[console->history_size - 1] = ptr; + } +} + +/* returns a line from the cachde */ +static int console_input_get_hist( struct console_input* console, WCHAR* buf, size_t len, int index ) +{ + int ret; + + /* FIXME: don't use len yet */ + if (!console || index >= console->history_index) + { + set_error( STATUS_INVALID_PARAMETER ); + return 0; + } + ret = strlenW(console->history[index]); + memcpy( buf, console->history[index], ret * sizeof(WCHAR) ); /* FIXME should use len */ + return ret; +} + +/* dumb dump */ static void console_input_dump( struct object *obj, int verbose ) { struct console_input *console = (struct console_input *)obj; assert( obj->ops == &console_input_ops ); - fprintf( stderr, "Console input fd=%d\n", console->obj.fd ); + fprintf( stderr, "Console input active=%p evt=%p\n", + console->active, console->evt ); } -static int console_input_get_poll_events( struct object *obj ) -{ - return POLLIN; -} - -static int console_input_get_fd( struct object *obj ) -{ - struct console_input *console = (struct console_input *)obj; - assert( obj->ops == &console_input_ops ); - return console->obj.fd; -} - -static int console_get_info( struct object *obj, struct get_file_info_request *req ) +static int console_get_file_info( struct object *obj, struct get_file_info_request *req ) { if (req) { @@ -373,59 +769,245 @@ static int console_get_info( struct object *obj, struct get_file_info_request *r static void console_input_destroy( struct object *obj ) { - struct console_input *console = (struct console_input *)obj; + struct console_input* console_in = (struct console_input *)obj; + struct screen_buffer* curr; + int i; + assert( obj->ops == &console_input_ops ); - if (console->output) console->output->input = NULL; + if (console_in->title) free( console_in->title ); + if (console_in->records) free( console_in->records ); + + if (console_in->active) release_object( console_in->active ); + console_in->active = NULL; + + for (curr = screen_buffer_list; curr; curr = curr->next) + { + if (curr->input == console_in) curr->input = NULL; + } + + release_object( console_in->evt ); + console_in->evt = NULL; + + for (i = 0; i < console_in->history_size; i++) + if (console_in->history[i]) free( console_in->history[i] ); + if (console_in->history) free( console_in->history ); } static void screen_buffer_dump( struct object *obj, int verbose ) { - struct screen_buffer *console = (struct screen_buffer *)obj; + struct screen_buffer *screen_buffer = (struct screen_buffer *)obj; assert( obj->ops == &screen_buffer_ops ); - fprintf( stderr, "Console screen buffer fd=%d\n", console->obj.fd ); -} -static int screen_buffer_get_poll_events( struct object *obj ) -{ - return POLLOUT; -} - -static int screen_buffer_get_fd( struct object *obj ) -{ - struct screen_buffer *console = (struct screen_buffer *)obj; - assert( obj->ops == &screen_buffer_ops ); - return console->obj.fd; + fprintf(stderr, "Console screen buffer input=%p\n", screen_buffer->input ); } static void screen_buffer_destroy( struct object *obj ) { - struct screen_buffer *console = (struct screen_buffer *)obj; + struct screen_buffer* screen_buffer = (struct screen_buffer *)obj; + struct screen_buffer** psb; + assert( obj->ops == &screen_buffer_ops ); - if (console->input) console->input->output = NULL; - if (console->title) free( console->title ); + + for (psb = &screen_buffer_list; *psb; *psb = (*psb)->next) + { + if (*psb == screen_buffer) + { + *psb = screen_buffer->next; + break; + } + } + if (screen_buffer->input && screen_buffer->input->active == screen_buffer) + { + struct screen_buffer* sb; + for (sb = screen_buffer_list; sb && sb->input != screen_buffer->input; sb = sb->next); + screen_buffer->input->active = sb; + } } -/* allocate a console for the current process */ +/* write data into a screen buffer */ +static int write_console_output( struct screen_buffer *screen_buffer, size_t size, + const unsigned char* data, int mode, short int x, short int y ) +{ + int uniform = mode & WRITE_CONSOLE_MODE_UNIFORM; + unsigned *ptr; + unsigned i, inc; + int len; + + mode &= ~WRITE_CONSOLE_MODE_UNIFORM; + + if (mode < 0 || mode > 3) + { + set_error(STATUS_INVALID_PARAMETER); + return 0; + } + + /* set destination pointer and increment */ + ptr = screen_buffer->data + (y * screen_buffer->width + x); + if (mode == WRITE_CONSOLE_MODE_ATTR) ptr = (unsigned*)((char*)ptr + 2); + inc = (mode == WRITE_CONSOLE_MODE_TEXTATTR) ? 4 : 2; + len = size / inc; + + /* crop if needed */ + if (x + len > screen_buffer->width) len = screen_buffer->width - x; + + for (i = 0; i < len; i++) + { + if (mode == WRITE_CONSOLE_MODE_TEXTSTDATTR) + { + memcpy( (char*)ptr + 2, &screen_buffer->attr, 2 ); + } + memcpy( ptr++, data, inc ); + if (!uniform) data += inc; + } + + if (len && screen_buffer == screen_buffer->input->active) + { + int y2; + struct console_renderer_event evt; + + y2 = (y * screen_buffer->width + x + len - 1) / screen_buffer->width; + + evt.event = CONSOLE_RENDERER_UPDATE_EVENT; + evt.u.update.top = y; + evt.u.update.bottom = y2; + console_input_events_append( screen_buffer->input->evt, &evt ); + } + return len; +} + +/* read data from a screen buffer */ +static int read_console_output( struct screen_buffer *screen_buffer, size_t size, void* data, + short int x, short int y, short int w, short int h, + short int* eff_w, short int* eff_h ) +{ + int j; + + if (size < w * h * 4 || x >= screen_buffer->width || y >= screen_buffer->height) + { + set_error(STATUS_INVALID_PARAMETER); + return 0; + } + + *eff_w = w; + *eff_h = h; + if (x + w > screen_buffer->width) *eff_w = screen_buffer->width - x; + if (y + h > screen_buffer->height) *eff_h = screen_buffer->height - y; + + for (j = 0; j < *eff_h; j++) + { + memcpy( (char*)data + 4 * j * w, &screen_buffer->data[(y + j) * screen_buffer->width + x], + *eff_w * 4 ); + } + + return *eff_w * *eff_h; +} + +/* scroll parts of a screen buffer */ +static void scroll_console_output( handle_t handle, short int xsrc, short int ysrc, + short int xdst, short int ydst, short int w, short int h ) +{ + struct screen_buffer *screen_buffer; + int j; + unsigned* psrc; + unsigned* pdst; + struct console_renderer_event evt; + + if (!(screen_buffer = (struct screen_buffer *)get_handle_obj( current->process, handle, + GENERIC_READ, &screen_buffer_ops ))) + return; + if (xsrc < 0 || ysrc < 0 || xdst < 0 || ydst < 0 || + xsrc + w > screen_buffer->width || + xdst + w > screen_buffer->width || + ysrc + h > screen_buffer->height || + ydst + h > screen_buffer->height || + w == 0 || h == 0) + { + set_error( STATUS_INVALID_PARAMETER ); + release_object( screen_buffer ); + return; + } + + if (ysrc < ydst) + { + psrc = &screen_buffer->data[(ysrc + h - 1) * screen_buffer->width + xsrc]; + pdst = &screen_buffer->data[(ydst + h - 1) * screen_buffer->width + xdst]; + + for (j = h; j > 0; j--) + { + memcpy(pdst, psrc, w * 4); + pdst -= screen_buffer->width; + psrc -= screen_buffer->width; + } + } + else + { + psrc = &screen_buffer->data[ysrc * screen_buffer->width + xsrc]; + pdst = &screen_buffer->data[ydst * screen_buffer->width + xdst]; + + for (j = 0; j < h; j++) + { + /* we use memmove here because when psrc and pdst are the same, + * copies are done on the same row, so the dst and src blocks + * can overlap */ + memmove( pdst, psrc, w * 4 ); + pdst += screen_buffer->width; + psrc += screen_buffer->width; + } + } + + /* FIXME: this could be enhanced, by signalling scroll */ + evt.event = CONSOLE_RENDERER_UPDATE_EVENT; + evt.u.update.top = min(ysrc, ydst); + evt.u.update.bottom = max(ysrc, ydst) + h - 1; + console_input_events_append( screen_buffer->input->evt, &evt ); + + release_object( screen_buffer ); +} + +/* allocate a console for the renderer */ DECL_HANDLER(alloc_console) { - handle_t in = 0, out = 0; + handle_t in = 0; + handle_t evt = 0; + struct process *process; + struct process *renderer = current->process; + struct console_input *console; - if (!alloc_console( current->process )) goto done; + process = (req->pid) ? get_process_from_id( req->pid ) : + (struct process *)grab_object( renderer->parent ); - if ((in = alloc_handle( current->process, current->process->console_in, - req->access, req->inherit ))) - { - if ((out = alloc_handle( current->process, current->process->console_out, - req->access, req->inherit ))) - goto done; /* everything is fine */ - close_handle( current->process, in, NULL ); - in = 0; + req->handle_in = 0; + req->event = 0; + if (!process) return; + if (process != renderer && process->console) + { + set_error( STATUS_ACCESS_DENIED ); + goto the_end; } - free_console( current->process ); - done: - req->handle_in = in; - req->handle_out = out; + if ((console = (struct console_input*)create_console_input( renderer ))) + { + if ((in = alloc_handle( renderer, console, req->access, req->inherit ))) + { + if ((evt = alloc_handle( renderer, console->evt, + SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE, FALSE ))) + { + if (process != renderer) + { + process->console = (struct console_input*)grab_object( console ); + console->num_proc++; + } + req->handle_in = in; + req->event = evt; + release_object( console ); + goto the_end; + } + close_handle( renderer, in, NULL ); + } + free_console( process ); + } + the_end: + release_object( process ); } /* free the console of the current process */ @@ -434,58 +1016,83 @@ DECL_HANDLER(free_console) free_console( current->process ); } +/* let the renderer peek the events it's waiting on */ +DECL_HANDLER(get_console_renderer_events) +{ + struct console_input_events* evt; + size_t len = 0; + + evt = (struct console_input_events *)get_handle_obj( current->process, req->handle, + GENERIC_WRITE, &console_input_events_ops ); + if (!evt) return; + len = console_input_events_get( evt, get_req_data(req), get_req_data_size(req) ); + set_req_data_size( req, len ); + release_object( evt ); +} + /* open a handle to the process console */ DECL_HANDLER(open_console) { - struct object *obj= req->output ? current->process->console_out : current->process->console_in; + struct object *obj = NULL; req->handle = 0; - if (obj) req->handle = alloc_handle( current->process, obj, req->access, req->inherit ); - else set_error( STATUS_ACCESS_DENIED ); + switch (req->from) + { + case 0: + if (current->process->console && current->process->console->renderer) + obj = grab_object( (struct object*)current->process->console ); + break; + case 1: + if (current->process->console && current->process->console->renderer && + current->process->console->active) + obj = grab_object( (struct object*)current->process->console->active ); + break; + default: + if ((obj = get_handle_obj( current->process, (handle_t)req->from, + GENERIC_READ|GENERIC_WRITE, &console_input_ops ))) + { + struct console_input* console = (struct console_input*)obj; + obj = (console->active) ? grab_object( console->active ) : NULL; + release_object( console ); + } + break; + } + + /* FIXME: req->share is not used (as in screen buffer creation) */ + if (obj) + { + req->handle = alloc_handle( current->process, obj, req->access, req->inherit ); + release_object( obj ); + } + if (!req->handle && !get_error()) set_error( STATUS_ACCESS_DENIED ); } -/* set info about a console (output only) */ -DECL_HANDLER(set_console_info) +/* set info about a console input */ +DECL_HANDLER(set_console_input_info) { - set_console_info( req->handle, req, get_req_data(req), get_req_data_size(req) ); + set_console_input_info( req, get_req_data(req), get_req_data_size(req) ); } /* get info about a console (output only) */ -DECL_HANDLER(get_console_info) +DECL_HANDLER(get_console_input_info) { - struct screen_buffer *console; - size_t len = 0; + struct console_input *console = 0; - if ((console = (struct screen_buffer *)get_handle_obj( current->process, req->handle, - GENERIC_READ, &screen_buffer_ops ))) + set_req_data_size( req, 0 ); + if (!(console = console_input_get( req->handle, GENERIC_READ ))) return; + + if (console->title) { - req->cursor_size = console->cursor_size; - req->cursor_visible = console->cursor_visible; - req->pid = console->pid; - if (console->title) - { - len = strlen( console->title ); - if (len > get_req_data_size(req)) len = get_req_data_size(req); - memcpy( get_req_data(req), console->title, len ); - } - release_object( console ); + size_t len = strlenW( console->title ) * sizeof(WCHAR); + if (len > get_req_data_size(req)) len = get_req_data_size(req); + memcpy( get_req_data(req), console->title, len ); + set_req_data_size( req, len ); } - set_req_data_size( req, len ); -} + req->history_mode = console->history_mode; + req->history_size = console->history_size; + req->history_index = console->history_index; -/* set a console fd */ -DECL_HANDLER(set_console_fd) -{ - int fd_out, fd_in = thread_get_inflight_fd( current, req->fd_in ); - - if (req->fd_out == req->fd_in) fd_out = dup( fd_in ); - else fd_out = thread_get_inflight_fd( current, req->fd_out ); - - if (fd_in == -1 || fd_out == -1) set_error( STATUS_INVALID_HANDLE ); - else if (set_console_fd( req->handle, fd_in, fd_out, req->pid )) return; - - if (fd_in != -1) close( fd_in ); - if (fd_out != -1) close( fd_out ); + release_object( console ); } /* get a console mode (input or output) */ @@ -503,8 +1110,16 @@ DECL_HANDLER(set_console_mode) /* add input records to a console input queue */ DECL_HANDLER(write_console_input) { - req->written = write_console_input( req->handle, get_req_data_size(req) / sizeof(INPUT_RECORD), + struct console_input *console; + + req->written = 0; + if (!(console = (struct console_input *)get_handle_obj( current->process, req->handle, + GENERIC_WRITE, &console_input_ops ))) + return; + + req->written = write_console_input( console, get_req_data_size(req) / sizeof(INPUT_RECORD), get_req_data(req) ); + release_object( console ); } /* fetch input records from a console input queue */ @@ -516,3 +1131,132 @@ DECL_HANDLER(read_console_input) if (size) set_req_data_size( req, res * sizeof(INPUT_RECORD) ); req->read = res; } + +/* appends a string to console's history */ +DECL_HANDLER(append_console_input_history) +{ + struct console_input* console; + + if (!(console = console_input_get( req->handle, GENERIC_WRITE ))) return; + console_input_append_hist( console, get_req_data(req), + get_req_data_size(req) / sizeof(WCHAR) ); + release_object( console ); +} + +/* appends a string to console's history */ +DECL_HANDLER(get_console_input_history) +{ + struct console_input* console; + int len; + + if (!(console = console_input_get( req->handle, GENERIC_WRITE ))) return; + + len = console_input_get_hist( console, get_req_data(req), 0 /* FIXME */, req->index ); + set_req_data_size( req, len * sizeof(WCHAR)); + release_object( console ); +} + +/* creates a screen buffer */ +DECL_HANDLER(create_console_output) +{ + struct console_input* console; + struct screen_buffer* screen_buffer; + + if (!(console = console_input_get( req->handle_in, GENERIC_WRITE))) return; + + screen_buffer = (struct screen_buffer*)create_console_output( console ); + if (screen_buffer) + { + /* FIXME: should store sharing and test it when opening the CONOUT$ device + * see file.c on how this could be done + */ + req->handle_out = alloc_handle( current->process, screen_buffer, req->access, req->inherit ); + release_object( screen_buffer ); + } + release_object( console ); +} + +/* set info about a console screen buffer */ +DECL_HANDLER(set_console_output_info) +{ + struct screen_buffer *screen_buffer; + + if (!(screen_buffer = (struct screen_buffer*)get_handle_obj( current->process, req->handle, + GENERIC_WRITE, &screen_buffer_ops ))) + return; + + set_console_output_info( screen_buffer, req ); + release_object( screen_buffer ); +} + +/* get info about a console screen buffer */ +DECL_HANDLER(get_console_output_info) +{ + struct screen_buffer *screen_buffer; + size_t len = 0; + + if ((screen_buffer = (struct screen_buffer *)get_handle_obj( current->process, req->handle, + GENERIC_READ, &screen_buffer_ops ))) + { + req->cursor_size = screen_buffer->cursor_size; + req->cursor_visible = screen_buffer->cursor_visible; + req->cursor_x = screen_buffer->cursor.X; + req->cursor_y = screen_buffer->cursor.Y; + req->width = screen_buffer->width; + req->height = screen_buffer->height; + req->attr = screen_buffer->attr; + req->win_left = screen_buffer->win.Left; + req->win_top = screen_buffer->win.Top; + req->win_right = screen_buffer->win.Right; + req->win_bottom = screen_buffer->win.Bottom; + req->max_width = screen_buffer->max_width; + req->max_height = screen_buffer->max_height; + + release_object( screen_buffer ); + } + set_req_data_size( req, len ); +} + +/* read data (chars & attrs) from a screen buffer */ +DECL_HANDLER(read_console_output) +{ + struct screen_buffer *screen_buffer; + size_t size = get_req_data_size(req); + int res; + + if (!(screen_buffer = (struct screen_buffer*)get_handle_obj( current->process, req->handle, + GENERIC_READ, &screen_buffer_ops ))) + return; + + res = read_console_output( screen_buffer, size, get_req_data(req), + req->x, req->y, req->w, req->h, &req->eff_w, &req->eff_h); + /* if size was 0 we didn't fetch anything */ + if (size) set_req_data_size( req, res * 4 ); + release_object( screen_buffer ); +} + +/* write data (char and/or attrs) to a screen buffer */ +DECL_HANDLER(write_console_output) +{ + struct screen_buffer *screen_buffer; + size_t size = get_req_data_size(req); + int res; + + if (!(screen_buffer = (struct screen_buffer*)get_handle_obj( current->process, req->handle, + GENERIC_WRITE, &screen_buffer_ops ))) + return; + + res = write_console_output( screen_buffer, size, get_req_data(req), req->mode, req->x, req->y ); + + /* if size was 0 we didn't fetch anything */ + if (size) set_req_data_size( req, res ); + req->written = res; + release_object( screen_buffer ); +} + +/* move a rect of data in a screen buffer */ +DECL_HANDLER(move_console_output) +{ + scroll_console_output( req->handle, req->x_src, req->y_src, req->x_dst, req->y_dst, + req->w, req->h ); +} diff --git a/server/console.h b/server/console.h new file mode 100644 index 00000000000..88999a15bbb --- /dev/null +++ b/server/console.h @@ -0,0 +1,35 @@ +/* + * Wine server consoles + * + * Copyright (C) 2001 Eric Pouech + */ + +#ifndef __WINE_SERVER_CONSOLE_H +#define __WINE_SERVER_CONSOLE_H + +struct screen_buffer; +struct console_input_events; + +struct console_input +{ + struct object obj; /* object header */ + int num_proc; /* number of processes attached to this console */ + struct process *renderer; /* console renderer thread */ + int mode; /* input mode */ + struct screen_buffer *active; /* active screen buffer */ + int recnum; /* number of input records */ + void *records; /* input records */ + struct console_input_events *evt; /* synchronization event with renderer */ + WCHAR *title; /* console title */ + WCHAR **history; /* lines history */ + int history_size; /* number of entries in history array */ + int history_index; /* number of used entries in history array */ + int history_mode; /* mode of history (non zero means remove doubled strings */ +}; + +/* console functions */ + +extern void inherit_console(struct process *parent, struct process *process, handle_t hconin); +extern int free_console( struct process *process ); + +#endif /* __WINE_SERVER_CONSOLE_H */ diff --git a/server/debugger.c b/server/debugger.c index bde34444f64..09b4240cab4 100644 --- a/server/debugger.c +++ b/server/debugger.c @@ -14,6 +14,7 @@ #include "process.h" #include "thread.h" #include "request.h" +#include "console.h" enum debug_event_state { EVENT_QUEUED, EVENT_SENT, EVENT_CONTINUED }; @@ -411,6 +412,11 @@ static int debugger_attach( struct process *process, struct thread *debugger ) for (thread = debugger; thread; thread = thread->process->debugger) if (thread->process == process) goto error; + /* don't let a debugger debug its console... won't work */ + if (debugger->process->console && + debugger->process->console->renderer == process && + process->console) goto error; + suspend_process( process ); /* we must have been able to attach all threads */ diff --git a/server/file.c b/server/file.c index 4ed9eac4c3d..6dde94cdd53 100644 --- a/server/file.c +++ b/server/file.c @@ -494,7 +494,7 @@ DECL_HANDLER(alloc_file_handle) if ((file = create_file_for_fd( fd, req->access, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, DRIVE_UNKNOWN ))) { - req->handle = alloc_handle( current->process, file, req->access, 0 ); + req->handle = alloc_handle( current->process, file, req->access, req->inherit ); release_object( file ); } } diff --git a/server/handle.c b/server/handle.c index 50fef3eba10..ac4235ffd04 100644 --- a/server/handle.c +++ b/server/handle.c @@ -355,6 +355,7 @@ struct object *get_handle_obj( struct process *process, handle_t handle, if (!(entry = get_handle( process, handle ))) return NULL; if ((entry->access & access) != access) { + fprintf( stderr, "handle %d access %08x denied (%08x)\n", handle, access, entry->access ); set_error( STATUS_ACCESS_DENIED ); return NULL; } @@ -376,6 +377,7 @@ int get_handle_fd( struct process *process, handle_t handle, unsigned int access if (!(entry = get_handle( process, handle ))) return -1; if ((entry->access & access) != access) { + fprintf( stderr, "handle %d access %08x denied (%08x)\n", handle, access, entry->access ); set_error( STATUS_ACCESS_DENIED ); return -1; } diff --git a/server/object.h b/server/object.h index 32fa3461595..09641d3f3aa 100644 --- a/server/object.h +++ b/server/object.h @@ -155,11 +155,6 @@ extern void file_set_error(void); int get_serial_async_timeout(struct object *obj, int type, int count); -/* console functions */ - -extern int alloc_console( struct process *process ); -extern int free_console( struct process *process ); - /* debugger functions */ extern int set_process_debugger( struct process *process, struct thread *debugger ); diff --git a/server/process.c b/server/process.c index b3196a90b50..f02f5b79077 100644 --- a/server/process.c +++ b/server/process.c @@ -26,6 +26,7 @@ #include "process.h" #include "thread.h" #include "request.h" +#include "console.h" /* process structure */ @@ -101,12 +102,18 @@ static int set_process_console( struct process *process, struct process *parent, { if (process->create_flags & CREATE_NEW_CONSOLE) { - if (!alloc_console( process )) return 0; + /* let the process init do the allocation */ + return 1; } else if (parent && !(process->create_flags & DETACHED_PROCESS)) { - if (parent->console_in) process->console_in = grab_object( parent->console_in ); - if (parent->console_out) process->console_out = grab_object( parent->console_out ); + /* FIXME: some better error checking should be done... + * like if hConOut and hConIn are console handles, then they should be on the same + * physical console + */ + inherit_console( parent, process, + (info->inherit_all || (info->start_flags & STARTF_USESTDHANDLES)) ? + info->hstdin : 0 ); } if (parent) { @@ -129,13 +136,20 @@ static int set_process_console( struct process *process, struct process *parent, } else { - /* no parent, use handles to the console for stdio */ - req->hstdin = alloc_handle( process, process->console_in, - GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1 ); - req->hstdout = alloc_handle( process, process->console_out, - GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1 ); - req->hstderr = alloc_handle( process, process->console_out, - GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1 ); + if (process->console) + { + req->hstdin = alloc_handle( process, process->console, + GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1 ); + req->hstdout = alloc_handle( process, process->console->active, + GENERIC_READ | GENERIC_WRITE, 1 ); + req->hstderr = alloc_handle( process, process->console->active, + GENERIC_READ | GENERIC_WRITE, 1 ); + } + else + { + /* no parent, let the caller decide what to do */ + req->hstdin = req->hstdout = req->hstderr = 0; + } } /* some handles above may have been invalid; this is not an error */ if (get_error() == STATUS_INVALID_HANDLE) clear_error(); @@ -152,6 +166,7 @@ struct thread *create_process( int fd ) if (!(process = alloc_object( &process_ops, fd ))) return NULL; process->next = NULL; process->prev = NULL; + process->parent = NULL; process->thread_list = NULL; process->debugger = NULL; process->handles = NULL; @@ -161,8 +176,7 @@ struct thread *create_process( int fd ) process->affinity = 1; process->suspend = 0; process->create_flags = 0; - process->console_in = NULL; - process->console_out = NULL; + process->console = NULL; process->init_event = NULL; process->idle_event = NULL; process->queue = NULL; @@ -173,6 +187,7 @@ struct thread *create_process( int fd ) process->exe.file = NULL; process->exe.dbg_offset = 0; process->exe.dbg_size = 0; + gettimeofday( &process->start_time, NULL ); if ((process->next = first_process) != NULL) process->next->prev = process; first_process = process; @@ -222,10 +237,11 @@ static void init_process( int ppid, struct init_process_request *req ) fatal_protocol_error( current, "init_process: called twice?\n" ); return; } + process->parent = (struct process *)grab_object( parent ); } /* set the process flags */ - process->create_flags = info ? info->create_flags : CREATE_NEW_CONSOLE; + process->create_flags = info ? info->create_flags : 0; /* create the handle table */ if (parent && info->inherit_all) @@ -288,6 +304,8 @@ static void process_destroy( struct object *obj ) /* we can't have a thread remaining */ assert( !process->thread_list ); + if (process->console) release_object( process->console ); + if (process->parent) release_object( process->parent ); if (process->next) process->next->prev = process->prev; if (process->prev) process->prev->next = process->next; else first_process = process->next; @@ -304,9 +322,8 @@ static void process_dump( struct object *obj, int verbose ) struct process *process = (struct process *)obj; assert( obj->ops == &process_ops ); - fprintf( stderr, "Process next=%p prev=%p console=%p/%p handles=%p\n", - process->next, process->prev, process->console_in, process->console_out, - process->handles ); + fprintf( stderr, "Process next=%p prev=%p handles=%p\n", + process->next, process->prev, process->handles ); } static int process_signaled( struct object *obj, struct thread *thread ) @@ -418,6 +435,25 @@ static void process_unload_dll( struct process *process, void *base ) set_error( STATUS_INVALID_PARAMETER ); } +/* kill all processes being attached to a console renderer */ +static void kill_console_processes( struct process *renderer, int exit_code ) +{ + for (;;) /* restart from the beginning of the list every time */ + { + struct process *process = first_process; + + /* find the first process being attached to 'renderer' and still running */ + while (process && + (process == renderer || !process->console || + process->console->renderer != renderer || !process->running_threads)) + { + process = process->next; + } + if (!process) break; + kill_process( process, NULL, exit_code ); + } +} + /* a process has been killed (i.e. its last thread died) */ static void process_killed( struct process *process ) { @@ -425,7 +461,13 @@ static void process_killed( struct process *process ) gettimeofday( &process->end_time, NULL ); if (process->handles) release_object( process->handles ); process->handles = NULL; + + /* close the console attached to this process, if any */ free_console( process ); + + /* close the processes using process as renderer, if any */ + kill_console_processes( process, 0 ); + while (process->exe.next) { struct process_dll *dll = process->exe.next; @@ -508,6 +550,7 @@ void resume_process( struct process *process ) void kill_process( struct process *process, struct thread *skip, int exit_code ) { struct thread *thread = process->thread_list; + while (thread) { struct thread *next = thread->proc_next; @@ -532,6 +575,7 @@ void kill_debugged_processes( struct thread *debugger, int exit_code ) } } + /* get all information about a process */ static void get_process_info( struct process *process, struct get_process_info_request *req ) { diff --git a/server/process.h b/server/process.h index 28fbd2e8759..d17cb1115a8 100644 --- a/server/process.h +++ b/server/process.h @@ -30,6 +30,7 @@ struct process struct object obj; /* object header */ struct process *next; /* system-wide process list */ struct process *prev; + struct process *parent; /* parent process */ struct thread *thread_list; /* head of the thread list */ struct thread *debugger; /* thread debugging this process */ struct object *handles; /* handle entries */ @@ -41,8 +42,7 @@ struct process int affinity; /* process affinity mask */ int suspend; /* global process suspend count */ int create_flags; /* process creation flags */ - struct object *console_in; /* console input */ - struct object *console_out; /* console output */ + struct console_input*console; /* console input */ struct event *init_event; /* event for init done */ struct event *idle_event; /* event for input idle */ struct msg_queue *queue; /* main message queue */ @@ -55,7 +55,6 @@ struct process struct process_snapshot { struct process *process; /* process ptr */ - struct process *parent; /* process parent */ int count; /* process refcount */ int threads; /* number of threads */ int priority; /* priority class */ diff --git a/server/protocol.def b/server/protocol.def index e44ef528cbb..f631297dc7b 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -533,6 +533,7 @@ enum event_op { PULSE_EVENT, SET_EVENT, RESET_EVENT }; /* Allocate a file handle for a Unix fd */ @REQ(alloc_file_handle) unsigned int access; /* wanted access rights */ + int inherit; /* inherit flag */ int fd; /* file descriptor on the client side */ @REPLY handle_t handle; /* handle to the file */ @@ -685,13 +686,14 @@ enum event_op { PULSE_EVENT, SET_EVENT, RESET_EVENT }; @END -/* Allocate a console for the current process */ +/* Allocate a console (only used by a console renderer) */ @REQ(alloc_console) unsigned int access; /* wanted access rights */ int inherit; /* inherit flag */ + void* pid; /* pid of process which shall be attached to the console */ @REPLY handle_t handle_in; /* handle to console input */ - handle_t handle_out; /* handle to console output */ + handle_t event; /* handle to renderer events change notification */ @END @@ -700,22 +702,67 @@ enum event_op { PULSE_EVENT, SET_EVENT, RESET_EVENT }; @END -/* Open a handle to the process console */ -@REQ(open_console) - int output; /* input or output? */ - unsigned int access; /* wanted access rights */ - int inherit; /* inherit flag */ +#define CONSOLE_RENDERER_NONE_EVENT 0x00 +#define CONSOLE_RENDERER_TITLE_EVENT 0x01 +#define CONSOLE_RENDERER_ACTIVE_SB_EVENT 0x02 +#define CONSOLE_RENDERER_SB_RESIZE_EVENT 0x03 +#define CONSOLE_RENDERER_UPDATE_EVENT 0x04 +#define CONSOLE_RENDERER_CURSOR_POS_EVENT 0x05 +#define CONSOLE_RENDERER_CURSOR_GEOM_EVENT 0x06 +#define CONSOLE_RENDERER_DISPLAY_EVENT 0x07 +#define CONSOLE_RENDERER_EXIT_EVENT 0x08 +struct console_renderer_event +{ + short event; + union + { + struct update + { + short top; + short bottom; + } update; + struct resize + { + short width; + short height; + } resize; + struct cursor_pos + { + short x; + short y; + } cursor_pos; + struct cursor_geom + { + short visible; + short size; + } cursor_geom; + struct display + { + short left; + short top; + short width; + short height; + } display; + } u; +}; + +/* retrieve console events for the renderer */ +@REQ(get_console_renderer_events) + handle_t handle; /* handle to console input events */ @REPLY - handle_t handle; /* handle to the console */ + VARARG(data,bytes); /* the various console_renderer_events */ @END -/* Set a console file descriptor */ -@REQ(set_console_fd) +/* Open a handle to the process console */ +@REQ(open_console) + int from; /* 0 (resp 1) input (resp output) of current process console */ + /* otherwise console_in handle to get active screen buffer? */ + unsigned int access; /* wanted access rights */ + int inherit; /* inherit flag */ + int share; /* share mask (only for output handles) */ +@REPLY handle_t handle; /* handle to the console */ - int fd_in; /* file descriptor to use as input */ - int fd_out; /* file descriptor to use as output */ - int pid; /* pid of xterm (hack) */ @END @@ -734,28 +781,104 @@ enum event_op { PULSE_EVENT, SET_EVENT, RESET_EVENT }; @END +/* Set info about a console (input only) */ +@REQ(set_console_input_info) + handle_t handle; /* handle to console input, or 0 for process' console */ + int mask; /* setting mask (see below) */ + handle_t active_sb; /* active screen buffer */ + int history_mode; /* whether we duplicate lines in history */ + int history_size; /* number of lines in history */ + VARARG(title,unicode_str); /* console title */ +@END +#define SET_CONSOLE_INPUT_INFO_ACTIVE_SB 0x01 +#define SET_CONSOLE_INPUT_INFO_TITLE 0x02 +#define SET_CONSOLE_INPUT_INFO_HISTORY_MODE 0x04 +#define SET_CONSOLE_INPUT_INFO_HISTORY_SIZE 0x08 + + +/* Get info about a console (input only) */ +@REQ(get_console_input_info) + handle_t handle; /* handle to console input, or 0 for process' console */ +@REPLY + int history_mode; /* whether we duplicate lines in history */ + int history_size; /* number of lines in history */ + int history_index; /* number of used lines in history */ + VARARG(title,unicode_str); /* console title */ +@END + + +/* appends a string to console's history */ +@REQ(append_console_input_history) + handle_t handle; /* handle to console input, or 0 for process' console */ + VARARG(line,unicode_str); /* line to add */ +@END + + +/* appends a string to console's history */ +@REQ(get_console_input_history) + handle_t handle; /* handle to console input, or 0 for process' console */ + int index; /* index to get line from */ +@REPLY + VARARG(line,unicode_str); /* line to add */ +@END + + +/* creates a new screen buffer on process' console */ +@REQ(create_console_output) + handle_t handle_in; /* handle to console input, or 0 for process' console */ + int access; /* wanted access rights */ + int share; /* sharing credentials */ + int inherit; /* inherit flag */ +@REPLY + handle_t handle_out; /* handle to the screen buffer */ +@END + + /* Set info about a console (output only) */ -@REQ(set_console_info) +@REQ(set_console_output_info) handle_t handle; /* handle to the console */ int mask; /* setting mask (see below) */ - int cursor_size; /* size of cursor (percentage filled) */ - int cursor_visible;/* cursor visibility flag */ - VARARG(title,string); /* console title */ + short int cursor_size; /* size of cursor (percentage filled) */ + short int cursor_visible;/* cursor visibility flag */ + short int cursor_x; /* position of cursor (x, y) */ + short int cursor_y; + short int width; /* width of the screen buffer */ + short int height; /* height of the screen buffer */ + short int attr; /* default attribute */ + short int win_left; /* window actually displayed by renderer */ + short int win_top; /* the rect area is expressed withing the */ + short int win_right; /* boundaries of the screen buffer */ + short int win_bottom; + short int max_width; /* maximum size (width x height) for the window */ + short int max_height; @END -#define SET_CONSOLE_INFO_CURSOR 0x01 -#define SET_CONSOLE_INFO_TITLE 0x02 +#define SET_CONSOLE_OUTPUT_INFO_CURSOR_GEOM 0x01 +#define SET_CONSOLE_OUTPUT_INFO_CURSOR_POS 0x02 +#define SET_CONSOLE_OUTPUT_INFO_SIZE 0x04 +#define SET_CONSOLE_OUTPUT_INFO_ATTR 0x08 +#define SET_CONSOLE_OUTPUT_INFO_DISPLAY_WINDOW 0x10 +#define SET_CONSOLE_OUTPUT_INFO_MAX_SIZE 0x20 + /* Get info about a console (output only) */ -@REQ(get_console_info) +@REQ(get_console_output_info) handle_t handle; /* handle to the console */ @REPLY - int cursor_size; /* size of cursor (percentage filled) */ - int cursor_visible;/* cursor visibility flag */ - int pid; /* pid of xterm (hack) */ - VARARG(title,string); /* console title */ + short int cursor_size; /* size of cursor (percentage filled) */ + short int cursor_visible;/* cursor visibility flag */ + short int cursor_x; /* position of cursor (x, y) */ + short int cursor_y; + short int width; /* width of the screen buffer */ + short int height; /* height of the screen buffer */ + short int attr; /* default attribute */ + short int win_left; /* window actually displayed by renderer */ + short int win_top; /* the rect area is expressed withing the */ + short int win_right; /* boundaries of the screen buffer */ + short int win_bottom; + short int max_width; /* maximum size (width x height) for the window */ + short int max_height; @END - /* Add input records to a console input queue */ @REQ(write_console_input) handle_t handle; /* handle to the console input */ @@ -764,6 +887,7 @@ enum event_op { PULSE_EVENT, SET_EVENT, RESET_EVENT }; int written; /* number of records written */ @END + /* Fetch input records from a console input queue */ @REQ(read_console_input) handle_t handle; /* handle to the console input */ @@ -774,6 +898,49 @@ enum event_op { PULSE_EVENT, SET_EVENT, RESET_EVENT }; @END +/* write data (chars and/or attributes) in a screen buffer */ +@REQ(write_console_output) + handle_t handle; /* handle to the console input */ + int mode; /* 0 for text, 1, for attributes, 2 for both */ + /* bit3 (4) set if uniform pattern in data */ + short int x; /* position where to start writing */ + short int y; + VARARG(data,bytes); /* info to write */ +@REPLY + int written; /* number of bytes actually written */ +@END +#define WRITE_CONSOLE_MODE_TEXT 0x00 +#define WRITE_CONSOLE_MODE_ATTR 0x01 +#define WRITE_CONSOLE_MODE_TEXTATTR 0x02 +#define WRITE_CONSOLE_MODE_TEXTSTDATTR 0x03 +#define WRITE_CONSOLE_MODE_UNIFORM 0x04 + + +/* read data (chars and/or attrubutes) from a screen buffer */ +@REQ(read_console_output) + handle_t handle; /* handle to the console input */ + short int x; /* position (x,y) where to start reading from */ + short int y; + short int w; /* size of area to read from (width x height) */ + short int h; +@REPLY + short int eff_w; /* effective width read */ + short int eff_h; /* effective height read */ + VARARG(data,bytes); +@END + +/* move a rect (of data) in screen buffer content */ +@REQ(move_console_output) + handle_t handle; /* handle to the console output */ + short int x_src; /* position (x, y) of rect to start moving from */ + short int y_src; + short int x_dst; /* position (x, y) of rect to move to */ + short int y_dst; + short int w; /* size of the rect (width, height) to move */ + short int h; +@END + + /* Create a change notification */ @REQ(create_change_notification) int subtree; /* watch all the subtree */ diff --git a/server/request.h b/server/request.h index 87c224e055c..58666416e07 100644 --- a/server/request.h +++ b/server/request.h @@ -118,14 +118,22 @@ DECL_HANDLER(get_socket_event); DECL_HANDLER(enable_socket_event); DECL_HANDLER(alloc_console); DECL_HANDLER(free_console); +DECL_HANDLER(get_console_renderer_events); DECL_HANDLER(open_console); -DECL_HANDLER(set_console_fd); DECL_HANDLER(get_console_mode); DECL_HANDLER(set_console_mode); -DECL_HANDLER(set_console_info); -DECL_HANDLER(get_console_info); +DECL_HANDLER(set_console_input_info); +DECL_HANDLER(get_console_input_info); +DECL_HANDLER(append_console_input_history); +DECL_HANDLER(get_console_input_history); +DECL_HANDLER(create_console_output); +DECL_HANDLER(set_console_output_info); +DECL_HANDLER(get_console_output_info); DECL_HANDLER(write_console_input); DECL_HANDLER(read_console_input); +DECL_HANDLER(write_console_output); +DECL_HANDLER(read_console_output); +DECL_HANDLER(move_console_output); DECL_HANDLER(create_change_notification); DECL_HANDLER(create_mapping); DECL_HANDLER(open_mapping); @@ -264,14 +272,22 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_enable_socket_event, (req_handler)req_alloc_console, (req_handler)req_free_console, + (req_handler)req_get_console_renderer_events, (req_handler)req_open_console, - (req_handler)req_set_console_fd, (req_handler)req_get_console_mode, (req_handler)req_set_console_mode, - (req_handler)req_set_console_info, - (req_handler)req_get_console_info, + (req_handler)req_set_console_input_info, + (req_handler)req_get_console_input_info, + (req_handler)req_append_console_input_history, + (req_handler)req_get_console_input_history, + (req_handler)req_create_console_output, + (req_handler)req_set_console_output_info, + (req_handler)req_get_console_output_info, (req_handler)req_write_console_input, (req_handler)req_read_console_input, + (req_handler)req_write_console_output, + (req_handler)req_read_console_output, + (req_handler)req_move_console_output, (req_handler)req_create_change_notification, (req_handler)req_create_mapping, (req_handler)req_open_mapping, diff --git a/server/trace.c b/server/trace.c index 2d144cf1120..df4617a313a 100644 --- a/server/trace.c +++ b/server/trace.c @@ -721,6 +721,7 @@ static void dump_create_file_reply( const struct create_file_request *req ) static void dump_alloc_file_handle_request( const struct alloc_file_handle_request *req ) { fprintf( stderr, " access=%08x,", req->access ); + fprintf( stderr, " inherit=%d,", req->inherit ); fprintf( stderr, " fd=%d", req->fd ); } @@ -881,24 +882,37 @@ static void dump_enable_socket_event_request( const struct enable_socket_event_r static void dump_alloc_console_request( const struct alloc_console_request *req ) { fprintf( stderr, " access=%08x,", req->access ); - fprintf( stderr, " inherit=%d", req->inherit ); + fprintf( stderr, " inherit=%d,", req->inherit ); + fprintf( stderr, " pid=%p", req->pid ); } static void dump_alloc_console_reply( const struct alloc_console_request *req ) { fprintf( stderr, " handle_in=%d,", req->handle_in ); - fprintf( stderr, " handle_out=%d", req->handle_out ); + fprintf( stderr, " event=%d", req->event ); } static void dump_free_console_request( const struct free_console_request *req ) { } +static void dump_get_console_renderer_events_request( const struct get_console_renderer_events_request *req ) +{ + fprintf( stderr, " handle=%d", req->handle ); +} + +static void dump_get_console_renderer_events_reply( const struct get_console_renderer_events_request *req ) +{ + fprintf( stderr, " data=" ); + cur_pos += dump_varargs_bytes( req ); +} + static void dump_open_console_request( const struct open_console_request *req ) { - fprintf( stderr, " output=%d,", req->output ); + fprintf( stderr, " from=%d,", req->from ); fprintf( stderr, " access=%08x,", req->access ); - fprintf( stderr, " inherit=%d", req->inherit ); + fprintf( stderr, " inherit=%d,", req->inherit ); + fprintf( stderr, " share=%d", req->share ); } static void dump_open_console_reply( const struct open_console_request *req ) @@ -906,14 +920,6 @@ static void dump_open_console_reply( const struct open_console_request *req ) fprintf( stderr, " handle=%d", req->handle ); } -static void dump_set_console_fd_request( const struct set_console_fd_request *req ) -{ - fprintf( stderr, " handle=%d,", req->handle ); - fprintf( stderr, " fd_in=%d,", req->fd_in ); - fprintf( stderr, " fd_out=%d,", req->fd_out ); - fprintf( stderr, " pid=%d", req->pid ); -} - static void dump_get_console_mode_request( const struct get_console_mode_request *req ) { fprintf( stderr, " handle=%d", req->handle ); @@ -930,28 +936,102 @@ static void dump_set_console_mode_request( const struct set_console_mode_request fprintf( stderr, " mode=%d", req->mode ); } -static void dump_set_console_info_request( const struct set_console_info_request *req ) +static void dump_set_console_input_info_request( const struct set_console_input_info_request *req ) +{ + fprintf( stderr, " handle=%d,", req->handle ); + fprintf( stderr, " mask=%d,", req->mask ); + fprintf( stderr, " active_sb=%d,", req->active_sb ); + fprintf( stderr, " history_mode=%d,", req->history_mode ); + fprintf( stderr, " history_size=%d,", req->history_size ); + fprintf( stderr, " title=" ); + cur_pos += dump_varargs_unicode_str( req ); +} + +static void dump_get_console_input_info_request( const struct get_console_input_info_request *req ) +{ + fprintf( stderr, " handle=%d", req->handle ); +} + +static void dump_get_console_input_info_reply( const struct get_console_input_info_request *req ) +{ + fprintf( stderr, " history_mode=%d,", req->history_mode ); + fprintf( stderr, " history_size=%d,", req->history_size ); + fprintf( stderr, " history_index=%d,", req->history_index ); + fprintf( stderr, " title=" ); + cur_pos += dump_varargs_unicode_str( req ); +} + +static void dump_append_console_input_history_request( const struct append_console_input_history_request *req ) +{ + fprintf( stderr, " handle=%d,", req->handle ); + fprintf( stderr, " line=" ); + cur_pos += dump_varargs_unicode_str( req ); +} + +static void dump_get_console_input_history_request( const struct get_console_input_history_request *req ) +{ + fprintf( stderr, " handle=%d,", req->handle ); + fprintf( stderr, " index=%d", req->index ); +} + +static void dump_get_console_input_history_reply( const struct get_console_input_history_request *req ) +{ + fprintf( stderr, " line=" ); + cur_pos += dump_varargs_unicode_str( req ); +} + +static void dump_create_console_output_request( const struct create_console_output_request *req ) +{ + fprintf( stderr, " handle_in=%d,", req->handle_in ); + fprintf( stderr, " access=%d,", req->access ); + fprintf( stderr, " share=%d,", req->share ); + fprintf( stderr, " inherit=%d", req->inherit ); +} + +static void dump_create_console_output_reply( const struct create_console_output_request *req ) +{ + fprintf( stderr, " handle_out=%d", req->handle_out ); +} + +static void dump_set_console_output_info_request( const struct set_console_output_info_request *req ) { fprintf( stderr, " handle=%d,", req->handle ); fprintf( stderr, " mask=%d,", req->mask ); fprintf( stderr, " cursor_size=%d,", req->cursor_size ); fprintf( stderr, " cursor_visible=%d,", req->cursor_visible ); - fprintf( stderr, " title=" ); - cur_pos += dump_varargs_string( req ); + fprintf( stderr, " cursor_x=%d,", req->cursor_x ); + fprintf( stderr, " cursor_y=%d,", req->cursor_y ); + fprintf( stderr, " width=%d,", req->width ); + fprintf( stderr, " height=%d,", req->height ); + fprintf( stderr, " attr=%d,", req->attr ); + fprintf( stderr, " win_left=%d,", req->win_left ); + fprintf( stderr, " win_top=%d,", req->win_top ); + fprintf( stderr, " win_right=%d,", req->win_right ); + fprintf( stderr, " win_bottom=%d,", req->win_bottom ); + fprintf( stderr, " max_width=%d,", req->max_width ); + fprintf( stderr, " max_height=%d", req->max_height ); } -static void dump_get_console_info_request( const struct get_console_info_request *req ) +static void dump_get_console_output_info_request( const struct get_console_output_info_request *req ) { fprintf( stderr, " handle=%d", req->handle ); } -static void dump_get_console_info_reply( const struct get_console_info_request *req ) +static void dump_get_console_output_info_reply( const struct get_console_output_info_request *req ) { fprintf( stderr, " cursor_size=%d,", req->cursor_size ); fprintf( stderr, " cursor_visible=%d,", req->cursor_visible ); - fprintf( stderr, " pid=%d,", req->pid ); - fprintf( stderr, " title=" ); - cur_pos += dump_varargs_string( req ); + fprintf( stderr, " cursor_x=%d,", req->cursor_x ); + fprintf( stderr, " cursor_y=%d,", req->cursor_y ); + fprintf( stderr, " width=%d,", req->width ); + fprintf( stderr, " height=%d,", req->height ); + fprintf( stderr, " attr=%d,", req->attr ); + fprintf( stderr, " win_left=%d,", req->win_left ); + fprintf( stderr, " win_top=%d,", req->win_top ); + fprintf( stderr, " win_right=%d,", req->win_right ); + fprintf( stderr, " win_bottom=%d,", req->win_bottom ); + fprintf( stderr, " max_width=%d,", req->max_width ); + fprintf( stderr, " max_height=%d", req->max_height ); } static void dump_write_console_input_request( const struct write_console_input_request *req ) @@ -979,6 +1059,49 @@ static void dump_read_console_input_reply( const struct read_console_input_reque cur_pos += dump_varargs_input_records( req ); } +static void dump_write_console_output_request( const struct write_console_output_request *req ) +{ + fprintf( stderr, " handle=%d,", req->handle ); + fprintf( stderr, " mode=%d,", req->mode ); + fprintf( stderr, " x=%d,", req->x ); + fprintf( stderr, " y=%d,", req->y ); + fprintf( stderr, " data=" ); + cur_pos += dump_varargs_bytes( req ); +} + +static void dump_write_console_output_reply( const struct write_console_output_request *req ) +{ + fprintf( stderr, " written=%d", req->written ); +} + +static void dump_read_console_output_request( const struct read_console_output_request *req ) +{ + fprintf( stderr, " handle=%d,", req->handle ); + fprintf( stderr, " x=%d,", req->x ); + fprintf( stderr, " y=%d,", req->y ); + fprintf( stderr, " w=%d,", req->w ); + fprintf( stderr, " h=%d", req->h ); +} + +static void dump_read_console_output_reply( const struct read_console_output_request *req ) +{ + fprintf( stderr, " eff_w=%d,", req->eff_w ); + fprintf( stderr, " eff_h=%d,", req->eff_h ); + fprintf( stderr, " data=" ); + cur_pos += dump_varargs_bytes( req ); +} + +static void dump_move_console_output_request( const struct move_console_output_request *req ) +{ + fprintf( stderr, " handle=%d,", req->handle ); + fprintf( stderr, " x_src=%d,", req->x_src ); + fprintf( stderr, " y_src=%d,", req->y_src ); + fprintf( stderr, " x_dst=%d,", req->x_dst ); + fprintf( stderr, " y_dst=%d,", req->y_dst ); + fprintf( stderr, " w=%d,", req->w ); + fprintf( stderr, " h=%d", req->h ); +} + static void dump_create_change_notification_request( const struct create_change_notification_request *req ) { fprintf( stderr, " subtree=%d,", req->subtree ); @@ -1955,14 +2078,22 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_enable_socket_event_request, (dump_func)dump_alloc_console_request, (dump_func)dump_free_console_request, + (dump_func)dump_get_console_renderer_events_request, (dump_func)dump_open_console_request, - (dump_func)dump_set_console_fd_request, (dump_func)dump_get_console_mode_request, (dump_func)dump_set_console_mode_request, - (dump_func)dump_set_console_info_request, - (dump_func)dump_get_console_info_request, + (dump_func)dump_set_console_input_info_request, + (dump_func)dump_get_console_input_info_request, + (dump_func)dump_append_console_input_history_request, + (dump_func)dump_get_console_input_history_request, + (dump_func)dump_create_console_output_request, + (dump_func)dump_set_console_output_info_request, + (dump_func)dump_get_console_output_info_request, (dump_func)dump_write_console_input_request, (dump_func)dump_read_console_input_request, + (dump_func)dump_write_console_output_request, + (dump_func)dump_read_console_output_request, + (dump_func)dump_move_console_output_request, (dump_func)dump_create_change_notification_request, (dump_func)dump_create_mapping_request, (dump_func)dump_open_mapping_request, @@ -2098,14 +2229,22 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { (dump_func)0, (dump_func)dump_alloc_console_reply, (dump_func)0, + (dump_func)dump_get_console_renderer_events_reply, (dump_func)dump_open_console_reply, - (dump_func)0, (dump_func)dump_get_console_mode_reply, (dump_func)0, (dump_func)0, - (dump_func)dump_get_console_info_reply, + (dump_func)dump_get_console_input_info_reply, + (dump_func)0, + (dump_func)dump_get_console_input_history_reply, + (dump_func)dump_create_console_output_reply, + (dump_func)0, + (dump_func)dump_get_console_output_info_reply, (dump_func)dump_write_console_input_reply, (dump_func)dump_read_console_input_reply, + (dump_func)dump_write_console_output_reply, + (dump_func)dump_read_console_output_reply, + (dump_func)0, (dump_func)dump_create_change_notification_reply, (dump_func)dump_create_mapping_reply, (dump_func)dump_open_mapping_reply, @@ -2241,14 +2380,22 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "enable_socket_event", "alloc_console", "free_console", + "get_console_renderer_events", "open_console", - "set_console_fd", "get_console_mode", "set_console_mode", - "set_console_info", - "get_console_info", + "set_console_input_info", + "get_console_input_info", + "append_console_input_history", + "get_console_input_history", + "create_console_output", + "set_console_output_info", + "get_console_output_info", "write_console_input", "read_console_input", + "write_console_output", + "read_console_output", + "move_console_output", "create_change_notification", "create_mapping", "open_mapping", diff --git a/tools/make_requests b/tools/make_requests index 3e2c536635e..ed3c59ecf26 100755 --- a/tools/make_requests +++ b/tools/make_requests @@ -9,6 +9,7 @@ %formats = ( "int" => "%d", + "short int" => "%d", "char" => "%c", "unsigned char" => "%02x", "unsigned short"=> "%04x", diff --git a/win32/Makefile.in b/win32/Makefile.in index 42c69a40cb3..890c2e745af 100644 --- a/win32/Makefile.in +++ b/win32/Makefile.in @@ -1,4 +1,4 @@ -DEFS = @DLLFLAGS@ -D__WINE__ +DEFS = @DLLFLAGS@ -D__WINE__ -DBINDIR="\"$(bindir)\"" TOPSRCDIR = @top_srcdir@ TOPOBJDIR = .. SRCDIR = @srcdir@ @@ -8,6 +8,7 @@ MODULE = win32 C_SRCS = \ console.c \ device.c \ + editline.c \ except.c \ file.c \ init.c \ diff --git a/win32/console.c b/win32/console.c index 2734e3f55a9..9544d6c03d5 100644 --- a/win32/console.c +++ b/win32/console.c @@ -5,13 +5,9 @@ * Copyright 1997 Karl Garrison * Copyright 1998 John Richardson * Copyright 1998 Marcus Meissner + * Copyright 2001 Eric Pouech */ -/* 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. @@ -19,302 +15,221 @@ #include "config.h" -#include #include -#include -#include #include -#include -#include -#include -#include -#include -#ifdef HAVE_SYS_ERRNO_H -#include -#endif -#include +#include #include -#include "windef.h" #include "winbase.h" #include "winnls.h" -#include "wingdi.h" -#include "wine/winuser16.h" -#include "thread.h" #include "winerror.h" #include "wincon.h" #include "heap.h" #include "wine/server.h" +#include "wine/exception.h" #include "debugtools.h" +#include "options.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 -}; +/* editline.c */ +extern WCHAR* CONSOLE_Readline(HANDLE, int); -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 WCHAR* S_EditString /* = NULL */; +static unsigned S_EditStrPos /* = 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_make_complex. */ -extern int wine_openpty(int *master, int *slave, char *name, - struct termios *term, struct winsize *winsize); - -/**************************************************************************** - * CONSOLE_GetPid +/*********************************************************************** + * FreeConsole (KERNEL32.@) */ -static int CONSOLE_GetPid( HANDLE handle ) +BOOL WINAPI FreeConsole(VOID) { - int ret = 0; - SERVER_START_REQ( get_console_info ) + BOOL ret; + + SERVER_START_REQ(free_console) { - req->handle = handle; - if (!SERVER_CALL_ERR()) ret = req->pid; + ret = !SERVER_CALL_ERR(); } SERVER_END_REQ; return ret; } -/**************************************************************************** - * XTERM_string_to_IR [internal] +/****************************************************************** + * start_console_renderer * - * 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. + * helper for AllocConsole + * starts the renderer process */ -static void -CONSOLE_string_to_IR( HANDLE hConsoleInput,unsigned char *buf,int len) { - int j,k; - INPUT_RECORD ir; - DWORD junk; +static BOOL start_console_renderer(void) +{ + char buffer[256]; + STARTUPINFOA si; + PROCESS_INFORMATION pi; + HANDLE hEvent = 0; + LPSTR p, path = NULL; + OBJECT_ATTRIBUTES attr; - for (j=0;j 'normal' keyboard event */ - ir.EventType = 1; /* Key_event */ + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); - 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 (k 0 && + CreateProcessA(NULL, buffer, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &si, &pi)) + goto succeed; + ERR("Couldn't launch Wine console from WINECONSOLE env var... trying default access\n"); } + + /* then the regular installation dir */ + if (snprintf(buffer, sizeof(buffer), "%s %d", BINDIR "/wineconsole", hEvent) > 0 && + CreateProcessA(NULL, buffer, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &si, &pi)) + goto succeed; + + /* then try the dir where we were started from */ + if ((path = HeapAlloc(GetProcessHeap(), 0, strlen(full_argv0) + sizeof(buffer)))) + { + int n; + + if ((p = strrchr(strcpy( path, full_argv0 ), '/'))) + { + p++; + sprintf(p, "wineconsole %d", hEvent); + if (CreateProcessA(NULL, path, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &si, &pi)) + goto succeed; + sprintf(p, "programs/wineconsole/wineconsole %d", hEvent); + if (CreateProcessA(NULL, path, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &si, &pi)) + goto succeed; + } + + n = readlink(full_argv0, buffer, sizeof(buffer)); + if (n != -1 && n < sizeof(buffer)) + { + buffer[n] = 0; + if (buffer[0] == '/') /* absolute path ? */ + strcpy(path, buffer); + else if ((p = strrchr(strcpy( path, full_argv0 ), '/'))) + { + strcpy(p + 1, buffer); + } + else *path = 0; + + if ((p = strrchr(path, '/'))) + { + p++; + sprintf(p, "wineconsole %d", hEvent); + if (CreateProcessA(NULL, path, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &si, &pi)) + goto succeed; + sprintf(p, "programs/wineconsole/wineconsole %d", hEvent); + if (CreateProcessA(NULL, path, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &si, &pi)) + goto succeed; + } + } else perror("readlink"); + + HeapFree(GetProcessHeap(), 0, path); path = NULL; + } + + /* then try the regular PATH */ + sprintf(buffer, "wineconsole %d\n", hEvent); + if (CreateProcessA(NULL, buffer, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &si, &pi)) + goto succeed; + + goto the_end; + + succeed: + if (path) HeapFree(GetProcessHeap(), 0, path); + if (WaitForSingleObject(hEvent, INFINITE) != WAIT_OBJECT_0) goto the_end; + CloseHandle(hEvent); + + TRACE("Started wineconsole pid=%08lx tid=%08lx\n", pi.dwProcessId, pi.dwThreadId); + + return TRUE; + + the_end: + ERR("Can't allocate console\n"); + if (path) HeapFree(GetProcessHeap(), 0, path); + CloseHandle(hEvent); + return FALSE; } -/**************************************************************************** - * CONSOLE_get_input (internal) +/*********************************************************************** + * AllocConsole (KERNEL32.@) * - * Reads (nonblocking) as much input events as possible and stores them - * in an internal queue. + * creates an xterm with a pty to our program */ -static void -CONSOLE_get_input( HANDLE handle, BOOL blockwait ) +BOOL WINAPI AllocConsole(void) { - char *buf = HeapAlloc(GetProcessHeap(),0,1); - int len = 0, escape_seen = 0; + HANDLE handle_in = INVALID_HANDLE_VALUE; + HANDLE handle_out = INVALID_HANDLE_VALUE; + HANDLE handle_err = INVALID_HANDLE_VALUE; + STARTUPINFOA si; - while (1) + TRACE("()\n"); + + handle_in = CreateFileA( "CONIN$", GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE, + 0, NULL, OPEN_EXISTING, 0, 0 ); + + if (handle_in != INVALID_HANDLE_VALUE) { - DWORD res; - char inchar; - - /* If we have at one time seen escape in this loop, we are - * within an Escape sequence, so wait for a bit more input for the - * rest of the loop. - */ - if (WaitForSingleObject( handle, escape_seen*10 )) break; - if (!ReadFile( handle, &inchar, 1, &res, NULL )) break; - if (!res) /* res 0 but readable means EOF? Hmm. */ - break; - buf = HeapReAlloc(GetProcessHeap(),0,buf,len+1); - buf[len++]=inchar; - if (inchar == 27) { - if (len>1) { - /* 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; - } + /* we already have a console opened on this process, don't create a new one */ + CloseHandle(handle_in); + return FALSE; } - CONSOLE_string_to_IR(handle,buf,len); - HeapFree(GetProcessHeap(),0,buf); + + if (!start_console_renderer()) + goto the_end; + + handle_in = CreateFileA( "CONIN$", GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE, + 0, NULL, OPEN_EXISTING, 0, 0 ); + if (handle_in == INVALID_HANDLE_VALUE) goto the_end; + + handle_out = CreateFileA( "CONOUT$", GENERIC_READ|GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, 0, 0 ); + if (handle_out == INVALID_HANDLE_VALUE) goto the_end; + + if (!DuplicateHandle(GetCurrentProcess(), handle_out, GetCurrentProcess(), &handle_err, + 0, TRUE, DUPLICATE_SAME_ACCESS)) + goto the_end; + + /* NT resets the STD_*_HANDLEs on console alloc */ + SetStdHandle(STD_INPUT_HANDLE, handle_in); + SetStdHandle(STD_OUTPUT_HANDLE, handle_out); + SetStdHandle(STD_ERROR_HANDLE, handle_err); + + GetStartupInfoA(&si); + if (si.dwFlags & STARTF_USESIZE) + { + COORD c; + c.X = si.dwXCountChars; + c.Y = si.dwYCountChars; + SetConsoleScreenBufferSize(handle_out, c); + } + if (si.dwFlags & STARTF_USEFILLATTRIBUTE) + SetConsoleTextAttribute(handle_out, si.dwFillAttribute); + if (si.lpTitle) + SetConsoleTitleA(si.lpTitle); + + SetLastError(ERROR_SUCCESS); + + return TRUE; + + the_end: + ERR("Can't allocate console\n"); + if (handle_in != INVALID_HANDLE_VALUE) CloseHandle(handle_in); + if (handle_out != INVALID_HANDLE_VALUE) CloseHandle(handle_out); + if (handle_err != INVALID_HANDLE_VALUE) CloseHandle(handle_err); + FreeConsole(); + return FALSE; } @@ -323,28 +238,344 @@ CONSOLE_get_input( HANDLE handle, BOOL blockwait ) * * Helper function for ReadConsole, ReadConsoleInput and PeekConsoleInput */ -static BOOL read_console_input( HANDLE handle, LPINPUT_RECORD buffer, DWORD count, - LPDWORD read, BOOL flush ) +static BOOL read_console_input(HANDLE handle, LPINPUT_RECORD buffer, DWORD count, + LPDWORD pRead, BOOL flush) { - BOOL ret; + BOOL ret; + unsigned read = 0; + DWORD mode; - count = min( count, REQUEST_MAX_VAR_SIZE/sizeof(INPUT_RECORD) ); - - SERVER_START_VAR_REQ( read_console_input, count*sizeof(INPUT_RECORD) ) + count = min(count, REQUEST_MAX_VAR_SIZE/sizeof(INPUT_RECORD)); + + SERVER_START_VAR_REQ(read_console_input, count*sizeof(INPUT_RECORD)) { req->handle = handle; req->flush = flush; if ((ret = !SERVER_CALL_ERR())) { - if (count) memcpy( buffer, server_data_ptr(req), server_data_size(req) ); - if (read) *read = req->read; + if (count) memcpy(buffer, server_data_ptr(req), server_data_size(req)); + read = req->read; } } SERVER_END_VAR_REQ; + if (count && flush && GetConsoleMode(handle, &mode) && (mode & ENABLE_PROCESSED_INPUT)) + { + int i; + + for (i = 0; i < read; i++) + { + if (buffer[i].EventType == KEY_EVENT && buffer[i].Event.KeyEvent.bKeyDown && + buffer[i].Event.KeyEvent.uChar.UnicodeChar == 'C' - 64 && + !(buffer[i].Event.KeyEvent.dwControlKeyState & ENHANCED_KEY)) + { + GenerateConsoleCtrlEvent(CTRL_C_EVENT, GetCurrentProcessId()); + /* FIXME: this is hackish, but it easily disables IR handling afterwards */ + buffer[i].Event.KeyEvent.uChar.UnicodeChar = 0; + } + } + } + if (pRead) *pRead = read; return ret; } +/*********************************************************************** + * ReadConsoleA (KERNEL32.@) + */ +BOOL WINAPI ReadConsoleA(HANDLE hConsoleInput, LPVOID lpBuffer, DWORD nNumberOfCharsToRead, + LPDWORD lpNumberOfCharsRead, LPVOID lpReserved) +{ + LPWSTR ptr = HeapAlloc(GetProcessHeap(), 0, nNumberOfCharsToRead * sizeof(WCHAR)); + DWORD ncr = 0; + BOOL ret; + + if ((ret = ReadConsoleW(hConsoleInput, ptr, nNumberOfCharsToRead, &ncr, 0))) + ncr = WideCharToMultiByte(CP_ACP, 0, ptr, ncr, lpBuffer, nNumberOfCharsToRead, NULL, NULL); + + if (lpNumberOfCharsRead) *lpNumberOfCharsRead = ncr; + HeapFree(GetProcessHeap(), 0, ptr); + + return ret; +} + +/*********************************************************************** + * ReadConsoleW (KERNEL32.@) + */ +BOOL WINAPI ReadConsoleW(HANDLE hConsoleInput, LPVOID lpBuffer, + DWORD nNumberOfCharsToRead, LPDWORD lpNumberOfCharsRead, LPVOID lpReserved) +{ + DWORD charsread; + LPWSTR xbuf = (LPWSTR)lpBuffer; + DWORD mode; + + TRACE("(%d,%p,%ld,%p,%p)\n", + hConsoleInput, lpBuffer, nNumberOfCharsToRead, lpNumberOfCharsRead, lpReserved); + + if (!GetConsoleMode(hConsoleInput, &mode)) + return FALSE; + + if (mode & ENABLE_LINE_INPUT) + { + if (!S_EditString || S_EditString[S_EditStrPos] == 0) + { + if (S_EditString) HeapFree(GetProcessHeap(), 0, S_EditString); + if (!(S_EditString = CONSOLE_Readline(hConsoleInput, mode & WINE_ENABLE_LINE_INPUT_EMACS))) + return FALSE; + S_EditStrPos = 0; + } + charsread = lstrlenW(&S_EditString[S_EditStrPos]); + if (charsread > nNumberOfCharsToRead) charsread = nNumberOfCharsToRead; + memcpy(xbuf, &S_EditString[S_EditStrPos], charsread * sizeof(WCHAR)); + S_EditStrPos += charsread; + } + else + { + INPUT_RECORD ir; + DWORD count; + + /* FIXME: should we read at least 1 char? The SDK does not say */ + /* wait for at least one available input record (it doesn't mean we'll have + * chars stored in xbuf... + */ + WaitForSingleObject(hConsoleInput, INFINITE); + for (charsread = 0; charsread < nNumberOfCharsToRead;) + { + if (!read_console_input(hConsoleInput, &ir, 1, &count, TRUE)) return FALSE; + if (count && ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown && + ir.Event.KeyEvent.uChar.UnicodeChar && + !(ir.Event.KeyEvent.dwControlKeyState & ENHANCED_KEY)) + { + xbuf[charsread++] = ir.Event.KeyEvent.uChar.UnicodeChar; + } + } + } + + if (lpNumberOfCharsRead) *lpNumberOfCharsRead = charsread; + + return TRUE; +} + + +/****************************************************************************** + * ReadConsoleInputA [KERNEL32.@] Reads data from a console + * + * PARAMS + * hConsoleInput [I] Handle to console input buffer + * lpBuffer [O] Address of buffer for read data + * nLength [I] Number of records to read + * lpNumberOfEventsRead [O] Address of number of records read + * + * RETURNS + * Success: TRUE + * Failure: FALSE + */ +BOOL WINAPI ReadConsoleInputA(HANDLE hConsoleInput, LPINPUT_RECORD lpBuffer, + DWORD nLength, LPDWORD lpNumberOfEventsRead) +{ + DWORD nread; + + if (!ReadConsoleInputW(hConsoleInput, lpBuffer, nLength, &nread)) + return FALSE; + + /* FIXME for now, the low part of unicode would do as ASCII */ + if (lpNumberOfEventsRead) *lpNumberOfEventsRead = nread; + return TRUE; +} + + +/*********************************************************************** + * ReadConsoleInputW (KERNEL32.@) + */ +BOOL WINAPI ReadConsoleInputW(HANDLE hConsoleInput, LPINPUT_RECORD lpBuffer, + DWORD nLength, LPDWORD lpNumberOfEventsRead) +{ + DWORD count; + + if (!nLength) + { + if (lpNumberOfEventsRead) *lpNumberOfEventsRead = 0; + return TRUE; + } + + /* loop until we get at least one event */ + for (;;) + { + WaitForSingleObject(hConsoleInput, INFINITE); + if (!read_console_input(hConsoleInput, lpBuffer, nLength, &count, TRUE)) + return FALSE; + if (count) + { + if (lpNumberOfEventsRead) *lpNumberOfEventsRead = count; + return TRUE; + } + } +} + + +/*********************************************************************** + * FlushConsoleInputBuffer (KERNEL32.@) + */ +BOOL WINAPI FlushConsoleInputBuffer(HANDLE handle) +{ + return read_console_input(handle, NULL, 0, NULL, TRUE); +} + + +/*********************************************************************** + * PeekConsoleInputA (KERNEL32.@) + * + * Gets 'count' first events (or less) from input queue. + * + * Does not need a complex console. + */ +BOOL WINAPI PeekConsoleInputA(HANDLE hConsoleInput, LPINPUT_RECORD pirBuffer, + DWORD cInRecords, LPDWORD lpcRead) +{ + /* FIXME: Hmm. Fix this if we get UNICODE input. */ + return PeekConsoleInputW(hConsoleInput, pirBuffer, cInRecords, lpcRead); +} + + +/*********************************************************************** + * PeekConsoleInputW (KERNEL32.@) + */ +BOOL WINAPI PeekConsoleInputW(HANDLE hConsoleInput, LPINPUT_RECORD pirBuffer, + DWORD cInRecords, LPDWORD lpcRead) +{ + if (!cInRecords) + { + if (lpcRead) *lpcRead = 0; + return TRUE; + } + return read_console_input(hConsoleInput, pirBuffer, cInRecords, lpcRead, FALSE); +} + + +/*********************************************************************** + * GetNumberOfConsoleInputEvents (KERNEL32.@) + */ +BOOL WINAPI GetNumberOfConsoleInputEvents(HANDLE hcon, LPDWORD nrofevents) +{ + return read_console_input(hcon, NULL, 0, nrofevents, FALSE); +} + +/*********************************************************************** + * GetNumberOfConsoleMouseButtons (KERNEL32.@) + */ +BOOL WINAPI GetNumberOfConsoleMouseButtons(LPDWORD nrofbuttons) +{ + FIXME("(%p): stub\n", nrofbuttons); + *nrofbuttons = 2; + return TRUE; +} + +/****************************************************************************** + * WriteConsoleInputA [KERNEL32.@] Write data to a console input buffer + * + */ +BOOL WINAPI WriteConsoleInputA(HANDLE handle, INPUT_RECORD *buffer, + DWORD count, LPDWORD written) +{ + BOOL ret = TRUE; + + if (written) *written = 0; + /* FIXME should zero out the non ASCII part for key events */ + + while (count && ret) + { + DWORD len = min(count, REQUEST_MAX_VAR_SIZE/sizeof(INPUT_RECORD)); + SERVER_START_VAR_REQ(write_console_input, len * sizeof(INPUT_RECORD)) + { + req->handle = handle; + memcpy(server_data_ptr(req), buffer, len * sizeof(INPUT_RECORD)); + if ((ret = !SERVER_CALL_ERR())) + { + if (written) *written += req->written; + count -= len; + buffer += len; + } + } + SERVER_END_VAR_REQ; + } + return ret; +} + +/****************************************************************************** + * WriteConsoleInputW [KERNEL32.@] Write data to a console input buffer + * + */ +BOOL WINAPI WriteConsoleInputW(HANDLE handle, INPUT_RECORD *buffer, + DWORD count, LPDWORD written) +{ + BOOL ret = TRUE; + + TRACE("(%d,%p,%ld,%p)\n", handle, buffer, count, written); + + if (written) *written = 0; + while (count && ret) + { + DWORD len = min(count, REQUEST_MAX_VAR_SIZE/sizeof(INPUT_RECORD)); + SERVER_START_VAR_REQ(write_console_input, len * sizeof(INPUT_RECORD)) + { + req->handle = handle; + memcpy(server_data_ptr(req), buffer, len * sizeof(INPUT_RECORD)); + if ((ret = !SERVER_CALL_ERR())) + { + if (written) *written += req->written; + count -= len; + buffer += len; + } + } + SERVER_END_VAR_REQ; + } + + return ret; +} + + +/****************************************************************************** + * SetConsoleInputExeNameW [KERNEL32.@] + * + * BUGS + * Unimplemented + */ +BOOL WINAPI SetConsoleInputExeNameW(LPCWSTR name) +{ + FIXME("(%s): stub!\n", debugstr_w(name)); + + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return TRUE; +} + +/****************************************************************************** + * SetConsoleInputExeNameA [KERNEL32.@] + * + * BUGS + * Unimplemented + */ +BOOL WINAPI SetConsoleInputExeNameA(LPCSTR name) +{ + int len = MultiByteToWideChar(CP_ACP, 0, name, -1, NULL, 0); + LPWSTR xptr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + BOOL ret; + + if (!xptr) return FALSE; + + MultiByteToWideChar(CP_ACP, 0, name, -1, xptr, len); + ret = SetConsoleInputExeNameW(xptr); + HeapFree(GetProcessHeap(), 0, xptr); + + return ret; +} + +static BOOL WINAPI CONSOLE_DefaultHandler(DWORD dwCtrlType) +{ + FIXME("Terminating process %lx on event %lx\n", GetCurrentProcessId(), dwCtrlType); + ExitProcess(0); + /* should never go here */ + return TRUE; +} + /****************************************************************************** * SetConsoleCtrlHandler [KERNEL32.@] Adds function to calling process list * @@ -362,46 +593,56 @@ static BOOL read_console_input( HANDLE handle, LPINPUT_RECORD buffer, DWORD coun * 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 PHANDLER_ROUTINE handlers[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; -BOOL WINAPI SetConsoleCtrlHandler( PHANDLER_ROUTINE func, BOOL add ) +static unsigned int console_ignore_ctrl_c = 0; /* FIXME: this should be inherited somehow */ +static PHANDLER_ROUTINE handlers[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,CONSOLE_DefaultHandler}; + +BOOL WINAPI SetConsoleCtrlHandler(PHANDLER_ROUTINE func, BOOL add) { - unsigned int alloc_loop = sizeof(handlers)/sizeof(PHANDLER_ROUTINE); - unsigned int done = 0; - FIXME("(%p,%i) - no error checking or testing yet\n", func, add); - if (!func) + int alloc_loop = sizeof(handlers)/sizeof(handlers[0]) - 1; + + FIXME("(%p,%i) - no error checking or testing yet\n", func, add); + + if (!func) { - console_ignore_ctrl_c = add; - return TRUE; + console_ignore_ctrl_c = add; + return TRUE; + } + if (add) + { + for (; alloc_loop >= 0 && handlers[alloc_loop]; alloc_loop--); + if (alloc_loop <= 0) + { + FIXME("Out of space on CtrlHandler table\n"); + return FALSE; + } + handlers[alloc_loop] = func; } - 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); + { + for (; alloc_loop >= 0 && handlers[alloc_loop] != func; alloc_loop--); + if (alloc_loop <= 0) + { + WARN("Attempt to remove non-installed CtrlHandler %p\n", func); + return FALSE; + } + /* sanity check */ + if (alloc_loop == sizeof(handlers)/sizeof(handlers[0]) - 1) + { + ERR("Who's trying to remove default handler???\n"); + return FALSE; + } + if (alloc_loop) + memmove(&handlers[1], &handlers[0], alloc_loop * sizeof(handlers[0])); + handlers[0] = 0; + } + return TRUE; } +static WINE_EXCEPTION_FILTER(CONSOLE_CtrlEventHandler) +{ + TRACE("(%lx)\n", GetExceptionCode()); + return EXCEPTION_EXECUTE_HANDLER; +} /****************************************************************************** * GenerateConsoleCtrlEvent [KERNEL32.@] Simulate a CTRL-C or CTRL-BREAK @@ -417,24 +658,69 @@ BOOL WINAPI SetConsoleCtrlHandler( PHANDLER_ROUTINE func, BOOL add ) * Success: True * Failure: False (and *should* [but doesn't] set LastError) */ -BOOL WINAPI GenerateConsoleCtrlEvent( DWORD dwCtrlEvent, - DWORD dwProcessGroupID ) +BOOL WINAPI GenerateConsoleCtrlEvent(DWORD dwCtrlEvent, + DWORD dwProcessGroupID) { - if (dwCtrlEvent != CTRL_C_EVENT && dwCtrlEvent != CTRL_BREAK_EVENT) + BOOL dbgOn = FALSE; + + if (dwCtrlEvent != CTRL_C_EVENT && dwCtrlEvent != CTRL_BREAK_EVENT) { - ERR("invalid event %d for PGID %ld\n", - (unsigned short)dwCtrlEvent, dwProcessGroupID ); - return FALSE; + ERR("invalid event %ld for PGID %ld\n", dwCtrlEvent, dwProcessGroupID); + return FALSE; } - if (dwProcessGroupID == GetCurrentProcessId() ) + + if (dwProcessGroupID == GetCurrentProcessId() || dwProcessGroupID == 0) { - FIXME("Attempt to send event %d to self - stub\n", - (unsigned short)dwCtrlEvent ); - return FALSE; + int i; + + FIXME("Attempt to send event %ld to self groupID, doing locally only\n", dwCtrlEvent); + + /* this is only meaningfull when done locally, otherwise it will have to be done on + * the 'receive' side of the event generation + */ + if (dwCtrlEvent == CTRL_C_EVENT && console_ignore_ctrl_c) + return TRUE; + + /* if the program is debugged, then generate an exception to the debugger first */ + SERVER_START_REQ( get_process_info ) + { + req->handle = GetCurrentProcess(); + if (!SERVER_CALL_ERR()) dbgOn = req->debugged; + } + SERVER_END_REQ; + + if (dbgOn && (dwCtrlEvent == CTRL_C_EVENT || dwCtrlEvent == CTRL_BREAK_EVENT)) + { + /* the debugger is running... so try to pass the exception to it + * if it continues, there's nothing more to do + * otherwise, we need to send the ctrl-event to the handlers + */ + BOOL seen; + __TRY + { + seen = TRUE; + RaiseException((dwCtrlEvent == CTRL_C_EVENT) ? DBG_CONTROL_C : DBG_CONTROL_BREAK, 0, 0, NULL); + } + __EXCEPT(CONSOLE_CtrlEventHandler) + { + /* the debugger didn't continue... so, pass to ctrl handlers */ + seen = FALSE; + } + __ENDTRY; + if (seen) return TRUE; + } + + /* proceed with installed handlers */ + for (i = 0; i < sizeof(handlers)/sizeof(handlers[0]); i++) + { + if (handlers[i] && (handlers[i])(dwCtrlEvent)) break; + } + + return TRUE; } - FIXME("event %d to external PGID %ld - not implemented yet\n", - (unsigned short)dwCtrlEvent, dwProcessGroupID ); - return FALSE; + FIXME("event %ld to external PGID %ld - not implemented yet\n", + dwCtrlEvent, dwProcessGroupID); + return FALSE; } @@ -455,35 +741,64 @@ BOOL WINAPI GenerateConsoleCtrlEvent( DWORD dwCtrlEvent, * 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 ) +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; + HANDLE ret = INVALID_HANDLE_VALUE; + + TRACE("(%ld,%ld,%p,%ld,%p)\n", + dwDesiredAccess, dwShareMode, sa, dwFlags, lpScreenBufferData); + + if (dwFlags != CONSOLE_TEXTMODE_BUFFER || lpScreenBufferData != NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return INVALID_HANDLE_VALUE; + } + + SERVER_START_REQ(create_console_output) + { + req->handle_in = 0; + req->access = dwDesiredAccess; + req->share = dwShareMode; + req->inherit = (sa && sa->bInheritHandle); + if (!SERVER_CALL_ERR()) + ret = req->handle_out; + } + SERVER_END_REQ; + + return ret; } /*********************************************************************** * GetConsoleScreenBufferInfo (KERNEL32.@) */ -BOOL WINAPI GetConsoleScreenBufferInfo( HANDLE hConsoleOutput, - LPCONSOLE_SCREEN_BUFFER_INFO csbi ) +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; + BOOL ret; + + SERVER_START_REQ(get_console_output_info) + { + req->handle = (handle_t)hConsoleOutput; + if ((ret = !SERVER_CALL_ERR())) + { + csbi->dwSize.X = req->width; + csbi->dwSize.Y = req->height; + csbi->dwCursorPosition.X = req->cursor_x; + csbi->dwCursorPosition.Y = req->cursor_y; + csbi->wAttributes = req->attr; + csbi->srWindow.Left = req->win_left; + csbi->srWindow.Right = req->win_right; + csbi->srWindow.Top = req->win_top; + csbi->srWindow.Bottom = req->win_bottom; + csbi->dwMaximumWindowSize.X = req->max_width; + csbi->dwMaximumWindowSize.Y = req->max_height; + } + } + SERVER_END_REQ; + + return ret; } @@ -494,11 +809,23 @@ BOOL WINAPI GetConsoleScreenBufferInfo( HANDLE hConsoleOutput, * Success: TRUE * Failure: FALSE */ -BOOL WINAPI SetConsoleActiveScreenBuffer( - HANDLE hConsoleOutput) /* [in] Handle to console screen buffer */ +BOOL WINAPI SetConsoleActiveScreenBuffer(HANDLE hConsoleOutput) { - FIXME("(%x): stub\n", hConsoleOutput); - return FALSE; + BOOL ret; + + TRACE("(%x)\n", hConsoleOutput); + + SERVER_START_VAR_REQ(set_console_input_info, 0) + { + req->handle = 0; + req->mask = SET_CONSOLE_INPUT_INFO_ACTIVE_SB; + req->active_sb = hConsoleOutput; + + ret = !SERVER_CALL_ERR(); + } + SERVER_END_VAR_REQ; + + return ret; } @@ -513,7 +840,7 @@ BOOL WINAPI SetConsoleActiveScreenBuffer( */ #ifdef __i386__ #undef GetLargestConsoleWindowSize -DWORD WINAPI GetLargestConsoleWindowSize( HANDLE hConsoleOutput ) +DWORD WINAPI GetLargestConsoleWindowSize(HANDLE hConsoleOutput) { COORD c; c.X = 80; @@ -533,7 +860,7 @@ DWORD WINAPI GetLargestConsoleWindowSize( HANDLE hConsoleOutput ) * VERSION: [!i386] */ #ifndef __i386__ -COORD WINAPI GetLargestConsoleWindowSize( HANDLE hConsoleOutput ) +COORD WINAPI GetLargestConsoleWindowSize(HANDLE hConsoleOutput) { COORD c; c.X = 80; @@ -543,156 +870,6 @@ COORD WINAPI GetLargestConsoleWindowSize( HANDLE hConsoleOutput ) #endif /* defined(__i386__) */ -/*********************************************************************** - * FreeConsole (KERNEL32.@) - */ -BOOL WINAPI FreeConsole(VOID) -{ - BOOL ret; - SERVER_START_REQ( free_console ) - { - ret = !SERVER_CALL_ERR(); - } - 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 debug output. - * - * All other functions should work independently 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; - - 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); - } - close( master ); - - /* 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++) - { - if (read( slave, &c, 1 ) == 1) - { - if (c == '\n') break; - } - else usleep(100); /* wait for xterm to be created */ - } - if (i == 10000) - { - ERR("can't read xterm WID\n"); - close( slave ); - return FALSE; - } - - wine_server_send_fd( slave ); - SERVER_START_REQ( set_console_fd ) - { - req->handle = handle; - req->fd_in = slave; - req->fd_out = slave; - req->pid = xpid; - SERVER_CALL(); - close( slave ); - } - SERVER_END_REQ; - - /* 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.@) - * - * 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( alloc_console ) - { - req->access = GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE; - req->inherit = FALSE; - ret = !SERVER_CALL_ERR(); - 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.@] Returns the OEM code page for the console * @@ -705,6 +882,20 @@ UINT WINAPI GetConsoleCP(VOID) } +/****************************************************************************** + * SetConsoleCP [KERNEL32.@] + * + * BUGS + * Unimplemented + */ +BOOL WINAPI SetConsoleCP(UINT cp) +{ + FIXME("(%d): stub\n", cp); + + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + /*********************************************************************** * GetConsoleOutputCP (KERNEL32.@) */ @@ -713,17 +904,35 @@ UINT WINAPI GetConsoleOutputCP(VOID) return GetConsoleCP(); } +/****************************************************************************** + * SetConsoleOutputCP [KERNEL32.@] 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; +} + + /*********************************************************************** * GetConsoleMode (KERNEL32.@) */ -BOOL WINAPI GetConsoleMode(HANDLE hcon,LPDWORD mode) +BOOL WINAPI GetConsoleMode(HANDLE hcon, LPDWORD mode) { BOOL ret; - SERVER_START_REQ( get_console_mode ) + + SERVER_START_REQ(get_console_mode) { - req->handle = hcon; - ret = !SERVER_CALL_ERR(); - if (ret && mode) *mode = req->mode; + req->handle = hcon; + ret = !SERVER_CALL_ERR(); + if (ret && mode) *mode = req->mode; } SERVER_END_REQ; return ret; @@ -741,466 +950,26 @@ BOOL WINAPI GetConsoleMode(HANDLE hcon,LPDWORD mode) * Success: TRUE * Failure: FALSE */ -BOOL WINAPI SetConsoleMode( HANDLE hcon, DWORD mode ) +BOOL WINAPI SetConsoleMode(HANDLE hcon, DWORD mode) { BOOL ret; - SERVER_START_REQ( set_console_mode ) + + TRACE("(%x,%lx)\n", hcon, mode); + + SERVER_START_REQ(set_console_mode) { - req->handle = hcon; - req->mode = mode; - ret = !SERVER_CALL_ERR(); + req->handle = hcon; + req->mode = mode; + ret = !SERVER_CALL_ERR(); } SERVER_END_REQ; + /* FIXME: when resetting a console input to editline mode, I think we should + * empty the S_EditString buffer + */ return ret; } -/****************************************************************************** - * SetConsoleOutputCP [KERNEL32.@] 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.@) - */ -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_VAR_REQ( get_console_info, REQUEST_MAX_VAR_SIZE ) - { - req->handle = hcon; - if (!SERVER_CALL_ERR()) - { - ret = server_data_size(req); - size = min( size-1, ret ); - memcpy( title, server_data_ptr(req), size ); - title[size] = 0; - } - } - SERVER_END_VAR_REQ; - CloseHandle( hcon ); - return ret; -} - - -/****************************************************************************** - * GetConsoleTitleW [KERNEL32.@] 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*sizeof(WCHAR) ))) return 0; - GetConsoleTitleA( tmp, size*sizeof(WCHAR) ); - ret = MultiByteToWideChar( CP_ACP, 0, tmp, -1, title, size ); - HeapFree( GetProcessHeap(), 0, tmp ); - return ret; -} - - -/*********************************************************************** - * WriteConsoleA (KERNEL32.@) - */ -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.@) - */ -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.@) - */ -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.@) - */ -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.@) - */ -BOOL WINAPI ReadConsoleA( HANDLE hConsoleInput, - LPVOID lpBuffer, - DWORD nNumberOfCharsToRead, - LPDWORD lpNumberOfCharsRead, - LPVOID lpReserved ) -{ - DWORD 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_ERR())) - { - if (written) *written += req->written; - count -= len; - buffer += len; - } - } - SERVER_END_VAR_REQ; - } - return ret; -} - -/****************************************************************************** - * WriteConsoleInputW [KERNEL32.@] 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.@) * @@ -1212,33 +981,18 @@ BOOL WINAPI WriteConsoleInputW( HANDLE handle, INPUT_RECORD *buffer, */ 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_VAR_REQ( set_console_info, len ) - { - req->handle = hcon; - req->mask = SET_CONSOLE_INFO_TITLE; - memcpy( server_data_ptr(req), title, len ); - ret = !SERVER_CALL_ERR(); - } - SERVER_END_VAR_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 ); + LPWSTR titleW = NULL; + BOOL ret; + DWORD len; + + len = MultiByteToWideChar(CP_ACP, 0, title, -1, NULL, 0); + titleW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + if (!titleW) return FALSE; + + MultiByteToWideChar(CP_ACP, 0, title, -1, titleW, len); + ret = SetConsoleTitleW(titleW); + + HeapFree(GetProcessHeap(), 0, titleW); return ret; } @@ -1256,13 +1010,364 @@ BOOL WINAPI SetConsoleTitleA(LPCSTR title) * Success: TRUE * Failure: FALSE */ -BOOL WINAPI SetConsoleTitleW( LPCWSTR title ) +BOOL WINAPI SetConsoleTitleW(LPCWSTR title) { - BOOL ret; + size_t len = strlenW(title) * sizeof(WCHAR); + BOOL ret; + + len = min(len, REQUEST_MAX_VAR_SIZE); + SERVER_START_VAR_REQ(set_console_input_info, len) + { + req->handle = 0; + req->mask = SET_CONSOLE_INPUT_INFO_TITLE; + memcpy(server_data_ptr(req), title, len); + ret = !SERVER_CALL_ERR(); + } + SERVER_END_VAR_REQ; + + return ret; +} + +/*********************************************************************** + * GetConsoleTitleA (KERNEL32.@) + */ +DWORD WINAPI GetConsoleTitleA(LPSTR title, DWORD size) +{ + WCHAR* ptr = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * size); + DWORD ret; + + if (!ptr) return 0; + + ret = GetConsoleTitleW(ptr, size); + if (ret) WideCharToMultiByte(CP_ACP, 0, ptr, ret + 1, title, size, NULL, NULL); + + return ret; +} + + +/****************************************************************************** + * GetConsoleTitleW [KERNEL32.@] 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) +{ + DWORD ret = 0; + + SERVER_START_VAR_REQ(get_console_input_info, REQUEST_MAX_VAR_SIZE) + { + req->handle = 0; + if (!SERVER_CALL_ERR()) + { + ret = server_data_size(req) / sizeof(WCHAR); + size = min(size - 1, ret); + memcpy(title, server_data_ptr(req), size * sizeof(WCHAR)); + title[size] = 0; + } + } + SERVER_END_VAR_REQ; + + return ret; +} + +/****************************************************************** + * write_char + * + * WriteConsoleOutput helper: hides server call semantics + */ +static int write_char(HANDLE hCon, LPCVOID lpBuffer, int nc, COORD* pos) +{ + BOOL ret; + int written = -1; + + if (!nc) return 0; + + assert(nc * sizeof(WCHAR) <= REQUEST_MAX_VAR_SIZE); + + SERVER_START_VAR_REQ(write_console_output, nc * sizeof(WCHAR)) + { + req->handle = hCon; + req->x = pos->X; + req->y = pos->Y; + req->mode = WRITE_CONSOLE_MODE_TEXTSTDATTR; + memcpy(server_data_ptr(req), lpBuffer, nc * sizeof(WCHAR)); + if ((ret = !SERVER_CALL_ERR())) + { + written = req->written; + } + } + SERVER_END_VAR_REQ; + + if (written > 0) pos->X += written; + + return written; +} + +/****************************************************************** + * next_line + * + * WriteConsoleOutput helper: handles passing to next line (+scrolling if necessary) + * + */ +static int next_line(HANDLE hCon, CONSOLE_SCREEN_BUFFER_INFO* csbi) +{ + SMALL_RECT src; + CHAR_INFO ci; + COORD dst; + + csbi->dwCursorPosition.X = 0; + csbi->dwCursorPosition.Y++; + + if (csbi->dwCursorPosition.Y < csbi->dwSize.Y) return 1; + + src.Top = 1; + src.Bottom = csbi->dwSize.Y - 1; + src.Left = 0; + src.Right = csbi->dwSize.X - 1; + + dst.X = 0; + dst.Y = 0; + + ci.Attributes = csbi->wAttributes; + ci.Char.UnicodeChar = ' '; + + csbi->dwCursorPosition.Y--; + if (!ScrollConsoleScreenBufferW(hCon, &src, NULL, dst, &ci)) + return 0; + return 1; +} + +/****************************************************************** + * write_block + * + * WriteConsoleOutput helper: writes a block of non special characters + * + */ +static int write_block(HANDLE hCon, CONSOLE_SCREEN_BUFFER_INFO* csbi, + DWORD mode, LPWSTR ptr, int len) +{ + int blk; /* number of chars to write on first line */ + + if (len <= 0) return 1; + + blk = min(len, csbi->dwSize.X - csbi->dwCursorPosition.X); + + if (write_char(hCon, ptr, blk, &csbi->dwCursorPosition) != blk) + return 0; + + if (blk < len) /* special handling for right border */ + { + if (mode & ENABLE_WRAP_AT_EOL_OUTPUT) /* writes remaining on next line */ + { + if (!next_line(hCon, csbi) || + write_char(hCon, ptr + blk, len - blk, &csbi->dwCursorPosition) != len - blk) + return 0; + } + else /* all remaining chars should be written on last column, so only write the last one */ + { + csbi->dwCursorPosition.X = csbi->dwSize.X - 1; + if (write_char(hCon, ptr + len - 1, 1, &csbi->dwCursorPosition) != 1) + return 0; + csbi->dwCursorPosition.X = csbi->dwSize.X - 1; + } + } + return 1; +} + +/*********************************************************************** + * WriteConsoleW (KERNEL32.@) + */ +BOOL WINAPI WriteConsoleW(HANDLE hConsoleOutput, LPCVOID lpBuffer, DWORD nNumberOfCharsToWrite, + LPDWORD lpNumberOfCharsWritten, LPVOID lpReserved) +{ + DWORD mode; + DWORD nw = 0; + WCHAR* psz = (WCHAR*)lpBuffer; + CONSOLE_SCREEN_BUFFER_INFO csbi; + int k, first = 0; + + TRACE("%d %s %ld %p %p\n", + hConsoleOutput, debugstr_wn(lpBuffer, nNumberOfCharsToWrite), + nNumberOfCharsToWrite, lpNumberOfCharsWritten, lpReserved); + + if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = 0; + + if (!GetConsoleMode(hConsoleOutput, &mode) || + !GetConsoleScreenBufferInfo(hConsoleOutput, &csbi)) + return FALSE; + + if (mode & ENABLE_PROCESSED_OUTPUT) + { + int i; + + for (i = 0; i < nNumberOfCharsToWrite; i++) + { + switch (psz[i]) + { + case '\b': case '\t': case '\n': case '\a': case '\r': + /* don't handle here the i-th char... done below */ + if ((k = i - first) > 0) + { + if (!write_block(hConsoleOutput, &csbi, mode, &psz[first], k)) + goto the_end; + nw += k; + } + first = i + 1; + nw++; + } + switch (psz[i]) + { + case '\b': + if (csbi.dwCursorPosition.X > 0) csbi.dwCursorPosition.X--; + break; + case '\t': + { + WCHAR tmp[8] = {' ',' ',' ',' ',' ',' ',' ',' '}; + + if (!write_block(hConsoleOutput, &csbi, mode, tmp, + ((csbi.dwCursorPosition.X + 8) & ~7) - csbi.dwCursorPosition.X)) + goto the_end; + } + break; + case '\n': + next_line(hConsoleOutput, &csbi); + break; + case '\a': + Beep(400, 300); + break; + case '\r': + csbi.dwCursorPosition.X = 0; + break; + default: + break; + } + } + } + + /* write the remaining block (if any) if processed output is enabled, or the + * entire buffer otherwise + */ + if ((k = nNumberOfCharsToWrite - first) > 0) + { + if (!write_block(hConsoleOutput, &csbi, mode, &psz[first], k)) + goto the_end; + nw += k; + } + + the_end: + SetConsoleCursorPosition(hConsoleOutput, csbi.dwCursorPosition); + if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = nw; + return nw != 0; +} + + +/*********************************************************************** + * WriteConsoleA (KERNEL32.@) + */ +BOOL WINAPI WriteConsoleA(HANDLE hConsoleOutput, LPCVOID lpBuffer, DWORD nNumberOfCharsToWrite, + LPDWORD lpNumberOfCharsWritten, LPVOID lpReserved) +{ + BOOL ret; + LPWSTR xstring; + DWORD n; + + n = MultiByteToWideChar(CP_ACP, 0, lpBuffer, nNumberOfCharsToWrite, NULL, 0); + + if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = 0; + xstring = HeapAlloc(GetProcessHeap(), 0, n * sizeof(WCHAR)); + if (!xstring) return 0; + + MultiByteToWideChar(CP_ACP, 0, lpBuffer, nNumberOfCharsToWrite, xstring, n); + + ret = WriteConsoleW(hConsoleOutput, xstring, n, lpNumberOfCharsWritten, 0); + + HeapFree(GetProcessHeap(), 0, xstring); + + return ret; +} + +/*********************************************************************** + * WriteConsoleOutputA (KERNEL32.@) + */ +BOOL WINAPI WriteConsoleOutputA(HANDLE hConsoleOutput, LPCHAR_INFO lpBuffer, COORD dwBufferSize, + COORD dwBufferCoord, LPSMALL_RECT lpWriteRegion) +{ + CHAR_INFO *ciw; + int i; + BOOL ret; + + ciw = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR_INFO) * dwBufferSize.X * dwBufferSize.Y); + if (!ciw) return FALSE; + + for (i = 0; i < dwBufferSize.X * dwBufferSize.Y; i++) + { + ciw[i].Attributes = lpBuffer[i].Attributes; + MultiByteToWideChar(CP_ACP, 0, &lpBuffer[i].Char.AsciiChar, 1, &ciw[i].Char.UnicodeChar, 1); + } + ret = WriteConsoleOutputW(hConsoleOutput, ciw, dwBufferSize, dwBufferCoord, lpWriteRegion); + HeapFree(GetProcessHeap(), 0, ciw); + + return ret; +} + +/*********************************************************************** + * WriteConsoleOutputW (KERNEL32.@) + */ +BOOL WINAPI WriteConsoleOutputW(HANDLE hConsoleOutput, LPCHAR_INFO lpBuffer, COORD dwBufferSize, + COORD dwBufferCoord, LPSMALL_RECT lpWriteRegion) +{ + short int w, h; + unsigned y; + DWORD ret = TRUE; + DWORD actual_width; + + TRACE("(%x,%p,(%d,%d),(%d,%d),(%d,%dx%d,%d)\n", + hConsoleOutput, lpBuffer, dwBufferSize.X, dwBufferSize.Y, dwBufferCoord.X, dwBufferCoord.Y, + lpWriteRegion->Left, lpWriteRegion->Top, lpWriteRegion->Right, lpWriteRegion->Bottom); + + w = min(lpWriteRegion->Right - lpWriteRegion->Left + 1, dwBufferSize.X - dwBufferCoord.X); + h = min(lpWriteRegion->Bottom - lpWriteRegion->Top + 1, dwBufferSize.Y - dwBufferCoord.Y); + + if (w <= 0 || h <= 0) + { + memset(lpWriteRegion, 0, sizeof(SMALL_RECT)); + return FALSE; + } + + /* this isn't supported for now, even if hConsoleOutput's row size fits in a single + * server's request... it would request cropping on client side + */ + if (w * sizeof(CHAR_INFO) > REQUEST_MAX_VAR_SIZE) + { + FIXME("This isn't supported yet, too wide CHAR_INFO array (%d)\n", w); + memset(lpWriteRegion, 0, sizeof(SMALL_RECT)); + return FALSE; + } + + actual_width = w; + for (y = 0; ret && y < h; y++) + { + SERVER_START_VAR_REQ(write_console_output, w * sizeof(CHAR_INFO)) + { + req->handle = hConsoleOutput; + req->mode = WRITE_CONSOLE_MODE_TEXTATTR; + req->x = lpWriteRegion->Left; + req->y = lpWriteRegion->Top + y; + memcpy(server_data_ptr(req), &lpBuffer[(y + dwBufferCoord.Y) * dwBufferSize.X + dwBufferCoord.X], w * sizeof(CHAR_INFO)); + if ((ret = !SERVER_CALL())) + actual_width = min(actual_width, req->written); + } + SERVER_END_VAR_REQ; + } + lpWriteRegion->Bottom = lpWriteRegion->Top + h; + lpWriteRegion->Right = lpWriteRegion->Left + actual_width; - LPSTR titleA = HEAP_strdupWtoA( GetProcessHeap(), 0, title ); - ret = SetConsoleTitleA(titleA); - HeapFree( GetProcessHeap(), 0, titleA ); return ret; } @@ -1276,46 +1381,58 @@ BOOL WINAPI SetConsoleTitleW( LPCWSTR title ) * * RETURNS STD */ -BOOL WINAPI SetConsoleCursorPosition( HANDLE hcon, COORD pos ) +BOOL WINAPI SetConsoleCursorPosition(HANDLE hcon, COORD pos) { - char xbuf[20]; - DWORD xlen; + BOOL ret; + CONSOLE_SCREEN_BUFFER_INFO csbi; + int do_move = 0; + int w, h; - /* make console complex only if we change lines, not just in the line */ - if (pos.Y) - CONSOLE_make_complex(hcon); + TRACE("%x %d %d\n", hcon, pos.X, pos.Y); - 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; -} + SERVER_START_REQ(set_console_output_info) + { + req->handle = hcon; + req->cursor_x = pos.X; + req->cursor_y = pos.Y; + req->mask = SET_CONSOLE_OUTPUT_INFO_CURSOR_POS; + ret = !SERVER_CALL_ERR(); + } + SERVER_END_REQ; -/*********************************************************************** - * GetNumberOfConsoleInputEvents (KERNEL32.@) - */ -BOOL WINAPI GetNumberOfConsoleInputEvents(HANDLE hcon,LPDWORD nrofevents) -{ - CONSOLE_get_input (hcon, FALSE); + if (!ret || !GetConsoleScreenBufferInfo(hcon, &csbi)) + return FALSE; - return read_console_input( hcon, NULL, 0, nrofevents, FALSE ); -} + /* if cursor is no longer visible, scroll the visible window... */ + w = csbi.srWindow.Right - csbi.srWindow.Left + 1; + h = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; + if (pos.X < csbi.srWindow.Left) + { + csbi.srWindow.Left = min(pos.X, csbi.dwSize.X - w); + do_move++; + } + else if (pos.X > csbi.srWindow.Right) + { + csbi.srWindow.Left = max(pos.X, w) - w + 1; + do_move++; + } + csbi.srWindow.Right = csbi.srWindow.Left + w - 1; -/*********************************************************************** - * GetNumberOfConsoleMouseButtons (KERNEL32.@) - */ -BOOL WINAPI GetNumberOfConsoleMouseButtons(LPDWORD nrofbuttons) -{ - FIXME("(%p): stub\n", nrofbuttons); - *nrofbuttons = 2; - return TRUE; + if (pos.Y < csbi.srWindow.Top) + { + csbi.srWindow.Top = min(pos.Y, csbi.dwSize.Y - h); + do_move++; + } + else if (pos.Y > csbi.srWindow.Bottom) + { + csbi.srWindow.Top = max(pos.Y, h) - h + 1; + do_move++; + } + csbi.srWindow.Bottom = csbi.srWindow.Top + h - 1; + + ret = (do_move) ? SetConsoleWindowInfo(hcon, TRUE, &csbi.srWindow) : TRUE; + + return ret; } /****************************************************************************** @@ -1329,11 +1446,11 @@ BOOL WINAPI GetNumberOfConsoleMouseButtons(LPDWORD nrofbuttons) * Success: TRUE * Failure: FALSE */ -BOOL WINAPI GetConsoleCursorInfo( HANDLE hcon, LPCONSOLE_CURSOR_INFO cinfo ) +BOOL WINAPI GetConsoleCursorInfo(HANDLE hcon, LPCONSOLE_CURSOR_INFO cinfo) { BOOL ret; - SERVER_START_REQ( get_console_info ) + SERVER_START_REQ(get_console_output_info) { req->handle = hcon; ret = !SERVER_CALL_ERR(); @@ -1351,28 +1468,23 @@ BOOL WINAPI GetConsoleCursorInfo( HANDLE hcon, LPCONSOLE_CURSOR_INFO cinfo ) /****************************************************************************** * SetConsoleCursorInfo [KERNEL32.@] Sets size and visibility of cursor * + * PARAMS + * hcon [I] Handle to console screen buffer + * cinfo [I] Address of cursor information * 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 */ +BOOL WINAPI SetConsoleCursorInfo(HANDLE hCon, LPCONSOLE_CURSOR_INFO cinfo) { - 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( set_console_info ) + SERVER_START_REQ(set_console_output_info) { - req->handle = hcon; + req->handle = hCon; req->cursor_size = cinfo->dwSize; req->cursor_visible = cinfo->bVisible; - req->mask = SET_CONSOLE_INFO_CURSOR; + req->mask = SET_CONSOLE_OUTPUT_INFO_CURSOR_GEOM; ret = !SERVER_CALL_ERR(); } SERVER_END_REQ; @@ -1383,17 +1495,42 @@ BOOL WINAPI SetConsoleCursorInfo( /****************************************************************************** * SetConsoleWindowInfo [KERNEL32.@] Sets size and position of console * + * PARAMS + * hcon [I] Handle to console screen buffer + * bAbsolute [I] Coordinate type flag + * window [I] Address of new window rectangle * 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 */ +BOOL WINAPI SetConsoleWindowInfo(HANDLE hCon, BOOL bAbsolute, LPSMALL_RECT window) { - FIXME("(%x,%d,%p): stub\n", hcon, bAbsolute, window); - return TRUE; + SMALL_RECT p = *window; + BOOL ret; + + if (!bAbsolute) + { + CONSOLE_SCREEN_BUFFER_INFO csbi; + if (!GetConsoleScreenBufferInfo(hCon, &csbi)) + return FALSE; + p.Left += csbi.srWindow.Left; + p.Top += csbi.srWindow.Top; + p.Right += csbi.srWindow.Left; + p.Bottom += csbi.srWindow.Top; + } + SERVER_START_REQ(set_console_output_info) + { + req->handle = hCon; + req->win_left = p.Left; + req->win_top = p.Top; + req->win_right = p.Right; + req->win_bottom = p.Bottom; + req->mask = SET_CONSOLE_OUTPUT_INFO_DISPLAY_WINDOW; + ret = !SERVER_CALL_ERR(); + } + SERVER_END_REQ; + + return ret; } @@ -1407,24 +1544,19 @@ BOOL WINAPI SetConsoleWindowInfo( * Success: TRUE * Failure: FALSE */ -BOOL WINAPI SetConsoleTextAttribute(HANDLE hConsoleOutput,WORD wAttr) +BOOL WINAPI SetConsoleTextAttribute(HANDLE hConsoleOutput, WORD wAttr) { - const int colormap[8] = { - 0,4,2,6, - 1,5,3,7, - }; - DWORD xlen; - char buffer[20]; + BOOL ret; - 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; + SERVER_START_REQ(set_console_output_info) + { + req->handle = hConsoleOutput; + req->attr = wAttr; + req->mask = SET_CONSOLE_OUTPUT_INFO_ATTR; + ret = !SERVER_CALL_ERR(); + } + SERVER_END_REQ; + return ret; } @@ -1439,11 +1571,30 @@ BOOL WINAPI SetConsoleTextAttribute(HANDLE hConsoleOutput,WORD wAttr) * Success: TRUE * Failure: FALSE */ -BOOL WINAPI SetConsoleScreenBufferSize( HANDLE hConsoleOutput, - COORD dwSize ) +BOOL WINAPI SetConsoleScreenBufferSize(HANDLE hConsoleOutput, COORD dwSize) { - FIXME("(%d,%dx%d): stub\n",hConsoleOutput,dwSize.X,dwSize.Y); - return TRUE; + BOOL ret; + + /* FIXME: most code relies on the fact we can transfer a complete row at a time... + * so check if it's possible... + */ + if (dwSize.X > REQUEST_MAX_VAR_SIZE / 4) + { + FIXME("too wide width not supported\n"); + SetLastError(STATUS_INVALID_PARAMETER); + return FALSE; + } + + SERVER_START_REQ(set_console_output_info) + { + req->handle = hConsoleOutput; + req->width = dwSize.X; + req->height = dwSize.Y; + req->mask = SET_CONSOLE_OUTPUT_INFO_SIZE; + ret = !SERVER_CALL_ERR(); + } + SERVER_END_REQ; + return ret; } @@ -1461,21 +1612,14 @@ BOOL WINAPI SetConsoleScreenBufferSize( HANDLE hConsoleOutput, * Success: TRUE * Failure: FALSE */ -BOOL WINAPI FillConsoleOutputCharacterA( - HANDLE hConsoleOutput, - BYTE cCharacter, - DWORD nLength, - COORD dwCoord, - LPDWORD lpNumCharsWritten) +BOOL WINAPI FillConsoleOutputCharacterA(HANDLE hConsoleOutput, BYTE cCharacter, + DWORD nLength, COORD dwCoord, LPDWORD lpNumCharsWritten) { - DWORD count; - DWORD xlen; + WCHAR wch; - SetConsoleCursorPosition(hConsoleOutput,dwCoord); - for(count=0;counthandle = hConsoleOutput; + req->x = dwCoord.X; + req->y = dwCoord.Y; + req->mode = WRITE_CONSOLE_MODE_TEXTSTDATTR|WRITE_CONSOLE_MODE_UNIFORM; + memcpy(server_data_ptr(req), &cCharacter, sizeof(WCHAR)); + written = SERVER_CALL_ERR() ? 0 : req->written; + } + SERVER_END_VAR_REQ; + + if (!written) break; + nLength -= written; + dwCoord.X = 0; + if (++dwCoord.Y == csbi.dwSize.Y) break; + } + + if (lpNumCharsWritten) *lpNumCharsWritten = initLen - nLength; + return initLen != nLength; } @@ -1527,178 +1690,384 @@ BOOL WINAPI FillConsoleOutputCharacterW(HANDLE hConsoleOutput, * Success: TRUE * Failure: FALSE */ -BOOL WINAPI FillConsoleOutputAttribute( HANDLE hConsoleOutput, - WORD wAttribute, DWORD nLength, COORD dwCoord, - LPDWORD lpNumAttrsWritten) +BOOL WINAPI FillConsoleOutputAttribute(HANDLE hConsoleOutput, WORD wAttribute, + DWORD nLength, COORD dwCoord, LPDWORD lpNumAttrsWritten) { - FIXME("(%d,%d,%ld,%dx%d,%p): stub\n", hConsoleOutput, - wAttribute,nLength,dwCoord.X,dwCoord.Y,lpNumAttrsWritten); - *lpNumAttrsWritten = nLength; + CONSOLE_SCREEN_BUFFER_INFO csbi; + int written; + DWORD initLen = nLength; + + TRACE("(%d,%d,%ld,(%dx%d),%p)\n", + hConsoleOutput, wAttribute, nLength, dwCoord.X, dwCoord.Y, lpNumAttrsWritten); + + if (!GetConsoleScreenBufferInfo(hConsoleOutput, &csbi)) + return FALSE; + + while (nLength) + { + SERVER_START_VAR_REQ(write_console_output, + min(csbi.dwSize.X - dwCoord.X, nLength) * sizeof(WCHAR)) + { + req->handle = hConsoleOutput; + req->x = dwCoord.X; + req->y = dwCoord.Y; + req->mode = WRITE_CONSOLE_MODE_ATTR|WRITE_CONSOLE_MODE_UNIFORM; + memcpy(server_data_ptr(req), &wAttribute, sizeof(WORD)); + written = SERVER_CALL_ERR() ? 0 : req->written; + } + SERVER_END_VAR_REQ; + + if (!written) break; + nLength -= written; + dwCoord.X = 0; + if (++dwCoord.Y == csbi.dwSize.Y) break; + } + + if (lpNumAttrsWritten) *lpNumAttrsWritten = initLen - nLength; + return initLen != nLength; +} + +/****************************************************************************** + * ScrollConsoleScreenBufferA [KERNEL32.@] + * + */ +BOOL WINAPI ScrollConsoleScreenBufferA(HANDLE hConsoleOutput, LPSMALL_RECT lpScrollRect, + LPSMALL_RECT lpClipRect, COORD dwDestOrigin, + LPCHAR_INFO lpFill) +{ + CHAR_INFO ciw; + + ciw.Attributes = lpFill->Attributes; + MultiByteToWideChar(CP_ACP, 0, &lpFill->Char.AsciiChar, 1, &ciw.Char.UnicodeChar, 1); + + return ScrollConsoleScreenBufferW(hConsoleOutput, lpScrollRect, lpClipRect, + dwDestOrigin, &ciw); +} + +/****************************************************************** + * fill_line_uniform + * + * Helper function for ScrollConsoleScreenBufferW + * Fills a part of a line with a constant character info + */ +static void fill_line_uniform(HANDLE hConsoleOutput, int i, int j, int len, LPCHAR_INFO lpFill) +{ + SERVER_START_VAR_REQ(write_console_output, len * sizeof(CHAR_INFO)) + { + req->handle = hConsoleOutput; + req->x = i; + req->y = j; + req->mode = WRITE_CONSOLE_MODE_TEXTATTR|WRITE_CONSOLE_MODE_UNIFORM; + memcpy(server_data_ptr(req), lpFill, sizeof(CHAR_INFO)); + SERVER_CALL_ERR(); + } + SERVER_END_VAR_REQ; +} + +/****************************************************************************** + * ScrollConsoleScreenBufferW [KERNEL32.@] + * + */ + +BOOL WINAPI ScrollConsoleScreenBufferW(HANDLE hConsoleOutput, LPSMALL_RECT lpScrollRect, + LPSMALL_RECT lpClipRect, COORD dwDestOrigin, + LPCHAR_INFO lpFill) +{ + SMALL_RECT dst; + DWORD ret; + int i, j; + int start = -1; + SMALL_RECT clip; + CONSOLE_SCREEN_BUFFER_INFO csbi; + BOOL inside; + + if (lpClipRect) + TRACE("(%d,(%d,%d-%d,%d),(%d,%d-%d,%d),%d-%d,%p)\n", hConsoleOutput, + lpScrollRect->Left, lpScrollRect->Top, + lpScrollRect->Right, lpScrollRect->Bottom, + lpClipRect->Left, lpClipRect->Top, + lpClipRect->Right, lpClipRect->Bottom, + dwDestOrigin.X, dwDestOrigin.Y, lpFill); + else + TRACE("(%d,(%d,%d-%d,%d),(nil),%d-%d,%p)\n", hConsoleOutput, + lpScrollRect->Left, lpScrollRect->Top, + lpScrollRect->Right, lpScrollRect->Bottom, + dwDestOrigin.X, dwDestOrigin.Y, lpFill); + + if (!GetConsoleScreenBufferInfo(hConsoleOutput, &csbi)) + return FALSE; + + /* step 1: get dst rect */ + dst.Left = dwDestOrigin.X; + dst.Top = dwDestOrigin.Y; + dst.Right = dst.Left + (lpScrollRect->Right - lpScrollRect->Left); + dst.Bottom = dst.Top + (lpScrollRect->Bottom - lpScrollRect->Top); + + /* step 2a: compute the final clip rect (optional passed clip and screen buffer limits */ + if (lpClipRect) + { + clip.Left = max(0, lpClipRect->Left); + clip.Right = min(csbi.dwSize.X - 1, lpClipRect->Right); + clip.Top = max(0, lpClipRect->Top); + clip.Bottom = min(csbi.dwSize.Y - 1, lpClipRect->Bottom); + } + else + { + clip.Left = 0; + clip.Right = csbi.dwSize.X - 1; + clip.Top = 0; + clip.Bottom = csbi.dwSize.Y - 1; + } + if (clip.Left > clip.Right || clip.Top > clip.Bottom) return FALSE; + + /* step 2b: clip dst rect */ + if (dst.Left < clip.Left ) dst.Left = clip.Left; + if (dst.Top < clip.Top ) dst.Top = clip.Top; + if (dst.Right > clip.Right ) dst.Right = clip.Right; + if (dst.Bottom > clip.Bottom) dst.Bottom = clip.Bottom; + + /* step 3: transfer the bits */ + SERVER_START_REQ(move_console_output) + { + req->handle = hConsoleOutput; + req->x_src = lpScrollRect->Left; + req->y_src = lpScrollRect->Top; + req->x_dst = dst.Left; + req->y_dst = dst.Top; + req->w = dst.Right - dst.Left + 1; + req->h = dst.Bottom - dst.Top + 1; + ret = !SERVER_CALL_ERR(); + } + SERVER_END_REQ; + + if (!ret) return FALSE; + + /* step 4: clean out the exposed part */ + + /* have to write celll [i,j] if it is not in dst rect (because it has already + * been written to by the scroll) and is in clip (we shall not write + * outside of clip) + */ + for (j = max(lpScrollRect->Top, clip.Top); j <= min(lpScrollRect->Bottom, clip.Bottom); j++) + { + inside = dst.Top <= j && j <= dst.Bottom; + start = -1; + for (i = max(lpScrollRect->Left, clip.Left); i <= min(lpScrollRect->Right, clip.Right); i++) + { + if (inside && dst.Left <= i && i <= dst.Right) + { + if (start != -1) + { + fill_line_uniform(hConsoleOutput, start, j, i - start, lpFill); + start = -1; + } + } + else + { + if (start == -1) start = i; + } + } + if (start != -1) + fill_line_uniform(hConsoleOutput, start, j, i - start, lpFill); + } + return TRUE; } /****************************************************************************** * ReadConsoleOutputCharacterA [KERNEL32.@] * - * BUGS - * Unimplemented */ -BOOL WINAPI ReadConsoleOutputCharacterA(HANDLE hConsoleOutput, - LPSTR lpstr, DWORD dword, COORD coord, LPDWORD lpdword) +BOOL WINAPI ReadConsoleOutputCharacterA(HANDLE hConsoleOutput, LPSTR lpstr, DWORD toRead, + COORD coord, LPDWORD lpdword) { - FIXME("(%d,%p,%ld,%dx%d,%p): stub\n", hConsoleOutput,lpstr, - dword,coord.X,coord.Y,lpdword); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; + DWORD read; + LPWSTR wptr = HeapAlloc(GetProcessHeap(), 0, toRead * sizeof(WCHAR)); + BOOL ret; + + if (lpdword) *lpdword = 0; + if (!wptr) return FALSE; + + ret = ReadConsoleOutputCharacterW(hConsoleOutput, wptr, toRead, coord, &read); + + read = WideCharToMultiByte(CP_ACP, 0, wptr, read, lpstr, toRead, NULL, NULL); + if (lpdword) *lpdword = read; + + HeapFree(GetProcessHeap(), 0, wptr); + + return ret; } /****************************************************************************** * ReadConsoleOutputCharacterW [KERNEL32.@] * - * BUGS - * Unimplemented */ -BOOL WINAPI ReadConsoleOutputCharacterW(HANDLE hConsoleOutput, - LPWSTR lpstr, DWORD dword, COORD coord, LPDWORD lpdword) +BOOL WINAPI ReadConsoleOutputCharacterW(HANDLE hConsoleOutput, LPWSTR lpstr, DWORD toRead, + COORD coord, LPDWORD lpdword) { - FIXME("(%d,%p,%ld,%dx%d,%p): stub\n", hConsoleOutput,lpstr, - dword,coord.X,coord.Y,lpdword); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; + DWORD read = 0; + DWORD ret = TRUE; + int i; + DWORD* ptr; + + TRACE("(%d,%p,%ld,%dx%d,%p)\n", hConsoleOutput, lpstr, toRead, coord.X, coord.Y, lpdword); + + while (ret && (read < toRead)) + { + SERVER_START_VAR_REQ(read_console_output, REQUEST_MAX_VAR_SIZE) + { + req->handle = (handle_t)hConsoleOutput; + req->x = coord.X; + req->y = coord.Y; + req->w = REQUEST_MAX_VAR_SIZE / 4; + req->h = 1; + if ((ret = !SERVER_CALL_ERR())) + { + ptr = server_data_ptr(req); + + for (i = 0; i < req->eff_w && read < toRead; i++) + { + lpstr[read++] = LOWORD(ptr[i]); + } + coord.X = 0; coord.Y++; + } + } + SERVER_END_VAR_REQ; + } + if (lpdword) *lpdword = read; + + TRACE("=> %lu %s\n", read, debugstr_wn(lpstr, read)); + + return ret; } -/****************************************************************************** - * ScrollConsoleScreenBufferA [KERNEL32.@] - * - * BUGS - * Unimplemented - */ -BOOL WINAPI ScrollConsoleScreenBufferA( HANDLE hConsoleOutput, - LPSMALL_RECT lpScrollRect, LPSMALL_RECT lpClipRect, - COORD dwDestOrigin, LPCHAR_INFO lpFill) -{ - FIXME("(%d,%p,%p,%dx%d,%p): stub\n", hConsoleOutput,lpScrollRect, - lpClipRect,dwDestOrigin.X,dwDestOrigin.Y,lpFill); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; -} - -/****************************************************************************** - * ScrollConsoleScreenBufferW [KERNEL32.@] - * - * BUGS - * Unimplemented - */ -BOOL WINAPI ScrollConsoleScreenBufferW( HANDLE hConsoleOutput, - LPSMALL_RECT lpScrollRect, LPSMALL_RECT lpClipRect, - COORD dwDestOrigin, LPCHAR_INFO lpFill) -{ - FIXME("(%d,%p,%p,%dx%d,%p): stub\n", hConsoleOutput,lpScrollRect, - lpClipRect,dwDestOrigin.X,dwDestOrigin.Y,lpFill); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; -} - /****************************************************************************** * ReadConsoleOutputA [KERNEL32.@] * - * BUGS - * Unimplemented */ -BOOL WINAPI ReadConsoleOutputA( HANDLE hConsoleOutput, - LPCHAR_INFO lpBuffer, - COORD dwBufferSize, - COORD dwBufferCoord, - LPSMALL_RECT lpReadRegion ) +BOOL WINAPI ReadConsoleOutputA(HANDLE hConsoleOutput, LPCHAR_INFO lpBuffer, COORD dwBufferSize, + COORD dwBufferCoord, LPSMALL_RECT lpReadRegion) { - FIXME("(%d,%p,%dx%d,%dx%d,%p): stub\n", hConsoleOutput, lpBuffer, - dwBufferSize.X, dwBufferSize.Y, dwBufferSize.X, dwBufferSize.Y, - lpReadRegion); + BOOL ret; + int x, y; + int pos; - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; + ret = ReadConsoleOutputW(hConsoleOutput, lpBuffer, dwBufferSize, dwBufferCoord, lpReadRegion); + if (!ret) return FALSE; + for (y = 0; y <= lpReadRegion->Bottom - lpReadRegion->Top; y++) + { + for (x = 0; x <= lpReadRegion->Right - lpReadRegion->Left; x++) + { + pos = (dwBufferCoord.Y + y) * dwBufferSize.X + dwBufferCoord.X + x; + WideCharToMultiByte(CP_ACP, 0, &lpBuffer[pos].Char.UnicodeChar, 1, + &lpBuffer[pos].Char.AsciiChar, 1, NULL, NULL); + } + } + return TRUE; } /****************************************************************************** * ReadConsoleOutputW [KERNEL32.@] * - * BUGS - * Unimplemented */ -BOOL WINAPI ReadConsoleOutputW( HANDLE hConsoleOutput, - LPCHAR_INFO lpBuffer, - COORD dwBufferSize, - COORD dwBufferCoord, - LPSMALL_RECT lpReadRegion ) +BOOL WINAPI ReadConsoleOutputW(HANDLE hConsoleOutput, LPCHAR_INFO lpBuffer, COORD dwBufferSize, + COORD dwBufferCoord, LPSMALL_RECT lpReadRegion) { - FIXME("(%d,%p,%dx%d,%dx%d,%p): stub\n", hConsoleOutput, lpBuffer, - dwBufferSize.X, dwBufferSize.Y, dwBufferSize.X, dwBufferSize.Y, - lpReadRegion); + int w, h; + int actual_width; + int y; + BOOL ret = TRUE; - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + w = min(lpReadRegion->Right - lpReadRegion->Left + 1, dwBufferSize.X - dwBufferCoord.X); + h = min(lpReadRegion->Bottom - lpReadRegion->Top + 1, dwBufferSize.Y - dwBufferCoord.Y); + + if (w <= 0 || h <= 0) goto got_err; + + /* this isn't supported for now, even if hConsoleOutput's row size fits in a single + * server's request... it would request cropping on client side + */ + if (w * sizeof(CHAR_INFO) > REQUEST_MAX_VAR_SIZE) + { + FIXME("This isn't supported yet, too wide CHAR_INFO array (%d)\n", w); + goto got_err; + } + + actual_width = w; + for (y = 0; ret && y < h; y++) + { + SERVER_START_VAR_REQ(read_console_output, w * sizeof(CHAR_INFO)) + { + req->handle = hConsoleOutput; + req->x = lpReadRegion->Left; + req->y = lpReadRegion->Top; + req->w = w; + req->h = 1; + if ((ret = !SERVER_CALL())) + { + actual_width = min(actual_width, req->eff_w); + memcpy(&lpBuffer[(y + dwBufferCoord.Y) * dwBufferSize.X + dwBufferCoord.X], + server_data_ptr(req), + req->eff_w * sizeof(CHAR_INFO)); + } + } + SERVER_END_VAR_REQ; + } + if (!ret) goto got_err; + + lpReadRegion->Bottom = lpReadRegion->Top + y; + lpReadRegion->Right = lpReadRegion->Left + actual_width; + + return ret; + got_err: + memset(lpReadRegion, 0, sizeof(SMALL_RECT)); return FALSE; } /****************************************************************************** * ReadConsoleOutputAttribute [KERNEL32.@] * - * BUGS - * Unimplemented */ -BOOL WINAPI ReadConsoleOutputAttribute( HANDLE hConsoleOutput, - LPWORD lpAttribute, - DWORD nLength, - COORD dwReadCoord, - LPDWORD lpNumberOfAttrsRead) +BOOL WINAPI ReadConsoleOutputAttribute(HANDLE hConsoleOutput, LPWORD lpAttribute, DWORD nLength, + COORD coord, LPDWORD lpNumberOfAttrsRead) { - FIXME("(%d,%p,%ld,%dx%d,%p): stub\n", hConsoleOutput, lpAttribute, - nLength, dwReadCoord.X, dwReadCoord.Y, lpNumberOfAttrsRead); + DWORD read = 0; + DWORD ret = TRUE; + int i; + DWORD* ptr; - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; -} - -/****************************************************************************** - * SetConsoleCP [KERNEL32.@] - * - * BUGS - * Unimplemented - */ -BOOL WINAPI SetConsoleCP( UINT cp ) -{ - FIXME("(%d): stub\n", cp); + TRACE("(%d,%p,%ld,%dx%d,%p)\n", + hConsoleOutput, lpAttribute, nLength, coord.X, coord.Y, lpNumberOfAttrsRead); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; -} - -/****************************************************************************** - * SetConsoleInputExeNameW [KERNEL32.@] - * - * BUGS - * Unimplemented - */ -BOOL WINAPI SetConsoleInputExeNameW( LPCWSTR name ) -{ - FIXME("(%s): stub!\n", debugstr_w(name)); - - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return TRUE; -} - -/****************************************************************************** - * SetConsoleInputExeNameA [KERNEL32.@] - * - * BUGS - * Unimplemented - */ -BOOL WINAPI SetConsoleInputExeNameA( LPCSTR name ) -{ - FIXME("(%s): stub!\n", name); - - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return TRUE; + while (ret && (read < nLength)) + { + SERVER_START_VAR_REQ(read_console_output, REQUEST_MAX_VAR_SIZE) + { + req->handle = (handle_t)hConsoleOutput; + req->x = coord.X; + req->y = coord.Y; + req->w = REQUEST_MAX_VAR_SIZE / 4; + req->h = 1; + if (SERVER_CALL_ERR()) + { + ret = FALSE; + } + else + { + ptr = server_data_ptr(req); + + for (i = 0; i < req->eff_w && read < nLength; i++) + { + lpAttribute[read++] = HIWORD(ptr[i]); + } + coord.X = 0; coord.Y++; + } + } + SERVER_END_VAR_REQ; + } + if (lpNumberOfAttrsRead) *lpNumberOfAttrsRead = read; + + return ret; } /****************************************************************************** @@ -1716,17 +2085,44 @@ BOOL WINAPI SetConsoleInputExeNameA( LPCSTR name ) * Success: TRUE * Failure: FALSE * - * BUGS - * Unimplemented */ -BOOL WINAPI WriteConsoleOutputAttribute( HANDLE hConsoleOutput, - CONST WORD *lpAttribute, DWORD nLength, COORD dwCoord, - LPDWORD lpNumAttrsWritten) +BOOL WINAPI WriteConsoleOutputAttribute(HANDLE hConsoleOutput, CONST WORD *lpAttribute, + DWORD nLength, COORD dwCoord, LPDWORD lpNumAttrsWritten) { - FIXME("(%d,%p,%ld,%dx%d,%p): stub\n", hConsoleOutput, + int written = 0; + int len; + BOOL ret = TRUE; + DWORD init_len = nLength; + CONSOLE_SCREEN_BUFFER_INFO csbi; + + TRACE("(%d,%p,%ld,%dx%d,%p)\n", hConsoleOutput, lpAttribute,nLength,dwCoord.X,dwCoord.Y,lpNumAttrsWritten); - *lpNumAttrsWritten = nLength; - return TRUE; + + if (!GetConsoleScreenBufferInfo(hConsoleOutput, & csbi)) + return FALSE; + + while (ret && nLength) + { + len = min(nLength * sizeof(WORD), REQUEST_MAX_VAR_SIZE); + SERVER_START_VAR_REQ(write_console_output, len) + { + req->handle = hConsoleOutput; + req->x = dwCoord.X; + req->y = dwCoord.Y; + req->mode = WRITE_CONSOLE_MODE_ATTR; + memcpy(server_data_ptr(req), &lpAttribute[written], len); + written = (SERVER_CALL_ERR()) ? 0 : req->written; + } + SERVER_END_VAR_REQ; + + if (!written) break; + nLength -= written; + dwCoord.X = 0; + if (++dwCoord.Y == csbi.dwSize.Y) break; + } + + if (lpNumAttrsWritten) *lpNumAttrsWritten = init_len - nLength; + return nLength != init_len; } /****************************************************************************** @@ -1739,18 +2135,30 @@ BOOL WINAPI WriteConsoleOutputAttribute( HANDLE hConsoleOutput, * nLength [I] Number of cells to write to * dwCoord [I] Coords of first cell * lpNumCharsWritten [O] Pointer to number of cells written - * - * BUGS - * Unimplemented */ -BOOL WINAPI WriteConsoleOutputCharacterA( HANDLE hConsoleOutput, - LPCSTR lpCharacter, DWORD nLength, COORD dwCoord, - LPDWORD lpNumCharsWritten) +BOOL WINAPI WriteConsoleOutputCharacterA(HANDLE hConsoleOutput, LPCSTR lpCharacter, DWORD nLength, + COORD dwCoord, LPDWORD lpNumCharsWritten) { - FIXME("(%d,%p,%ld,%dx%d,%p): stub\n", hConsoleOutput, - lpCharacter,nLength,dwCoord.X,dwCoord.Y,lpNumCharsWritten); - *lpNumCharsWritten = nLength; - return TRUE; + BOOL ret; + LPWSTR xstring; + DWORD n; + + TRACE("(%d,%s,%ld,%dx%d,%p)\n", hConsoleOutput, + debugstr_an(lpCharacter, nLength), nLength, dwCoord.X, dwCoord.Y, lpNumCharsWritten); + + n = MultiByteToWideChar(CP_ACP, 0, lpCharacter, nLength, NULL, 0); + + if (lpNumCharsWritten) *lpNumCharsWritten = 0; + xstring = HeapAlloc(GetProcessHeap(), 0, n * sizeof(WCHAR)); + if (!xstring) return FALSE; + + MultiByteToWideChar(CP_ACP, 0, lpCharacter, nLength, xstring, n); + + ret = WriteConsoleOutputCharacterW(hConsoleOutput, xstring, n, dwCoord, lpNumCharsWritten); + + HeapFree(GetProcessHeap(), 0, xstring); + + return ret; } /****************************************************************************** @@ -1768,15 +2176,123 @@ BOOL WINAPI WriteConsoleOutputCharacterA( HANDLE hConsoleOutput, * Success: TRUE * Failure: FALSE * - * BUGS - * Unimplemented */ -BOOL WINAPI WriteConsoleOutputCharacterW( HANDLE hConsoleOutput, - LPCWSTR lpCharacter, DWORD nLength, COORD dwCoord, - LPDWORD lpNumCharsWritten) +BOOL WINAPI WriteConsoleOutputCharacterW(HANDLE hConsoleOutput, LPCWSTR lpCharacter, DWORD nLength, + COORD dwCoord, LPDWORD lpNumCharsWritten) { - FIXME("(%d,%p,%ld,%dx%d,%p): stub\n", hConsoleOutput, - lpCharacter,nLength,dwCoord.X,dwCoord.Y,lpNumCharsWritten); - *lpNumCharsWritten = nLength; - return TRUE; + int written = 0; + int len; + DWORD init_len = nLength; + CONSOLE_SCREEN_BUFFER_INFO csbi; + + TRACE("(%d,%s,%ld,%dx%d,%p)\n", hConsoleOutput, + debugstr_wn(lpCharacter, nLength), nLength, dwCoord.X, dwCoord.Y, lpNumCharsWritten); + + if (!GetConsoleScreenBufferInfo(hConsoleOutput, &csbi)) + return FALSE; + + while (nLength) + { + len = min(nLength * sizeof(WCHAR), REQUEST_MAX_VAR_SIZE); + SERVER_START_VAR_REQ(write_console_output, len) + { + req->handle = hConsoleOutput; + req->x = dwCoord.X; + req->y = dwCoord.Y; + req->mode = WRITE_CONSOLE_MODE_TEXT; + memcpy(server_data_ptr(req), &lpCharacter[written], len); + written = (SERVER_CALL_ERR()) ? 0 : req->written; + } + SERVER_END_VAR_REQ; + + if (!written) break; + nLength -= written; + dwCoord.X += written; + if (dwCoord.X >= csbi.dwSize.X) + { + dwCoord.X = 0; + if (++dwCoord.Y == csbi.dwSize.Y) break; + } + } + + if (lpNumCharsWritten) *lpNumCharsWritten = init_len - nLength; + return nLength != init_len; } + +/* ==================================================================== + * + * Console manipulation functions + * + * ====================================================================*/ +/* some missing functions... + * FIXME: those are likely to be defined as undocumented function in kernel32 (or part of them) + * should get the right API and implement them + * GetConsoleCommandHistory[AW] (dword dword dword) + * GetConsoleCommandHistoryLength[AW] + * SetConsoleCommandHistoryMode + * SetConsoleNumberOfCommands[AW] + */ +int CONSOLE_GetHistory(int idx, WCHAR* buf, int buf_len) +{ + int len = 0; + + SERVER_START_VAR_REQ(get_console_input_history, REQUEST_MAX_VAR_SIZE) + { + req->handle = 0; + req->index = idx; + if (!SERVER_CALL_ERR()) + { + len = server_data_size(req) / sizeof(WCHAR) + 1; + if (buf) + { + len = min(len, buf_len); + memcpy(buf, server_data_ptr(req), len * sizeof(WCHAR)); + buf[len - 1] = 0; + } + } + } + SERVER_END_VAR_REQ; + return len; +} + +/****************************************************************** + * CONSOLE_AppendHistory + * + * + */ +BOOL CONSOLE_AppendHistory(const WCHAR* ptr) +{ + size_t len = strlenW(ptr); + BOOL ret; + + while (len && (ptr[len - 1] == '\n' || ptr[len - 1] == '\r')) + len--; + + len *= sizeof(WCHAR); + SERVER_START_VAR_REQ(append_console_input_history, len) + { + req->handle = 0; + memcpy(server_data_ptr(req), ptr, len); + ret = !SERVER_CALL_ERR(); + } + SERVER_END_VAR_REQ; + return ret; +} + +/****************************************************************** + * CONSOLE_GetNumHistoryEntries + * + * + */ +unsigned CONSOLE_GetNumHistoryEntries(void) +{ + unsigned ret = 0; + SERVER_START_REQ(get_console_input_info) + { + req->handle = 0; + if (!SERVER_CALL_ERR()) ret = req->history_index; + } + SERVER_END_REQ; + return ret; +} + diff --git a/win32/editline.c b/win32/editline.c new file mode 100644 index 00000000000..739743c4342 --- /dev/null +++ b/win32/editline.c @@ -0,0 +1,708 @@ +/* + * line edition function for Win32 console + * + * Copyright 2001 Eric Pouech + */ + +#include "config.h" +#include + +#include "windef.h" +#include "winbase.h" +#include "wincon.h" +#include "wine/unicode.h" +#include "winnls.h" +#include "debugtools.h" + +DEFAULT_DEBUG_CHANNEL(console); + +/* console.c */ +extern int CONSOLE_GetHistory(int idx, WCHAR* buf, int buf_len); +extern BOOL CONSOLE_AppendHistory(const WCHAR *p); +extern unsigned int CONSOLE_GetNumHistoryEntries(void); + +struct WCEL_Context; + +typedef struct +{ + WCHAR val; /* vk or unicode char */ + void (*func)(struct WCEL_Context* ctx); +} KeyEntry; + +typedef struct +{ + DWORD keyState; /* keyState (from INPUT_RECORD) to match */ + BOOL chkChar; /* check vk or char */ + KeyEntry* entries; /* array of entries */ +} KeyMap; + +typedef struct WCEL_Context { + WCHAR* line; /* the line being edited */ + size_t alloc; /* number of WCHAR in line */ + unsigned len; /* number of chars in line */ + unsigned ofs; /* offset for cursor in current line */ + WCHAR* yanked; /* yanked line */ + unsigned mark; /* marked point (emacs mode only) */ + CONSOLE_SCREEN_BUFFER_INFO csbi; /* current state (initial cursor, window size, attribute) */ + HANDLE hConIn; + HANDLE hConOut; + unsigned done : 1, /* to 1 when we're done with editing */ + error : 1; /* to 1 when an error occurred in the editing */ + unsigned histSize; + unsigned histPos; + WCHAR* histCurr; +} WCEL_Context; + +#if 0 +static void WCEL_Dump(WCEL_Context* ctx, const char* pfx) +{ + MESSAGE("%s: [line=%s[alloc=%u] ofs=%u len=%u start=(%d,%d) mask=%c%c\n" + "\t\thist=(size=%u pos=%u curr=%s)\n", + pfx, debugstr_w(ctx->line), ctx->alloc, ctx->ofs, ctx->len, + ctx->csbi.dwCursorPosition.X, ctx->csbi.dwCursorPosition.Y, + ctx->done ? 'D' : 'd', ctx->error ? 'E' : 'e', + ctx->histSize, ctx->histPos, debugstr_w(ctx->histCurr)); +} +#endif + +/* ==================================================================== + * + * Console helper functions + * + * ====================================================================*/ + +static BOOL WCEL_Get(WCEL_Context* ctx, INPUT_RECORD* ir) +{ + DWORD retv; + + for (;;) + { + /* data available ? */ + if (ReadConsoleInputW(ctx->hConIn, ir, 1, &retv) && retv == 1) + return TRUE; + /* then wait... */ + switch (WaitForSingleObject(ctx->hConIn, INFINITE)) + { + case WAIT_OBJECT_0: + break; + default: + /* we have checked that hConIn was a console handle (could be sb) */ + ERR("Shouldn't happen\n"); + /* fall thru */ + case WAIT_ABANDONED: + case WAIT_TIMEOUT: + ctx->error = 1; + ERR("hmm bad situation\n"); + return FALSE; + } + } +} + +static inline void WCEL_Beep(WCEL_Context* ctx) +{ + Beep(400, 300); +} + +static inline COORD WCEL_GetCoord(WCEL_Context* ctx, int ofs) +{ + COORD c; + c.X = ctx->csbi.dwCursorPosition.X + ofs; + c.Y = ctx->csbi.dwCursorPosition.Y; + return c; +} + +static inline void WCEL_GetRect(WCEL_Context* ctx, LPSMALL_RECT sr, int beg, int end) +{ + sr->Left = ctx->csbi.dwCursorPosition.X + beg; + sr->Top = ctx->csbi.dwCursorPosition.Y; + sr->Right = ctx->csbi.dwCursorPosition.X + end; + sr->Bottom = ctx->csbi.dwCursorPosition.Y; +} + +/* ==================================================================== + * + * context manipulation functions + * + * ====================================================================*/ + +static BOOL WCEL_Grow(WCEL_Context* ctx, size_t len) +{ + if (ctx->csbi.dwCursorPosition.X + ctx->ofs + len >= ctx->csbi.dwSize.X) + { + FIXME("Current implementation doesn't allow edition to spray across several lines\n"); + return FALSE; + } + + if (ctx->len + len >= ctx->alloc) + { + WCHAR* newline; + newline = HeapReAlloc(GetProcessHeap(), 0, ctx->line, sizeof(WCHAR) * (ctx->alloc + 32)); + if (!newline) return FALSE; + ctx->line = newline; + ctx->alloc += 32; + } + return TRUE; +} + +static void WCEL_DeleteString(WCEL_Context* ctx, int beg, int end) +{ + SMALL_RECT scl, clp; + CHAR_INFO ci; + + if (end < ctx->len) + memmove(&ctx->line[beg], &ctx->line[end], (ctx->len - end) * sizeof(WCHAR)); + /* make the source rect bigger than the actual rect to that the part outside the clip + * rect (before the scroll) will get redrawn after the scroll + */ + WCEL_GetRect(ctx, &scl, end, ctx->len + end - beg); + WCEL_GetRect(ctx, &clp, beg, ctx->len); + + ci.Char.UnicodeChar = ' '; + ci.Attributes = ctx->csbi.wAttributes; + ScrollConsoleScreenBufferW(ctx->hConOut, &scl, &clp, WCEL_GetCoord(ctx, beg), &ci); + + ctx->len -= end - beg; + ctx->line[ctx->len] = 0; +} + +static void WCEL_InsertString(WCEL_Context* ctx, const WCHAR* str) +{ + size_t len = lstrlenW(str); + + if (!len || !WCEL_Grow(ctx, len)) return; + if (ctx->len > ctx->ofs) + memmove(&ctx->line[ctx->ofs + len], &ctx->line[ctx->ofs], (ctx->len - ctx->ofs) * sizeof(WCHAR)); + memcpy(&ctx->line[ctx->ofs], str, len * sizeof(WCHAR)); + ctx->len += len; + ctx->line[ctx->len] = 0; + WriteConsoleOutputCharacterW(ctx->hConOut, &ctx->line[ctx->ofs], ctx->len - ctx->ofs, + WCEL_GetCoord(ctx, ctx->ofs), NULL); + ctx->ofs += len; +} + +static void WCEL_InsertChar(WCEL_Context* ctx, WCHAR c) +{ + WCHAR buffer[2]; + + /* do not insert 0..31 control characters */ + if (c < ' ') + { + if (c != '\t') return; + } + buffer[0] = c; + buffer[1] = 0; + WCEL_InsertString(ctx, buffer); +} + +static void WCEL_SaveYank(WCEL_Context* ctx, int beg, int end) +{ + int len = end - beg; + ctx->yanked = HeapReAlloc(GetProcessHeap(), 0, ctx->yanked, (len + 1) * sizeof(WCHAR)); + if (!ctx->yanked) return; + memcpy(ctx->yanked, &ctx->line[beg], len * sizeof(WCHAR)); + ctx->yanked[len] = 0; +} + +/* FIXME NTDLL doesn't export iswalnum, and I don't want to link in msvcrt when most + * of the data lay in unicode lib + */ +static inline BOOL WCEL_iswalnum(WCHAR wc) +{ + return get_char_typeW(wc) & (C1_ALPHA|C1_DIGIT|C1_LOWER|C1_UPPER); +} + +static int WCEL_GetLeftWordTransition(WCEL_Context* ctx, int ofs) +{ + ofs--; + while (ofs >= 0 && !WCEL_iswalnum(ctx->line[ofs])) ofs--; + while (ofs >= 0 && WCEL_iswalnum(ctx->line[ofs])) ofs--; + if (ofs >= 0) ofs++; + return max(ofs, 0); +} + +static int WCEL_GetRightWordTransition(WCEL_Context* ctx, int ofs) +{ + ofs++; + while (ofs <= ctx->len && !WCEL_iswalnum(ctx->line[ofs])) ofs++; + while (ofs <= ctx->len && WCEL_iswalnum(ctx->line[ofs])) ofs++; + return min(ofs, ctx->len); +} + +static WCHAR* WCEL_GetHistory(WCEL_Context* ctx, int idx) +{ + WCHAR* ptr; + + if (idx == ctx->histSize - 1) + { + ptr = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(ctx->histCurr) + 1) * sizeof(WCHAR)); + lstrcpyW(ptr, ctx->histCurr); + } + else + { + int len = CONSOLE_GetHistory(idx, NULL, 0); + + if ((ptr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)))) + { + CONSOLE_GetHistory(idx, ptr, len); + } + } + return ptr; +} + +static void WCEL_HistoryInit(WCEL_Context* ctx) +{ + ctx->histPos = CONSOLE_GetNumHistoryEntries(); + ctx->histSize = ctx->histPos + 1; + ctx->histCurr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WCHAR)); +} + +static void WCEL_MoveToHist(WCEL_Context* ctx, int idx) +{ + WCHAR* data = WCEL_GetHistory(ctx, idx); + int len = lstrlenW(data) + 1; + + /* save current line edition for recall when needed (FIXME seems broken to me) */ + if (ctx->histPos == ctx->histSize - 1) + { + if (ctx->histCurr) HeapFree(GetProcessHeap(), 0, ctx->histCurr); + ctx->histCurr = HeapAlloc(GetProcessHeap(), 0, (ctx->len + 1) * sizeof(WCHAR)); + memcpy(ctx->histCurr, ctx->line, (ctx->len + 1) * sizeof(WCHAR)); + } + /* need to clean also the screen if new string is shorter than old one */ + WCEL_DeleteString(ctx, 0, ctx->len); + ctx->ofs = 0; + /* insert new string */ + if (WCEL_Grow(ctx, len)) + { + WCEL_InsertString(ctx, data); + HeapFree(GetProcessHeap(), 0, data); + ctx->histPos = idx; + } +} + +/* ==================================================================== + * + * basic edition functions + * + * ====================================================================*/ + +static void WCEL_Done(WCEL_Context* ctx) +{ + if (!WCEL_Grow(ctx, 1)) return; + ctx->line[ctx->len++] = '\n'; + ctx->line[ctx->len] = 0; + WriteConsoleA(ctx->hConOut, "\n", 1, NULL, NULL); + ctx->done = 1; +} + +static void WCEL_MoveLeft(WCEL_Context* ctx) +{ + if (ctx->ofs > 0) ctx->ofs--; +} + +static void WCEL_MoveRight(WCEL_Context* ctx) +{ + if (ctx->ofs < ctx->len) ctx->ofs++; +} + +static void WCEL_MoveToLeftWord(WCEL_Context* ctx) +{ + int new_ofs = WCEL_GetLeftWordTransition(ctx, ctx->ofs); + if (new_ofs != ctx->ofs) ctx->ofs = new_ofs; +} + +static void WCEL_MoveToRightWord(WCEL_Context* ctx) +{ + int new_ofs = WCEL_GetRightWordTransition(ctx, ctx->ofs); + if (new_ofs != ctx->ofs) ctx->ofs = new_ofs; +} + +static void WCEL_MoveToBeg(WCEL_Context* ctx) +{ + ctx->ofs = 0; +} + +static void WCEL_MoveToEnd(WCEL_Context* ctx) +{ + ctx->ofs = ctx->len; +} + +static void WCEL_SetMark(WCEL_Context* ctx) +{ + ctx->mark = ctx->ofs; +} + +static void WCEL_ExchangeMark(WCEL_Context* ctx) +{ + unsigned tmp; + + if (ctx->mark > ctx->len) return; + tmp = ctx->ofs; + ctx->ofs = ctx->mark; + ctx->mark = tmp; +} + +static void WCEL_CopyMarkedZone(WCEL_Context* ctx) +{ + unsigned beg, end; + + if (ctx->mark > ctx->len || ctx->mark == ctx->ofs) return; + if (ctx->mark > ctx->ofs) + { + beg = ctx->ofs; end = ctx->mark; + } + else + { + beg = ctx->mark; end = ctx->ofs; + } + WCEL_SaveYank(ctx, beg, end); +} + +static void WCEL_TransposeChar(WCEL_Context* ctx) +{ + WCHAR c; + + if (!ctx->ofs || ctx->ofs == ctx->len) return; + + c = ctx->line[ctx->ofs]; + ctx->line[ctx->ofs] = ctx->line[ctx->ofs - 1]; + ctx->line[ctx->ofs - 1] = c; + + WriteConsoleOutputCharacterW(ctx->hConOut, &ctx->line[ctx->ofs - 1], 2, WCEL_GetCoord(ctx, ctx->ofs - 1), NULL); + ctx->ofs++; +} + +static void WCEL_TransposeWords(WCEL_Context* ctx) +{ + FIXME("NIY\n"); +} + +static void WCEL_LowerCaseWord(WCEL_Context* ctx) +{ + int new_ofs = WCEL_GetRightWordTransition(ctx, ctx->ofs); + if (new_ofs != ctx->ofs) + { + int i; + for (i = ctx->ofs; i <= new_ofs; i++) + ctx->line[i] = tolowerW(ctx->line[i]); + WriteConsoleOutputCharacterW(ctx->hConOut, &ctx->line[ctx->ofs], new_ofs - ctx->ofs + 1, + WCEL_GetCoord(ctx, ctx->ofs), NULL); + ctx->ofs = new_ofs; + } +} + +static void WCEL_UpperCaseWord(WCEL_Context* ctx) +{ + int new_ofs = WCEL_GetRightWordTransition(ctx, ctx->ofs); + if (new_ofs != ctx->ofs) + { + int i; + for (i = ctx->ofs; i <= new_ofs; i++) + ctx->line[i] = toupperW(ctx->line[i]); + WriteConsoleOutputCharacterW(ctx->hConOut, &ctx->line[ctx->ofs], new_ofs - ctx->ofs + 1, + WCEL_GetCoord(ctx, ctx->ofs), NULL); + ctx->ofs = new_ofs; + } +} + +static void WCEL_CapitalizeWord(WCEL_Context* ctx) +{ + int new_ofs = WCEL_GetRightWordTransition(ctx, ctx->ofs); + if (new_ofs != ctx->ofs) + { + int i; + + ctx->line[ctx->ofs] = toupperW(ctx->line[ctx->ofs]); + for (i = ctx->ofs + 1; i <= new_ofs; i++) + ctx->line[i] = tolowerW(ctx->line[i]); + WriteConsoleOutputCharacterW(ctx->hConOut, &ctx->line[ctx->ofs], new_ofs - ctx->ofs + 1, + WCEL_GetCoord(ctx, ctx->ofs), NULL); + ctx->ofs = new_ofs; + } +} + +static void WCEL_Yank(WCEL_Context* ctx) +{ + WCEL_InsertString(ctx, ctx->yanked); + HeapFree(GetProcessHeap(), 0, ctx->yanked); + ctx->yanked = NULL; +} + +static void WCEL_KillToEndOfLine(WCEL_Context* ctx) +{ + WCEL_SaveYank(ctx, ctx->ofs, ctx->len); + WCEL_DeleteString(ctx, ctx->ofs, ctx->len); +} + +static void WCEL_KillMarkedZone(WCEL_Context* ctx) +{ + unsigned beg, end; + + if (ctx->mark > ctx->len || ctx->mark == ctx->ofs) return; + if (ctx->mark > ctx->ofs) + { + beg = ctx->ofs; end = ctx->mark; + } + else + { + beg = ctx->mark; end = ctx->ofs; + } + WCEL_SaveYank(ctx, beg, end); + WCEL_DeleteString(ctx, beg, end); + ctx->ofs = beg; +} + +static void WCEL_DeletePrevChar(WCEL_Context* ctx) +{ + if (ctx->ofs) + { + WCEL_DeleteString(ctx, ctx->ofs - 1, ctx->ofs); + ctx->ofs--; + } +} + +static void WCEL_DeleteCurrChar(WCEL_Context* ctx) +{ + if (ctx->ofs < ctx->len) + WCEL_DeleteString(ctx, ctx->ofs, ctx->ofs + 1); +} + +static void WCEL_DeleteLeftWord(WCEL_Context* ctx) +{ + int new_ofs = WCEL_GetLeftWordTransition(ctx, ctx->ofs); + if (new_ofs != ctx->ofs) + { + WCEL_DeleteString(ctx, new_ofs, ctx->ofs); + ctx->ofs = new_ofs; + } +} + +static void WCEL_DeleteRightWord(WCEL_Context* ctx) +{ + int new_ofs = WCEL_GetRightWordTransition(ctx, ctx->ofs); + if (new_ofs != ctx->ofs) + { + WCEL_DeleteString(ctx, ctx->ofs, new_ofs); + } +} + +static void WCEL_MoveToPrevHist(WCEL_Context* ctx) +{ + if (ctx->histPos) WCEL_MoveToHist(ctx, ctx->histPos - 1); +} + +static void WCEL_MoveToNextHist(WCEL_Context* ctx) +{ + if (ctx->histPos < ctx->histSize - 1) WCEL_MoveToHist(ctx, ctx->histPos + 1); +} + +static void WCEL_MoveToFirstHist(WCEL_Context* ctx) +{ + if (ctx->histPos != 0) WCEL_MoveToHist(ctx, 0); +} + +static void WCEL_MoveToLastHist(WCEL_Context* ctx) +{ + if (ctx->histPos != ctx->histSize - 1) WCEL_MoveToHist(ctx, ctx->histSize - 1); +} + +/* ==================================================================== + * + * Key Maps + * + * ====================================================================*/ + +#define CTRL(x) ((x) - '@') +static KeyEntry StdKeyMap[] = +{ + {/*BACK*/0x08, WCEL_DeletePrevChar }, + {/*RETURN*/0x0d, WCEL_Done }, + {/*DEL*/127, WCEL_DeleteCurrChar }, + { 0, NULL } +}; + +static KeyEntry EmacsKeyMapCtrl[] = +{ + { CTRL('@'), WCEL_SetMark }, + { CTRL('A'), WCEL_MoveToBeg }, + { CTRL('B'), WCEL_MoveLeft }, + /* C */ + { CTRL('D'), WCEL_DeleteCurrChar }, + { CTRL('E'), WCEL_MoveToEnd }, + { CTRL('F'), WCEL_MoveRight }, + { CTRL('G'), WCEL_Beep }, + { CTRL('H'), WCEL_DeletePrevChar }, + /* I: meaningless (or tab ???) */ + { CTRL('J'), WCEL_Done }, + { CTRL('K'), WCEL_KillToEndOfLine }, + /* L: [NIY] redraw the whole stuff */ + { CTRL('M'), WCEL_Done }, + { CTRL('N'), WCEL_MoveToNextHist }, + /* O; insert line... meaningless */ + { CTRL('P'), WCEL_MoveToPrevHist }, + /* Q: [NIY] quoting... */ + /* R: [NIY] search backwards... */ + /* S: [NIY] search forwards... */ + { CTRL('T'), WCEL_TransposeChar }, + /* U: [NIY] set repeat count... */ + /* V: paragraph down... meaningless */ + { CTRL('W'), WCEL_KillMarkedZone }, + { CTRL('X'), WCEL_ExchangeMark }, + { CTRL('Y'), WCEL_Yank }, + /* Z: meaningless */ + { 0, NULL } +}; + +static KeyEntry EmacsKeyMapAlt[] = +{ + {/*DEL*/127, WCEL_DeleteLeftWord }, + { '<', WCEL_MoveToFirstHist }, + { '>', WCEL_MoveToLastHist }, + { '?', WCEL_Beep }, + { 'b', WCEL_MoveToLeftWord }, + { 'c', WCEL_CapitalizeWord }, + { 'd', WCEL_DeleteRightWord }, + { 'f', WCEL_MoveToRightWord }, + { 'l', WCEL_LowerCaseWord }, + { 't', WCEL_TransposeWords }, + { 'u', WCEL_UpperCaseWord }, + { 'w', WCEL_CopyMarkedZone }, + { 0, NULL } +}; + +static KeyEntry EmacsKeyMapExtended[] = +{ + {/*VK_PRIOR*/0x21, WCEL_MoveToPrevHist }, + {/*VK_NEXT*/0x22, WCEL_MoveToNextHist }, + {/*VK_RIGHT*/0x27, WCEL_MoveRight }, + {/*VK_LEFT*/0x25, WCEL_MoveLeft }, + { 0, NULL } +}; + +static KeyMap EmacsKeyMap[] = +{ + {0x00000000, 1, StdKeyMap}, + {0x00000001, 1, EmacsKeyMapAlt}, /* left alt */ + {0x00000002, 1, EmacsKeyMapAlt}, /* right alt */ + {0x00000004, 1, EmacsKeyMapCtrl}, /* left ctrl */ + {0x00000008, 1, EmacsKeyMapCtrl}, /* right ctrl */ + {0x00000100, 0, EmacsKeyMapExtended}, + {0, 0, 0} +}; + +static KeyEntry Win32KeyMapExtended[] = +{ + {/*VK_LEFT*/ 0x25, WCEL_MoveLeft }, + {/*VK_RIGHT*/0x27, WCEL_MoveRight }, + {/*VK_HOME*/ 0x24, WCEL_MoveToBeg }, + {/*VK_END*/ 0x23, WCEL_MoveToEnd }, + {/*VK_UP*/ 0x26, WCEL_MoveToPrevHist }, + {/*VK_DOWN*/ 0x28, WCEL_MoveToNextHist }, + { 0, NULL } +}; + +static KeyEntry Win32KeyMapCtrlExtended[] = +{ + {/*VK_LEFT*/ 0x25, WCEL_MoveToLeftWord }, + {/*VK_RIGHT*/0x27, WCEL_MoveToRightWord }, + { 0, NULL } +}; + +KeyMap Win32KeyMap[] = +{ + {0x00000000, 1, StdKeyMap}, + {0x00000100, 0, Win32KeyMapExtended}, + {0x00000104, 0, Win32KeyMapCtrlExtended}, + {0x00000108, 0, Win32KeyMapCtrlExtended}, + {0, 0, 0} +}; +#undef CTRL + +/* ==================================================================== + * + * Read line master function + * + * ====================================================================*/ + +WCHAR* CONSOLE_Readline(HANDLE hConsoleIn, int use_emacs) +{ + WCEL_Context ctx; + INPUT_RECORD ir; + KeyMap* km; + KeyEntry* ke; + unsigned ofs; + void (*func)(struct WCEL_Context* ctx); + + memset(&ctx, 0, sizeof(ctx)); + ctx.hConIn = hConsoleIn; + WCEL_HistoryInit(&ctx); + if ((ctx.hConOut = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, 0, 0 )) == INVALID_HANDLE_VALUE || + !GetConsoleScreenBufferInfo(ctx.hConOut, &ctx.csbi)) + return NULL; + if (!WCEL_Grow(&ctx, 1)) + { + CloseHandle(ctx.hConOut); + return NULL; + } + ctx.line[0] = 0; + +/* EPP WCEL_Dump(&ctx, "init"); */ + + while (!ctx.done && !ctx.error && WCEL_Get(&ctx, &ir)) + { + if (ir.EventType != KEY_EVENT || !ir.Event.KeyEvent.bKeyDown) continue; + TRACE("key%s repeatCount=%u, keyCode=%02x scanCode=%02x char=%02x keyState=%08lx\n", + ir.Event.KeyEvent.bKeyDown ? "Down" : "Up ", ir.Event.KeyEvent.wRepeatCount, + ir.Event.KeyEvent.wVirtualKeyCode, ir.Event.KeyEvent.wVirtualScanCode, + ir.Event.KeyEvent.uChar.UnicodeChar, ir.Event.KeyEvent.dwControlKeyState); + +/* EPP WCEL_Dump(&ctx, "before func"); */ + ofs = ctx.ofs; + + func = NULL; + for (km = (use_emacs) ? EmacsKeyMap : Win32KeyMap; km->entries != NULL; km++) + { + if (km->keyState != ir.Event.KeyEvent.dwControlKeyState) + continue; + if (km->chkChar) + { + for (ke = &km->entries[0]; ke->func != 0; ke++) + if (ke->val == ir.Event.KeyEvent.uChar.UnicodeChar) break; + } + else + { + for (ke = &km->entries[0]; ke->func != 0; ke++) + if (ke->val == ir.Event.KeyEvent.wVirtualKeyCode) break; + + } + if (ke->func) + { + func = ke->func; + break; + } + } + + if (func) + (func)(&ctx); + else if (!(ir.Event.KeyEvent.dwControlKeyState & (ENHANCED_KEY|LEFT_ALT_PRESSED))) + WCEL_InsertChar(&ctx, ir.Event.KeyEvent.uChar.UnicodeChar); + else TRACE("Dropped event\n"); + +/* EPP WCEL_Dump(&ctx, "after func"); */ + if (ctx.ofs != ofs) + SetConsoleCursorPosition(ctx.hConOut, WCEL_GetCoord(&ctx, ctx.ofs)); + } + if (ctx.error) + { + HeapFree(GetProcessHeap(), 0, ctx.line); + ctx.line = NULL; + } + if (ctx.line) + CONSOLE_AppendHistory(ctx.line); + + CloseHandle(ctx.hConOut); + if (ctx.histCurr) HeapFree(GetProcessHeap(), 0, ctx.histCurr); + return ctx.line; +} +