From af47236499235349187c00a9be981a713fa5041c Mon Sep 17 00:00:00 2001 From: Jacek Caban Date: Mon, 31 Aug 2020 15:11:00 +0200 Subject: [PATCH] conhost: Implement IOCTL_CONDRV_SCROLL. Signed-off-by: Jacek Caban Signed-off-by: Alexandre Julliard --- programs/conhost/conhost.c | 101 +++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/programs/conhost/conhost.c b/programs/conhost/conhost.c index 80f09e738a7..0050f4d1add 100644 --- a/programs/conhost/conhost.c +++ b/programs/conhost/conhost.c @@ -673,6 +673,102 @@ static NTSTATUS fill_output( struct screen_buffer *screen_buffer, const struct c return STATUS_SUCCESS; } +static NTSTATUS scroll_output( struct screen_buffer *screen_buffer, const struct condrv_scroll_params *params ) +{ + int x, y, xsrc, ysrc, w, h; + char_info_t *psrc, *pdst; + SMALL_RECT src, dst; + SMALL_RECT clip; + + xsrc = params->scroll.Left; + ysrc = params->scroll.Top; + w = params->scroll.Right - params->scroll.Left + 1; + h = params->scroll.Bottom - params->scroll.Top + 1; + + TRACE( "(%d %d) -> (%u %u) w %u h %u\n", xsrc, ysrc, params->origin.X, params->origin.Y, w, h ); + + clip.Left = max( params->clip.Left, 0 ); + clip.Top = max( params->clip.Top, 0 ); + clip.Right = min( params->clip.Right, screen_buffer->width - 1 ); + clip.Bottom = min( params->clip.Bottom, screen_buffer->height - 1 ); + if (clip.Left > clip.Right || clip.Top > clip.Bottom || params->scroll.Left < 0 || params->scroll.Top < 0 || + params->scroll.Right >= screen_buffer->width || params->scroll.Bottom >= screen_buffer->height || + params->scroll.Right < params->scroll.Left || params->scroll.Top > params->scroll.Bottom || + params->origin.X < 0 || params->origin.X >= screen_buffer->width || params->origin.Y < 0 || + params->origin.Y >= screen_buffer->height) + return STATUS_INVALID_PARAMETER; + + src.Left = max( xsrc, clip.Left ); + src.Top = max( ysrc, clip.Top ); + src.Right = min( xsrc + w - 1, clip.Right ); + src.Bottom = min( ysrc + h - 1, clip.Bottom ); + + dst.Left = params->origin.X; + dst.Top = params->origin.Y; + dst.Right = params->origin.X + w - 1; + dst.Bottom = params->origin.Y + h - 1; + + if (dst.Left < clip.Left) + { + xsrc += clip.Left - dst.Left; + w -= clip.Left - dst.Left; + dst.Left = clip.Left; + } + if (dst.Top < clip.Top) + { + ysrc += clip.Top - dst.Top; + h -= clip.Top - dst.Top; + dst.Top = clip.Top; + } + if (dst.Right > clip.Right) w -= dst.Right - clip.Right; + if (dst.Bottom > clip.Bottom) h -= dst.Bottom - clip.Bottom; + + if (w > 0 && h > 0) + { + if (ysrc < dst.Top) + { + psrc = &screen_buffer->data[(ysrc + h - 1) * screen_buffer->width + xsrc]; + pdst = &screen_buffer->data[(dst.Top + h - 1) * screen_buffer->width + dst.Left]; + + for (y = h; y > 0; y--) + { + memcpy( pdst, psrc, w * sizeof(*pdst) ); + pdst -= screen_buffer->width; + psrc -= screen_buffer->width; + } + } + else + { + psrc = &screen_buffer->data[ysrc * screen_buffer->width + xsrc]; + pdst = &screen_buffer->data[dst.Top * screen_buffer->width + dst.Left]; + + for (y = 0; y < h; y++) + { + /* 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 * sizeof(*pdst) ); + pdst += screen_buffer->width; + psrc += screen_buffer->width; + } + } + } + + for (y = src.Top; y <= src.Bottom; y++) + { + int left = src.Left; + int right = src.Right; + if (dst.Top <= y && y <= dst.Bottom) + { + if (dst.Left <= src.Left) left = max( left, dst.Right + 1 ); + if (dst.Left >= src.Left) right = min( right, dst.Left - 1 ); + } + for (x = left; x <= right; x++) screen_buffer->data[y * screen_buffer->width + x] = params->fill; + } + + return STATUS_SUCCESS; +} + static NTSTATUS set_console_title( struct console *console, const WCHAR *in_title, size_t size ) { WCHAR *title = NULL; @@ -744,6 +840,11 @@ static NTSTATUS screen_buffer_ioctl( struct screen_buffer *screen_buffer, unsign return STATUS_INVALID_PARAMETER; return fill_output( screen_buffer, in_data ); + case IOCTL_CONDRV_SCROLL: + if (in_size != sizeof(struct condrv_scroll_params) || *out_size) + return STATUS_INVALID_PARAMETER; + return scroll_output( screen_buffer, in_data ); + default: FIXME( "unsupported ioctl %x\n", code ); return STATUS_NOT_SUPPORTED;