diff --git a/include/wine/condrv.h b/include/wine/condrv.h index 839db84ea58..a6df63155cf 100644 --- a/include/wine/condrv.h +++ b/include/wine/condrv.h @@ -22,6 +22,7 @@ #define _INC_CONDRV #include "winioctl.h" +#include "wincon.h" /* common console input and output ioctls */ #define IOCTL_CONDRV_GET_MODE CTL_CODE(FILE_DEVICE_CONSOLE, 0, METHOD_BUFFERED, FILE_READ_PROPERTIES) @@ -43,6 +44,7 @@ #define IOCTL_CONDRV_SET_OUTPUT_INFO CTL_CODE(FILE_DEVICE_CONSOLE, 33, METHOD_BUFFERED, FILE_WRITE_PROPERTIES) #define IOCTL_CONDRV_ACTIVATE CTL_CODE(FILE_DEVICE_CONSOLE, 34, METHOD_BUFFERED, FILE_WRITE_DATA) #define IOCTL_CONDRV_FILL_OUTPUT CTL_CODE(FILE_DEVICE_CONSOLE, 35, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_CONDRV_SCROLL CTL_CODE(FILE_DEVICE_CONSOLE, 36, METHOD_BUFFERED, FILE_WRITE_DATA) /* console renderer ioctls */ #define IOCTL_CONDRV_GET_RENDERER_EVENTS CTL_CODE(FILE_DEVICE_CONSOLE, 70, METHOD_BUFFERED, FILE_READ_PROPERTIES) @@ -133,6 +135,16 @@ struct condrv_output_info_params struct condrv_output_info info; /* output info */ }; +#define SET_CONSOLE_OUTPUT_INFO_CURSOR_GEOM 0x0001 +#define SET_CONSOLE_OUTPUT_INFO_CURSOR_POS 0x0002 +#define SET_CONSOLE_OUTPUT_INFO_SIZE 0x0004 +#define SET_CONSOLE_OUTPUT_INFO_ATTR 0x0008 +#define SET_CONSOLE_OUTPUT_INFO_DISPLAY_WINDOW 0x0010 +#define SET_CONSOLE_OUTPUT_INFO_MAX_SIZE 0x0020 +#define SET_CONSOLE_OUTPUT_INFO_FONT 0x0040 +#define SET_CONSOLE_OUTPUT_INFO_COLORTABLE 0x0080 +#define SET_CONSOLE_OUTPUT_INFO_POPUP_ATTR 0x0100 + /* IOCTL_CONDRV_FILL_OUTPUT params */ struct condrv_fill_output_params { @@ -145,15 +157,14 @@ struct condrv_fill_output_params unsigned short attr; /* attribute to write */ }; -#define SET_CONSOLE_OUTPUT_INFO_CURSOR_GEOM 0x0001 -#define SET_CONSOLE_OUTPUT_INFO_CURSOR_POS 0x0002 -#define SET_CONSOLE_OUTPUT_INFO_SIZE 0x0004 -#define SET_CONSOLE_OUTPUT_INFO_ATTR 0x0008 -#define SET_CONSOLE_OUTPUT_INFO_DISPLAY_WINDOW 0x0010 -#define SET_CONSOLE_OUTPUT_INFO_MAX_SIZE 0x0020 -#define SET_CONSOLE_OUTPUT_INFO_FONT 0x0040 -#define SET_CONSOLE_OUTPUT_INFO_COLORTABLE 0x0080 -#define SET_CONSOLE_OUTPUT_INFO_POPUP_ATTR 0x0100 +/* IOCTL_CONDRV_SCROLL params */ +struct condrv_scroll_params +{ + SMALL_RECT scroll; /* source rectangle */ + COORD origin; /* destination coordinates */ + SMALL_RECT clip; /* clipping rectangle */ + char_info_t fill; /* empty character info */ +}; /* IOCTL_CONDRV_GET_RENDERER_EVENTS result */ struct condrv_renderer_event diff --git a/server/console.c b/server/console.c index 8bd3e94e657..f924ddc06b1 100644 --- a/server/console.c +++ b/server/console.c @@ -1446,7 +1446,85 @@ static int fill_console_output( struct screen_buffer *screen_buffer, char_info_t /* scroll parts of a screen buffer */ static void scroll_console_output( struct screen_buffer *screen_buffer, int xsrc, int ysrc, int xdst, int ydst, - int w, int h ) + int w, int h, const rectangle_t *clip, char_info_t fill ) +{ + struct condrv_renderer_event evt; + rectangle_t src, dst; + int x, y; + + 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 = xdst; + dst.top = ydst; + dst.right = xdst + w - 1; + dst.bottom = ydst + 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 < ydst) + { + for (y = h; y > 0; y--) + { + memcpy( &screen_buffer->data[(dst.top + y - 1) * screen_buffer->width + dst.left], + &screen_buffer->data[(ysrc + y - 1) * screen_buffer->width + xsrc], + w * sizeof(screen_buffer->data[0]) ); + } + } + else + { + 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( &screen_buffer->data[(dst.top + y) * screen_buffer->width + dst.left], + &screen_buffer->data[(ysrc + y) * screen_buffer->width + xsrc], + w * sizeof(screen_buffer->data[0]) ); + } + } + } + + 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] = fill; + } + + /* FIXME: this could be enhanced, by signalling scroll */ + evt.event = CONSOLE_RENDERER_UPDATE_EVENT; + memset(&evt.u, 0, sizeof(evt.u)); + evt.u.update.top = min( src.top, dst.top ); + evt.u.update.bottom = max( src.bottom, dst.bottom ); + console_input_events_append( screen_buffer->input, &evt ); +} + +/* scroll parts of a screen buffer */ +static void scroll_console_output_req( struct screen_buffer *screen_buffer, int xsrc, int ysrc, int xdst, int ydst, + int w, int h ) { int j; char_info_t *psrc, *pdst; @@ -1811,6 +1889,40 @@ static int screen_buffer_ioctl( struct fd *fd, ioctl_code_t code, struct async * return !get_error(); } + case IOCTL_CONDRV_SCROLL: + { + const struct condrv_scroll_params *params = get_req_data(); + rectangle_t clip; + if (get_req_data_size() != sizeof(*params)) + { + set_error( STATUS_INVALID_PARAMETER ); + return 0; + } + if (console_input_is_bare( screen_buffer->input ) || !screen_buffer->input) + { + set_error( STATUS_OBJECT_TYPE_MISMATCH ); + return 0; + } + 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) + { + set_error( STATUS_INVALID_PARAMETER ); + return 0; + } + + scroll_console_output( screen_buffer, params->scroll.Left, params->scroll.Top, params->origin.X, params->origin.Y, + params->scroll.Right - params->scroll.Left + 1, params->scroll.Bottom - params->scroll.Top + 1, + &clip, params->fill ); + return !get_error(); + } + default: set_error( STATUS_INVALID_HANDLE ); return 0; @@ -2182,8 +2294,8 @@ DECL_HANDLER(move_console_output) release_object( screen_buffer ); return; } - scroll_console_output( screen_buffer, req->x_src, req->y_src, req->x_dst, req->y_dst, - req->w, req->h ); + scroll_console_output_req( screen_buffer, req->x_src, req->y_src, req->x_dst, req->y_dst, + req->w, req->h ); release_object( screen_buffer ); } }