diff options
| author | Javier <dev.git@javispedro.com> | 2022-04-03 21:23:10 +0200 | 
|---|---|---|
| committer | Javier <dev.git@javispedro.com> | 2022-04-03 21:23:10 +0200 | 
| commit | a5abb118e0e61587adfa7b4e4cc72948311381d3 (patch) | |
| tree | b226705401bd0b4c78399c621cebc1fbab6003b9 | |
| parent | 7d93442564b57c2d292df7f823c2115d3e0b8c12 (diff) | |
| download | vbados-a5abb118e0e61587adfa7b4e4cc72948311381d3.tar.gz vbados-a5abb118e0e61587adfa7b4e4cc72948311381d3.zip  | |
try to handle graphical cursor rendering in a more generic way
| -rw-r--r-- | dosmain.c | 19 | ||||
| -rw-r--r-- | dostsr.c | 372 | ||||
| -rw-r--r-- | dostsr.h | 17 | ||||
| -rw-r--r-- | int10vga.h | 196 | ||||
| -rw-r--r-- | int33.h | 9 | ||||
| -rw-r--r-- | vbox.h | 9 | 
6 files changed, 398 insertions, 224 deletions
@@ -24,6 +24,7 @@  #include <i86.h>  #include "dlog.h" +#include "int33.h"  #include "ps2.h"  #include "vbox.h"  #include "dostsr.h" @@ -85,7 +86,7 @@ static int set_integration(LPTSRDATA data, bool enable)  			printf("Disabled VirtualBox integration\n");  			data->vbavail = false;  		} else { -			printf("VirtualBox already disabled or not available\n"); +			printf("VirtualBox integration already disabled or not available\n");  		}  	} @@ -164,17 +165,17 @@ static bool check_if_driver_uninstallable(LPTSRDATA data)  	void (__interrupt __far *our_int33_handler)() = MK_FP(FP_SEG(data), FP_OFF(int33_isr));  	if (cur_int33_handler != our_int33_handler) { -		fprintf(stderr, "INT33 has been hooked by some other driver, removing anyway (will likely crash soon)\n"); +		fprintf(stderr, "INT33 has been hooked by someone else, removing anyway\n");  		return true;  	}  #if USE_INT2F  	{ -		void (__interrupt __far *cur_int2f_handler)() = _dos_getvect(0x33); +		void (__interrupt __far *cur_int2f_handler)() = _dos_getvect(0x2f);  		void (__interrupt __far *our_int2f_handler)() = MK_FP(FP_SEG(data), FP_OFF(int2f_isr));  		if (cur_int2f_handler != our_int2f_handler) { -			fprintf(stderr, "INT2F has been hooked by some other driver, removing anyway (will likely crash soon)\n"); +			fprintf(stderr, "INT2F has been hooked by someone else, removing anyway\n");  			return true;  		}  	} @@ -214,18 +215,10 @@ static int uninstall_driver(LPTSRDATA data)  	return 0;  } -static unsigned int33_call(unsigned ax); -#pragma aux int33_call parm [ax] value [ax] = \ -	"int 0x33"; - -static unsigned int2f_call(unsigned ax); -#pragma aux int2f_call parm [ax] value [ax] = \ -	"int 0x2f"; -  static int driver_reset(void)  {  	printf("Reset mouse driver\n"); -	return int33_call(0x0) == 0xFFFF; +	return int33_reset() == 0xFFFF;  }  static int driver_not_found(void) @@ -26,6 +26,8 @@  #include "int33.h"  #include "dostsr.h" +#define MSB_MASK 0x8000U +  TSRDATA data;  static const uint16_t default_cursor_graphic[] = { @@ -39,6 +41,7 @@ static const uint16_t default_cursor_graphic[] = {      0x0600, 0x0300, 0x0300, 0x0000  }; +/** Constraint current mouse position to the user-set window. */  static void bound_position_to_window(void)  {  	if (data.pos.x < data.min.x) data.pos.x = data.min.x; @@ -47,24 +50,10 @@ static void bound_position_to_window(void)  	if (data.pos.y > data.max.y) data.pos.y = data.max.y;  } -static inline bool is_text_mode(uint8_t mode) -{ -	switch (mode) { -	case 0: -	case 1: -	case 2: -	case 3: // CGA text modes with 25 rows and variable columns -	case 7: // MDA Mono text mode -		return true; -	default: -		return false; -	} -} -  static void hide_text_cursor(void)  {  	// Restore the character under the old position of the cursor -	uint16_t __far *ch = get_video_char(data.screen_page, +	uint16_t __far *ch = get_video_char(&data.video_mode,  	                                    data.cursor_pos.x / 8, data.cursor_pos.y / 8);  	*ch = data.cursor_prev_char;  	data.cursor_visible = false; @@ -72,7 +61,7 @@ static void hide_text_cursor(void)  static void show_text_cursor(void)  { -	uint16_t __far *ch = get_video_char(data.screen_page, +	uint16_t __far *ch = get_video_char(&data.video_mode,  	                                    data.pos.x / 8, data.pos.y / 8);  	data.cursor_prev_char = *ch;  	data.cursor_pos = data.pos; @@ -80,15 +69,17 @@ static void show_text_cursor(void)  	data.cursor_visible = true;  } +/** Given the new position of the cursor, compute the both our source + *  (i.e. the mouse cursor shape) and target clipping areas. + *  @param cursor_pos cursor position + *  @param start top left corner of clipping box in screen + *  @param size size of the clipping box in screen as well as cursor shape + *  @param offset top left corner of clipping box in mouse cursor shape */  static bool get_graphic_cursor_area(struct point __far *cursor_pos,                                      struct point __far *start,                                      struct point __far *size,                                      struct point __far *offset)  { -	struct point screen_size; -	screen_size.x = ((data.screen_max.x + 1) / data.screen_scale.x); -	screen_size.y = ((data.screen_max.y + 1) / data.screen_scale.y); -  	start->x = (cursor_pos->x / data.screen_scale.x) - data.cursor_hotspot.x;  	start->y = (cursor_pos->y / data.screen_scale.y) - data.cursor_hotspot.y;  	size->x = GRAPHIC_CURSOR_WIDTH; @@ -97,133 +88,176 @@ static bool get_graphic_cursor_area(struct point __far *cursor_pos,  	offset->y = 0;  	// Start clipping around -	if (start->x < 0) { +	// Cursor is left/top of visible area +	if (start->x <= -size->x) { +		return false; +	} else if (start->x < 0) {  		offset->x += -start->x; +		size->x   -= -start->x;  		start->x = 0;  	} -	if (start->y < 0) { +	if (start->y <= -size->y) { +		return false; +	} else if (start->y < 0) {  		offset->y += -start->y; +		size->y   -= -start->y;  		start->y = 0;  	} -	if (start->x > screen_size.x) { +	// Cursor is right/bottom of visible area +	if (start->x > data.video_mode.pixels_width) {  		return false; // Don't render cursor -	} else if (start->x + size->x > screen_size.x) { -		size->x -= (start->x + size->x) - screen_size.x; +	} else if (start->x + size->x > data.video_mode.pixels_width) { +		size->x -= (start->x + size->x) - data.video_mode.pixels_width;  	} -	if (start->y > screen_size.y) { +	if (start->y > data.video_mode.pixels_height) {  		return false; -	} else if (start->y + size->y > screen_size.y) { -		size->y -= (start->y + size->y) - screen_size.y; +	} else if (start->y + size->y > data.video_mode.pixels_height) { +		size->y -= (start->y + size->y) - data.video_mode.pixels_height;  	}  	return true;  } -static inline uint8_t * get_prev_graphic_cursor_scanline(unsigned y) +static inline uint8_t * get_prev_graphic_cursor_scanline(unsigned bytes_per_line, +                                                         unsigned num_lines, +                                                         unsigned plane, +                                                         unsigned y)  { -	return &data.cursor_prev_graphic[y * GRAPHIC_CURSOR_WIDTH]; +	return &data.cursor_prev_graphic[((plane * num_lines) + y) * bytes_per_line];  } -static inline uint16_t get_graphic_cursor_and_mask(unsigned y) +static inline uint16_t get_graphic_cursor_and_mask_line(unsigned y)  {  	return data.cursor_graphic[y];  } -static inline uint16_t get_graphic_cursor_xor_mask(unsigned y) +static inline uint16_t get_graphic_cursor_xor_mask_line(unsigned y)  {  	return data.cursor_graphic[GRAPHIC_CURSOR_HEIGHT + y];  } +/** Compute the total number of bytes between start and end pixels, + *  rounding up as necessary to cover all bytes. */ +static inline unsigned get_scanline_segment_bytes(unsigned bits_per_pixel, unsigned start, unsigned size) +{ +	// Get starting byte (round down) +	unsigned start_byte = (start * bits_per_pixel) / 8; +	// Get end byte (round up) +	unsigned end_byte = (((start + size) * bits_per_pixel) + (8-1)) / 8; + +	return end_byte - start_byte; +} + +/** Creates a bitmask that extracts the topmost N bits of a byte. */ +static inline uint8_t build_pixel_mask(unsigned bits_per_pixel) +{ +	if      (bits_per_pixel == 1) return 0x80; +	else if (bits_per_pixel == 2) return 0xC0; +	else if (bits_per_pixel == 4) return 0xF0; +	else                          return 0xFF; +} + +/** Hides the graphical mouse cursor, by restoring the contents of + *  data.cursor_prev_graphic (i.e. what was below the cursor before we drew it) + *  to video memory. */  static void hide_graphic_cursor(void)  { -	uint8_t __far *pixel; -	uint8_t *cursor_prev; +	struct modeinfo *info = &data.video_mode;  	struct point start, size, offset; -	unsigned pixels_per_byte, y, x; +	unsigned cursor_bytes_per_line; +	unsigned plane, y;  	// Compute the area where the cursor is currently positioned  	if (!get_graphic_cursor_area(&data.cursor_pos, &start, &size, &offset)) {  		return;  	} -	switch (data.screen_mode) { -	case 4: -	case 5: -	case 6: // CGA modes -		pixels_per_byte = data.screen_mode == 0x6 ? 8 : 4; +	// For each scanline, we will copy this amount of bytes +	cursor_bytes_per_line = get_scanline_segment_bytes(info->bits_per_pixel, +	                                                   start.x, size.x); + +	for (plane = 0; plane < info->num_planes; plane++) { +		if (info->num_planes) vga_select_plane(plane);  		for (y = 0; y < size.y; y++) { -			cursor_prev = get_prev_graphic_cursor_scanline(offset.y + y); -			pixel = get_video_scanline(data.screen_mode, data.screen_page, start.y + y) -			        + start.x / pixels_per_byte; +			uint8_t __far *line = get_video_scanline(info, start.y + y) +			                      + (start.x * info->bits_per_pixel) / 8; +			uint8_t *prev = get_prev_graphic_cursor_scanline(cursor_bytes_per_line, size.y, +			                                                 plane, y);  			// Restore this scaline from cursor_prev -			nfmemcpy(pixel, cursor_prev, -			         ((start.x % pixels_per_byte) + size.x + (pixels_per_byte - 1)) / pixels_per_byte); +			nfmemcpy(line, prev, cursor_bytes_per_line);  		} - -		break;  	}  	data.cursor_visible = false;  } +/** Renders the graphical cursor. + *  It will also backup whatever pixels are below + *  the cursor area to cursor_prev_graphic. */  static void show_graphic_cursor(void)  { -	static const uint16_t msb_mask = 0x8000; -	uint16_t cursor_and_mask, cursor_xor_mask; -	uint8_t __far *pixel, pixel_mask; -	uint8_t *cursor_prev; +	const struct modeinfo *info = &data.video_mode;  	struct point start, size, offset; -	unsigned pixels_per_byte, x, y; +	unsigned cursor_bytes_per_line; +	const uint8_t msb_pixel_mask = build_pixel_mask(info->bits_per_pixel); +	unsigned plane, y;  	// Compute the area where the cursor is supposed to be drawn  	if (!get_graphic_cursor_area(&data.pos, &start, &size, &offset)) {  		return;  	} -	switch (data.screen_mode) { -	case 4: -	case 5: -	case 6: // CGA modes -		pixels_per_byte = data.screen_mode == 0x6 ? 8 : 4; +	// For each scanline, we will copy this amount of bytes +	cursor_bytes_per_line = get_scanline_segment_bytes(info->bits_per_pixel, +	                                                   start.x, size.x); + +	for (plane = 0; plane < info->num_planes; plane++) { +		if (info->num_planes) vga_select_plane(plane);  		for (y = 0; y < size.y; y++) { -			cursor_and_mask = get_graphic_cursor_and_mask(offset.y + y) << offset.x; -			cursor_xor_mask = get_graphic_cursor_xor_mask(offset.y + y) << offset.y; -			cursor_prev = get_prev_graphic_cursor_scanline(offset.y + y); -			pixel = get_video_scanline(data.screen_mode, data.screen_page, start.y + y) -			        + start.x / pixels_per_byte; - -			// First copy this scanline to cursor_prev before any changes -			fnmemcpy(cursor_prev, pixel, -			         ((start.x % pixels_per_byte) + size.x + (pixels_per_byte - 1)) / pixels_per_byte); - -			// pixel points the previous multiple of pixels_per_byte; -			// now advance to the start of cursor while updating the pixel_mask -			pixel_mask = pixels_per_byte == 8 ? 0x80 : 0xC0; -			for (x = 0; x < (start.x % pixels_per_byte); x++) { -				pixel_mask >>= 8 / pixels_per_byte; +			uint8_t __far *line = get_video_scanline(info, start.y + y) +			                      + (start.x * info->bits_per_pixel) / 8; +			uint8_t *prev = get_prev_graphic_cursor_scanline(cursor_bytes_per_line, size.y, +			                                                 plane, y); +			uint16_t cursor_and_mask = get_graphic_cursor_and_mask_line(offset.y + y) +			                           << offset.x; +			uint16_t cursor_xor_mask = get_graphic_cursor_xor_mask_line(offset.y + y) +			                           << offset.x; +			uint8_t pixel_mask = msb_pixel_mask; +			unsigned x; + +			// First, backup this scanline to prev before any changes +			fnmemcpy(prev, line, cursor_bytes_per_line); + +			// when start.x is not pixel aligned, +			// scaline points the previous multiple of pixels_per_byte; +			// and the initial pixel will not be at the MSB of it. +			// advance the pixel_mask accordingly +			if (info->bits_per_pixel < 8) { +				pixel_mask >>= (start.x * info->bits_per_pixel) % 8;  			} -			// Now pixel points to the start of cursor -			for (; x < (start.x % pixels_per_byte) + size.x; x++) { -				uint8_t rest = *pixel & ~pixel_mask; +			for (x = 0; x < size.x; x++) { +				uint8_t pixel = *line; -				if (!(cursor_and_mask & msb_mask)) { -					*pixel = rest; +				if (!(cursor_and_mask & MSB_MASK)) { +					pixel &= ~pixel_mask;  				} -				if (cursor_xor_mask & msb_mask) { -					*pixel = rest | (*pixel ^ 0xFF) & pixel_mask; +				if (cursor_xor_mask & MSB_MASK) { +					pixel ^= pixel_mask; +				} +				if (!(cursor_and_mask & MSB_MASK) || (cursor_xor_mask & MSB_MASK)) { +					*line = pixel;  				}  				// Advance to the next pixel -				if (x % pixels_per_byte == pixels_per_byte - 1) { -					// Next iteration starts new byte, reload pixel_mask -					pixel++; -					pixel_mask = pixels_per_byte == 8 ? 0x80 : 0xC0; -				} else { -					pixel_mask >>= 8 / pixels_per_byte; +				pixel_mask >>= info->bits_per_pixel; +				if (!pixel_mask) { +					// Time to advance to the next byte +					line++; +					pixel_mask = msb_pixel_mask;  				}  				// Advance to the next bit in the cursor mask @@ -231,12 +265,6 @@ static void show_graphic_cursor(void)  				cursor_xor_mask <<= 1;  			}  		} -		break; -	default: -		dlog_print("Graphic mode 0x"); -		dlog_printx(data.screen_mode); -		dlog_puts(" not supported"); -		return;  	}  	data.cursor_pos = data.pos; @@ -247,6 +275,7 @@ static void show_graphic_cursor(void)  static void refresh_cursor(void)  {  	bool should_show = data.visible_count >= 0; +	bool pos_changed, needs_refresh;  #if USE_INT2F  	// Windows 386 is already rendering the cursor for us. @@ -256,21 +285,32 @@ static void refresh_cursor(void)  #if USE_VIRTUALBOX  	if (data.vbwantcursor) { +		// We want to use the VirtualBox host cursor. +		// See if we have to update its visibility.  		int err = 0;  		if (should_show != data.cursor_visible) { -			int err = vbox_set_pointer_visible(&data.vb, should_show); +			err = vbox_set_pointer_visible(&data.vb, should_show);  			if (err == 0 && data.vbhaveabs) {  				data.cursor_visible = should_show;  			}  		}  		if (err == 0 & data.vbhaveabs) { -			// No need to show the cursor; VirtualBox is already showing it for us. +			// No need to refresh the cursor; VirtualBox is already showing it for us.  			return;  		}  	}  #endif -	if (is_text_mode(data.screen_mode)) { +	pos_changed = data.cursor_pos.x != data.pos.x || data.cursor_pos.y != data.pos.y; +	needs_refresh = should_show && pos_changed || should_show != data.cursor_visible; + +	if (!needs_refresh) { +		// Nothing to do +		return; +	} + +	if (data.video_mode.type == VIDEO_TEXT) { +		// Text video mode  		if (data.cursor_visible) {  			// Hide the cursor at the old position if any  			hide_text_cursor(); @@ -279,13 +319,30 @@ static void refresh_cursor(void)  			// Show the cursor at the new position  			show_text_cursor();  		} -	} else { +	} else if (data.video_mode.type != VIDEO_UNKNOWN) { +		// Graphic video modes + +		bool video_planar = data.video_mode.num_planes > 1; +		struct videoregs regs; +		// If current video mode is planar, +		// we will have to play with the VGA registers +		// so let's save and restore them. +		if (video_planar) { +			save_video_registers(®s); +		} +  		if (data.cursor_visible) {  			hide_graphic_cursor();  		}  		if (should_show) {  			show_graphic_cursor();  		} + +		if (video_planar) { +			restore_video_registers(®s); +		} +	} else { +		// Unknown video mode, don't render cursor.  	}  } @@ -301,12 +358,10 @@ static void hide_cursor(void)  	}  #endif -	if (is_text_mode(data.screen_mode)) { -		if (data.cursor_visible) { +	if (data.cursor_visible) { +		if (data.video_mode.type == VIDEO_TEXT) {  			hide_text_cursor(); -		} -	} else { -		if (data.cursor_visible) { +		} else if (data.video_mode.type != VIDEO_UNKNOWN) {  			hide_graphic_cursor();  		}  	} @@ -320,15 +375,12 @@ static void load_cursor(void)  	if (data.vbwantcursor) {  		VMMDevReqMousePointer *req = (VMMDevReqMousePointer *) data.vb.buf;  		const unsigned width = GRAPHIC_CURSOR_WIDTH, height = GRAPHIC_CURSOR_HEIGHT; -		const unsigned and_mask_size = (width + 7) / 8 * height; -		const unsigned xor_mask_size = width * height * 4; -		const unsigned data_size = and_mask_size + xor_mask_size; -		const unsigned full_size = MAX(sizeof(VMMDevReqMousePointer), 24 + 20 + data_size); -		unsigned int offset = 0, y, x; +		uint8_t  *output = req->pointerData; +		unsigned int y, x; -		bzero(req, full_size); +		bzero(req, sizeof(VMMDevReqMousePointer)); -		req->header.size = full_size; +		req->header.size = vbox_req_mouse_pointer_size(width, height);  		req->header.version = VMMDEV_REQUEST_HEADER_VERSION;  		req->header.requestType = VMMDevReq_SetPointerShape;  		req->header.rc = -1; @@ -339,28 +391,34 @@ static void load_cursor(void)  		req->width = width;  		req->height = height; -		// Just byteswap the AND mask +		// AND mask +		// int33 format is 1-bit per pixel packed into 16-bit LE values, +		// while VirtualBox wants 1-bit per pixel packed into 8-bit. +		// All we have to do is byteswap 16-bit values.  		for (y = 0; y < height; ++y) { -			uint16_t line = get_graphic_cursor_and_mask(y); -			req->pointerData[(y*2)]   = (line >> 8) & 0xFF; -			req->pointerData[(y*2)+1] = line & 0xFF; +			uint16_t cursor_line = get_graphic_cursor_and_mask_line(y); +			output[0] = (cursor_line >> 8) & 0xFF; +			output[1] = cursor_line & 0xFF; +			output += GRAPHIC_CURSOR_SCANLINE_LEN;  		} -		offset += and_mask_size; -		// But the XOR mask needs to be converted to huge 4-byte "RGBA" format. +		// XOR mask +		// int33 format is again 1-bit per pixel packed into 16-bit LE values, +		// however VirtualBox wants 4-byte per pixel packed "RGBA".  		for (y = 0; y < height; ++y) { -			uint16_t line = get_graphic_cursor_xor_mask(y); +			uint16_t cursor_line = get_graphic_cursor_xor_mask_line(y);  			for (x = 0; x < width; ++x) { -				unsigned int pos = offset + (y * width * 4) + (x*4) + 0; -				uint8_t val = (line & 0x8000) ? 0xFF : 0; +				// MSB of line is current mask bit, we shift it on each iteration +				uint8_t val = (cursor_line & MSB_MASK) ? 0xFF : 0; -				req->pointerData[pos + 0] = val; -				req->pointerData[pos + 1] = val; -				req->pointerData[pos + 2] = val; -				req->pointerData[pos + 3] = 0; +				output[0] = val; +				output[1] = val; +				output[2] = val; +				output[3] = 0; -				line <<= 1; +				cursor_line <<= 1; +				output += 4;  			}  		} @@ -384,8 +442,8 @@ static void load_cursor(void)  /** Refreshes the information about the current video mode. */  static void refresh_video_info(void)  { -	uint8_t mode = bda_get_video_mode() & ~0x80; -	bool mode_change = mode != data.screen_mode; +	uint8_t cur_mode = bda_get_video_mode() & ~0x80; +	bool mode_change = cur_mode != data.video_mode.mode;  	if (mode_change && data.cursor_visible) {  		// Assume cursor is lost @@ -393,63 +451,22 @@ static void refresh_video_info(void)  	}  	dlog_print("Current video mode="); -	dlog_printx(mode); -	dlog_print(" with cols="); -	dlog_printd(bda_get_num_columns()); -	dlog_print(" lastrow="); -	dlog_printd(bda_get_last_row()); +	dlog_printx(cur_mode);  	dlog_endline(); -	data.screen_mode = mode; -	data.screen_page = bda_get_cur_video_page(); +	get_current_video_mode_info(&data.video_mode); + +	data.screen_max.x = data.video_mode.pixels_width - 1; +	data.screen_max.y = data.video_mode.pixels_height - 1;  	data.screen_scale.x = 1;  	data.screen_scale.y = 1; -	// Compute screen coordinates which are used to events from -	// absolute mouse coordinates. -	switch (mode) { -	case 0: -	case 1: -	case 2: -	case 3: // CGA text modes with 25 rows and variable columns -	case 7: // MDA Mono text mode -		data.screen_max.x = (bda_get_num_columns() * 8) - 1; -		data.screen_max.y = (                   25 * 8) - 1; -		break; - -	case 4: -	case 5: // CGA low-res modes -	case 0xd: // EGA low-res mode +	// The actual range of coordinates expected by int33 clients +	// is, for some reason, different than real resolution in some modes. +	// For example, 320x... modes are mapped to 640x... pixels. +	if (data.video_mode.pixels_width == 320) {  		data.screen_max.x = 640 - 1; -		data.screen_max.y = 200 - 1; -		data.screen_scale.x = 2; // Really 320x200 -		break; - -	case 6: // CGA hi-res mode -	case 0xe: // EGA modes -	case 0x13: // VGA 256color mode -		data.screen_max.x = 640 - 1; -		data.screen_max.y = 200 - 1; -		break; - -	case 0xf: -	case 0x10: // EGA 640x350 modes -		data.screen_max.x = 640 - 1; -		data.screen_max.y = 350 - 1; -		break; - -	case 0x11: -	case 0x12: // VGA 640x480 modes -		data.screen_max.x = 640 - 1; -		data.screen_max.y = 480 - 1; -		break; - -	default: -		// Unknown mode; assume default coordinates -		// Note that if program sets up larger window coordinates, we'll use that instead. -		data.screen_max.x = 640 - 1; -		data.screen_max.y = 200 - 1; -		break; +		data.screen_scale.x = 640 / 320;  	}  } @@ -602,6 +619,7 @@ static void handle_mouse_event(uint16_t buttons, bool absolute, int x, int y, in  	}  } +/** PS/2 BIOS calls this routine to notify mouse events. */  static void __far ps2_mouse_callback(uint8_t status, uint8_t x, uint8_t y, uint8_t z)  {  #pragma aux (PS2_CB) ps2_mouse_callback @@ -694,6 +712,7 @@ static void reset_mouse_hardware()  	ps2m_enable(true);  } +/** Reset "software" mouse settings, i.e. those configurable by the client program. */  static void reset_mouse_settings()  {  	data.event_mask = 0; @@ -717,6 +736,7 @@ static void reset_mouse_settings()  	refresh_cursor(); // This will hide the cursor and update data.cursor_visible  } +/** Reset the current mouse state and throw away past events. */  static void reset_mouse_state()  {  	int i; @@ -765,6 +785,7 @@ static void return_clear_button_counter(union INTPACK __far *r, struct buttoncou  	c->count = 0;  } +/** Entry point for our int33 API. */  static void int33_handler(union INTPACK r)  #pragma aux int33_handler "*" parm caller [] modify [ax bx cx dx es]  { @@ -861,7 +882,7 @@ static void int33_handler(union INTPACK r)  		hide_cursor();  		data.cursor_hotspot.x = r.x.bx;  		data.cursor_hotspot.y = r.x.cx; -		fnmemcpy(data.cursor_graphic, MK_FP(r.x.es, r.x.dx), 64); +		fnmemcpy(data.cursor_graphic, MK_FP(r.x.es, r.x.dx), sizeof(data.cursor_graphic));  		load_cursor();  		refresh_cursor();  		break; @@ -1016,6 +1037,7 @@ void __declspec(naked) __far int33_isr(void)  }  #if USE_INT2F +/** Windows will call this function to notify events when we are inside a DOS box. */  static void windows_mouse_handler(int action, int x, int y, int buttons, int events)  #pragma aux windows_mouse_handler "*" parm [ax] [bx] [cx] [dx] [si] modify [ax bx cx dx es]  { @@ -25,6 +25,7 @@  #include "vbox.h"  #include "int2fwin.h" +#include "int10vga.h"  #define USE_VIRTUALBOX 1  #define USE_INT2F 1 @@ -34,6 +35,9 @@  #define GRAPHIC_CURSOR_WIDTH 16  #define GRAPHIC_CURSOR_HEIGHT 16 +#define GRAPHIC_CURSOR_SCANLINE_LEN 2 +#define GRAPHIC_CURSOR_MASK_LEN (GRAPHIC_CURSOR_HEIGHT * GRAPHIC_CURSOR_SCANLINE_LEN) +#define GRAPHIC_CURSOR_DATA_LEN (2 * GRAPHIC_CURSOR_MASK_LEN)  #define VERSION_MAJOR 0  #define VERSION_MINOR 3 @@ -55,16 +59,15 @@ typedef struct tsrdata {  	bool usewheel;  	// Video settings -	/** Current video mode. */ -	uint8_t screen_mode; -	/** Active video page. */ -	uint8_t screen_page; +	/** Information of the current video mode. */ +	struct modeinfo video_mode;  	/** Max (virtual) coordinates of full screen in the current mode.  	 *  Used for rendering graphic cursor, mapping absolute coordinates,  	 *  and initializing the default min/max window. */  	struct point screen_max; -	/** Some graphic modes have pixel doubling, so the virtual coordinates -	 *  are double vs real framebuffer coordinates. */ +	/** In some modes, the virtual coordinates are larger than the +	 *  physical screen coordinates. +	 *  real coordinates = virtual coordinates * screen_scale. */  	struct point screen_scale;  	// Detected mouse hardware @@ -89,7 +92,7 @@ typedef struct tsrdata {  	/** Hotspot for the graphic cursor. */  	struct point cursor_hotspot;  	/** Masks for the graphic cursor. */ -	uint16_t cursor_graphic[GRAPHIC_CURSOR_HEIGHT*2]; +	uint16_t cursor_graphic[GRAPHIC_CURSOR_DATA_LEN/sizeof(uint16_t)];  	// Current mouse status  	/** Current cursor position (in pixels). */ @@ -5,18 +5,6 @@  #define BIOS_DATA_AREA_SEGMENT 0x40 -// TODO Assuming screenwidth, videopage on ss rather than using far -static uint8_t int10_get_video_mode(uint8_t *screenwidth, uint8_t *videopage); -#pragma aux int10_get_video_mode = \ -	"mov al, 0" \ -	"mov ah, 0x0F" \ -	"int 0x10" \ -	"mov ss:[si], ah" \ -	"mov ss:[di], bh" \ -	__parm [si] [di] \ -	__value [al] \ -	__modify [ax bh] -  static inline uint32_t bda_get_dword(unsigned int offset) {  	uint32_t __far *p = MK_FP(BIOS_DATA_AREA_SEGMENT, offset);  	return *p; @@ -43,29 +31,183 @@ static inline uint8_t bda_get_byte(unsigned int offset) {  #define bda_get_tick_count()      bda_get_dword(0x6c)  #define bda_get_tick_count_lo()   bda_get_word(0x6c) -static inline uint16_t __far * get_video_char(uint8_t page, unsigned int x, unsigned int y) -{ -	return MK_FP(0xB800, (page * bda_get_video_page_size()) -	                     + ((y * bda_get_num_columns()) + x) * 2); -} +enum videotype { +	VIDEO_UNKNOWN, +	VIDEO_TEXT, +	VIDEO_CGA, +	VIDEO_EGA, +	VIDEO_VGA +}; + +struct modeinfo { +	uint8_t mode; +	uint8_t page; +	enum videotype type; + +	uint16_t pixels_width, pixels_height; +	uint16_t bytes_per_line; +	uint16_t odd_scanline_offset; +	uint8_t bits_per_pixel; +	uint8_t num_planes; -static inline uint8_t __far * get_video_scanline(uint8_t mode, uint8_t page, unsigned int y) +	/** Pointer to video memory. */ +	uint8_t __far * begin; +}; + +static void get_current_video_mode_info(struct modeinfo *info)  { -	switch (mode) { +	uint16_t segment; + +	info->mode = bda_get_video_mode() & ~0x80; +	info->page = bda_get_cur_video_page(); + +	info->odd_scanline_offset = 0; + +	switch (info->mode) { +	case 0: +	case 1: +	case 2: +	case 3: // CGA text modes with 25 rows and variable columns +	case 7: // MDA Mono text mode +		info->type = VIDEO_TEXT; +		info->pixels_width = bda_get_num_columns() * 8; +		info->pixels_height = (bda_get_last_row()+1) * 8; +		info->bytes_per_line = bda_get_num_columns() * 2; +		info->bits_per_pixel = 2 * 8; +		info->num_planes = 1; +		segment = 0xB800; +		break; +  	case 4: -	case 5: -	case 6: // CGA modes -		// Scalines are 80 bytes long, however they are interleaved. -		// Even numbered scanlines begin at 0, odd lines begin at 0x2000. -		// So offset by y%2 * 0x2000. -		return MK_FP(0xB800, (page * bda_get_video_page_size()) -		                      + ((y/2) * 80) + (y%2) * 0x2000); +	case 5: // CGA 320x200 4-color +		info->type = VIDEO_CGA; +		info->pixels_width = 320; +		info->pixels_height = 200; +		info->bytes_per_line = 80; +		info->odd_scanline_offset = 0x2000; +		info->bits_per_pixel = 2; +		info->num_planes = 1; +		segment = 0xB800; +		break; + +	case 6: // CGA 640x200 2-color +		info->type = VIDEO_CGA; +		info->pixels_width = 640; +		info->pixels_height = 200; +		info->bytes_per_line = 80; +		info->odd_scanline_offset = 0x2000; +		info->bits_per_pixel = 1; +		info->num_planes = 1; +		segment = 0xB800; +		break; + +	case 0xd: // EGA 320x200 16-color +		info->type = VIDEO_EGA; +		info->pixels_width = 320; +		info->pixels_height = 200; +		info->bytes_per_line = 40; +		info->bits_per_pixel = 1; +		info->num_planes = 4; +		segment = 0xA000; +		break; + +	case 0xe: // EGA 640x200 16-color +		info->type = VIDEO_EGA; +		info->pixels_width = 640; +		info->pixels_height = 200; +		info->bytes_per_line = 80; +		info->bits_per_pixel = 1; +		info->num_planes = 4; +		segment = 0xA000; +		break; + +	case 0xf: // EGA 640x350 4-color +		info->type = VIDEO_EGA; +		info->pixels_width = 640; +		info->pixels_height = 350; +		info->bytes_per_line = 80; +		info->bits_per_pixel = 1; +		info->num_planes = 2; +		segment = 0xA000; +		break; + +	case 0x10: // EGA 640x350 16-color +		info->type = VIDEO_EGA; +		info->pixels_width = 640; +		info->pixels_height = 350; +		info->bytes_per_line = 80; +		info->bits_per_pixel = 1; +		info->num_planes = 4; +		segment = 0xA000; +		break; + +	case 0x11: // VGA 640x480 2-color +	case 0x12: // VGA 640x480 16-color +		info->type = VIDEO_VGA; +		info->pixels_width = 640; +		info->pixels_height = 480; +		info->bytes_per_line = 80; +		info->bits_per_pixel = 1; +		info->num_planes = 4; +		segment = 0xA000; +		break; + +	case 0x13: // VGA 320x200 256-color +		info->type = VIDEO_VGA; +		info->pixels_width = 320; +		info->pixels_height = 200; +		info->bytes_per_line = 320; +		info->bits_per_pixel = 8; +		info->num_planes = 1; +		segment = 0xA000; +		break; +  	default: -		return 0; +		info->type = VIDEO_UNKNOWN; +		// Let's put in some default coordinates at leas +		info->pixels_width = 640; +		info->pixels_height = 200; +		segment = 0; +	} + +	info->begin = MK_FP(segment, info->page * bda_get_video_page_size()); +} + +static inline uint16_t __far * get_video_char(const struct modeinfo *info, unsigned int x, unsigned int y) +{ +	return (uint16_t __far *) (info->begin + (y * info->bytes_per_line) + (x * 2)); +} + +static inline uint8_t __far * get_video_scanline(const struct modeinfo *info, unsigned int y) +{ +	if (info->odd_scanline_offset) { +		return info->begin +		        + (y%2 * info->odd_scanline_offset) +		        + (y/2 * info->bytes_per_line); +	} else { +		return info->begin +		        + (y * info->bytes_per_line);  	}  } +struct videoregs { +	uint16_t dummy; +}; +static void save_video_registers(struct videoregs *regs) +{ +	// TODO +} + +static void restore_video_registers(struct videoregs *regs) +{ +	// TODO +} + +static inline void vga_select_plane(unsigned plane) +{ +	// TODO +}  #endif @@ -16,9 +16,13 @@   * along with this program; if not, write to the Free Software   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.   */ +  #ifndef INT33_H  #define INT33_H +#include <stdint.h> +#include <stdbool.h> +  enum INT33_API {  	/** Reinitializes mouse hardware and resets mouse to default driver values.  	 *  On return, ax = 0xFFFF, bx = number of buttons. */ @@ -185,7 +189,8 @@ static uint16_t int33_reset(void);  #pragma aux int33_reset = \  	"mov ax, 0x0" \  	"int 0x33" \ -	__value [ax] +	__value [ax] \ +	__modify [ax bx]  static void int33_set_horizontal_window(int16_t min, int16_t max);  #pragma aux int33_set_horizontal_window = \ @@ -221,4 +226,4 @@ static uint16_t int33_get_driver_version(void);  	__value [bx] \  	__modify [ax bx cx dx] -#endif +#endif /* INT33_H */ @@ -145,4 +145,13 @@ static int vbox_set_pointer_visible(LPVBOXCOMM vb, bool visible)  	return req->header.rc;  } +/** Computes size of a VMMDevReqMousePointer message. */ +static inline unsigned vbox_req_mouse_pointer_size(unsigned width, unsigned height) +{ +	const unsigned and_mask_size = (width + 7) / 8 * height; +	const unsigned xor_mask_size = width * height * 4; +	const unsigned data_size = and_mask_size + xor_mask_size; +	return MAX(sizeof(VMMDevReqMousePointer), 24 + 20 + data_size); +} +  #endif  | 
