diff --git a/programs/conhost/conhost.c b/programs/conhost/conhost.c index dcdf6c9f5c6..5c5f184e6e7 100644 --- a/programs/conhost/conhost.c +++ b/programs/conhost/conhost.c @@ -294,11 +294,6 @@ static void init_tty_output( struct console *console ) console->tty_cursor_visible = TRUE; } -static void empty_update_rect( struct screen_buffer *screen_buffer, RECT *rect ) -{ - SetRect( rect, screen_buffer->width, screen_buffer->height, 0, 0 ); -} - static void scroll_to_cursor( struct screen_buffer *screen_buffer ) { int w = screen_buffer->win.right - screen_buffer->win.left + 1; diff --git a/programs/conhost/conhost.h b/programs/conhost/conhost.h index 3aaf237eb6c..49c3cccbce3 100644 --- a/programs/conhost/conhost.h +++ b/programs/conhost/conhost.h @@ -133,4 +133,9 @@ struct screen_buffer BOOL init_window( struct console *console ); NTSTATUS change_screen_buffer_size( struct screen_buffer *screen_buffer, int new_width, int new_height ); +static inline void empty_update_rect( struct screen_buffer *screen_buffer, RECT *rect ) +{ + SetRect( rect, screen_buffer->width, screen_buffer->height, 0, 0 ); +} + #endif /* RC_INVOKED */ diff --git a/programs/conhost/window.c b/programs/conhost/window.c index bc6d778e2e1..4085fd7a579 100644 --- a/programs/conhost/window.c +++ b/programs/conhost/window.c @@ -29,11 +29,19 @@ WINE_DEFAULT_DEBUG_CHANNEL(conhost); +enum update_state +{ + UPDATE_NONE, + UPDATE_PENDING, + UPDATE_BUSY +}; + struct console_window { HDC mem_dc; /* memory DC holding the bitmap below */ HBITMAP bitmap; /* bitmap of display window content */ HFONT font; /* font used for rendering, usually fixed */ + HBITMAP cursor_bitmap; /* bitmap used for the caret */ unsigned int ui_charset; /* default UI charset */ WCHAR *config_key; /* config registry key name */ LONG ext_leading; /* external leading for font */ @@ -48,6 +56,9 @@ struct console_window unsigned int sb_width; /* active screen buffer width */ unsigned int sb_height; /* active screen buffer height */ COORD cursor_pos; /* cursor position */ + + RECT update; /* screen buffer update rect */ + enum update_state update_state; /* update state */ }; struct console_config @@ -356,6 +367,251 @@ static void save_config( const WCHAR *key_name, const struct console_config *con RegCloseKey(key); } +/* fill memory DC with current cells values */ +static void fill_mem_dc( struct console *console, const RECT *update ) +{ + unsigned int i, j, k; + unsigned int attr; + char_info_t *cell; + HFONT old_font; + HBRUSH brush; + WCHAR *line; + INT *dx; + RECT r; + + if (!console->window->font) + return; + + if (!(line = malloc( (update->right - update->left + 1) * sizeof(WCHAR))) ) return; + dx = malloc( (update->right - update->left + 1) * sizeof(*dx) ); + + old_font = SelectObject( console->window->mem_dc, console->window->font ); + for (j = update->top; j <= update->bottom; j++) + { + cell = &console->active->data[j * console->active->width]; + for (i = update->left; i <= update->right; i++) + { + attr = cell[i].attr; + SetBkColor( console->window->mem_dc, console->active->color_map[(attr >> 4) & 0x0F] ); + SetTextColor( console->window->mem_dc, console->active->color_map[attr & 0x0F] ); + for (k = i; k <= update->right && cell[k].attr == attr; k++) + { + line[k - i] = cell[k].ch; + dx[k - i] = console->active->font.width; + } + ExtTextOutW( console->window->mem_dc, i * console->active->font.width, + j * console->active->font.height, 0, NULL, line, k - i, dx ); + if (console->window->ext_leading && + (brush = CreateSolidBrush( console->active->color_map[(attr >> 4) & 0x0F] ))) + { + r.left = i * console->active->font.width; + r.top = (j + 1) * console->active->font.height - console->window->ext_leading; + r.right = k * console->active->font.width; + r.bottom = (j + 1) * console->active->font.height; + FillRect( console->window->mem_dc, &r, brush ); + DeleteObject( brush ); + } + i = k - 1; + } + } + SelectObject( console->window->mem_dc, old_font ); + free( dx ); + free( line ); +} + +/* set a new position for the cursor */ +static void update_window_cursor( struct console *console ) +{ + if (console->win != GetFocus() || !console->active->cursor_visible) return; + + SetCaretPos( (console->active->cursor_x - console->active->win.left) * console->active->font.width, + (console->active->cursor_y - console->active->win.top) * console->active->font.height ); + ShowCaret( console->win ); +} + +/* sets a new shape for the cursor */ +static void shape_cursor( struct console *console ) +{ + int size = console->active->cursor_size; + + if (console->active->cursor_visible && console->win == GetFocus()) DestroyCaret(); + if (console->window->cursor_bitmap) DeleteObject( console->window->cursor_bitmap ); + console->window->cursor_bitmap = NULL; + console->window->cursor_visible = FALSE; + + if (size != 100) + { + int w16b; /* number of bytes per row, aligned on word size */ + int i, j, nbl; + BYTE *ptr; + + w16b = ((console->active->font.width + 15) & ~15) / 8; + ptr = calloc( w16b, console->active->font.height ); + if (!ptr) return; + nbl = max( (console->active->font.height * size) / 100, 1 ); + for (j = console->active->font.height - nbl; j < console->active->font.height; j++) + { + for (i = 0; i < console->active->font.width; i++) + { + ptr[w16b * j + (i / 8)] |= 0x80 >> (i & 7); + } + } + console->window->cursor_bitmap = CreateBitmap( console->active->font.width, + console->active->font.height, 1, 1, ptr ); + free(ptr); + } +} + +static void update_window( struct console *console ) +{ + unsigned int win_width, win_height; + BOOL update_all = FALSE; + int dx, dy; + RECT r; + + console->window->update_state = UPDATE_BUSY; + + if (console->window->sb_width != console->active->width || + console->window->sb_height != console->active->height || + !console->window->bitmap) + { + console->window->sb_width = console->active->width; + console->window->sb_height = console->active->height; + + if (console->active->width && console->active->height && console->window->font) + { + HBITMAP bitmap; + HDC dc; + RECT r; + + if (!(dc = GetDC( console->win ))) return; + + bitmap = CreateCompatibleBitmap( dc, + console->active->width * console->active->font.width, + console->active->height * console->active->font.height ); + ReleaseDC( console->win, dc ); + SelectObject( console->window->mem_dc, bitmap ); + + if (console->window->bitmap) DeleteObject( console->window->bitmap ); + console->window->bitmap = bitmap; + SetRect( &r, 0, 0, console->active->width - 1, console->active->height - 1 ); + fill_mem_dc( console, &r ); + } + + empty_update_rect( console->active, &console->window->update ); + update_all = TRUE; + } + + /* compute window size from desired client size */ + win_width = console->active->win.right - console->active->win.left + 1; + win_height = console->active->win.bottom - console->active->win.top + 1; + + if (update_all || win_width != console->window->win_width || + win_height != console->window->win_height) + { + console->window->win_width = win_width; + console->window->win_height = win_height; + + r.left = r.top = 0; + r.right = win_width * console->active->font.width; + r.bottom = win_height * console->active->font.height; + AdjustWindowRect( &r, GetWindowLongW( console->win, GWL_STYLE ), FALSE ); + + dx = dy = 0; + if (console->active->width > win_width) + { + dy = GetSystemMetrics( SM_CYHSCROLL ); + SetScrollRange( console->win, SB_HORZ, 0, console->active->width - win_width, FALSE ); + SetScrollPos( console->win, SB_VERT, console->active->win.top, FALSE ); + ShowScrollBar( console->win, SB_HORZ, TRUE ); + } + else + { + ShowScrollBar( console->win, SB_HORZ, FALSE ); + } + + if (console->active->height > win_height) + { + dx = GetSystemMetrics( SM_CXVSCROLL ); + SetScrollRange( console->win, SB_VERT, 0, console->active->height - win_height, FALSE ); + SetScrollPos( console->win, SB_VERT, console->active->win.top, FALSE ); + ShowScrollBar( console->win, SB_VERT, TRUE ); + } + else + ShowScrollBar( console->win, SB_VERT, FALSE ); + + dx += r.right - r.left; + dy += r.bottom - r.top; + SetWindowPos( console->win, 0, 0, 0, dx, dy, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE ); + + SystemParametersInfoW( SPI_GETWORKAREA, 0, &r, 0 ); + console->active->max_width = (r.right - r.left) / console->active->font.width; + console->active->max_height = (r.bottom - r.top - GetSystemMetrics( SM_CYCAPTION )) / + console->active->font.height; + + InvalidateRect( console->win, NULL, FALSE ); + UpdateWindow( console->win ); + update_all = TRUE; + } + else if (console->active->win.left != console->window->win_pos.X || + console->active->win.top != console->window->win_pos.Y) + { + ScrollWindow( console->win, + (console->window->win_pos.X - console->active->win.left) * console->active->font.width, + (console->window->win_pos.Y - console->active->win.top) * console->active->font.height, + NULL, NULL ); + SetScrollPos( console->win, SB_HORZ, console->active->win.left, TRUE ); + SetScrollPos( console->win, SB_VERT, console->active->win.top, TRUE ); + InvalidateRect( console->win, NULL, FALSE ); + } + + console->window->win_pos.X = console->active->win.left; + console->window->win_pos.Y = console->active->win.top; + + if (console->window->update.top <= console->window->update.bottom && + console->window->update.left <= console->window->update.right) + { + RECT *update = &console->window->update; + r.left = (update->left - console->active->win.left) * console->active->font.width; + r.right = (update->right - console->active->win.left + 1) * console->active->font.width; + r.top = (update->top - console->active->win.top) * console->active->font.height; + r.bottom = (update->bottom - console->active->win.top + 1) * console->active->font.height; + fill_mem_dc( console, update ); + empty_update_rect( console->active, &console->window->update ); + InvalidateRect( console->win, &r, FALSE ); + UpdateWindow( console->win ); + } + + if (update_all || console->active->cursor_size != console->window->cursor_size) + { + console->window->cursor_size = console->active->cursor_size; + shape_cursor( console ); + } + + if (console->active->cursor_visible != console->window->cursor_visible) + { + console->window->cursor_visible = console->active->cursor_visible; + if (console->win == GetFocus()) + { + if (console->window->cursor_visible) + CreateCaret( console->win, console->window->cursor_bitmap, + console->active->font.width, console->active->font.height ); + else + DestroyCaret(); + } + } + + if (update_all || console->active->cursor_x != console->window->cursor_pos.X || + console->active->cursor_y != console->window->cursor_pos.Y) + { + console->window->cursor_pos.X = console->active->cursor_x; + console->window->cursor_pos.Y = console->active->cursor_y; + update_window_cursor( console ); + } + + console->window->update_state = UPDATE_NONE; +} + static void fill_logfont( LOGFONTW *lf, const WCHAR *name, unsigned int height, unsigned int weight ) { lf->lfHeight = height; @@ -640,6 +896,8 @@ static void apply_config( struct console *console, const struct console_config * { update_console_font( console, config->face_name, config->cell_height, config->font_weight ); } + + update_window( console ); } static LRESULT window_create( HWND hwnd, const CREATESTRUCTW *create ) @@ -651,6 +909,7 @@ static LRESULT window_create( HWND hwnd, const CREATESTRUCTW *create ) SetWindowLongPtrW( hwnd, 0, (DWORD_PTR)console ); console->win = hwnd; + console->window->mem_dc = CreateCompatibleDC( 0 ); return 0; }