[patch from Kelvin Delbarre]

Software cursor mode is now supported, although currently the existing hardware
cursor mode is used whenever possible. (Software mode will be used if you are
running with a recent version of SDL's Quartz video driver, since a bug in SDL
1.2.11 and later prevents the hardware cursor from working properly with that
driver.)

In hardware cursor mode, the hot-spot is now determined heuristically. Formerly
it could not be determined and was always (1,1), an annoyance for many cursors
other than the arrow.

In hardware cursor mode, the cursor will now be hidden when requested by the
emulated OS (such as when you are typing in a text field).

In hardware cursor mode, some cursor image formats that the code does not handle
correctly will now be rejected, causing the emulated OS to revert temporarily to
software cursor mode. Formerly you would just end up with random garbage for a
cursor. This typically happened for grayscale or color cursors; rejecting images
with rowBytes != 2 eliminates the worst cases.
This commit is contained in:
asvitkine 2008-06-25 02:52:52 +00:00
parent d885e5ccf4
commit c23511080e
3 changed files with 125 additions and 14 deletions

View File

@ -1543,7 +1543,23 @@ void SDL_monitor_desc::switch_to_current_mode(void)
#ifdef SHEEPSHAVER
bool video_can_change_cursor(void)
{
return (display_type == DISPLAY_WINDOW);
static char driver[] = "Quartz?";
static int quartzok = -1;
if (display_type != DISPLAY_WINDOW)
return false;
if (quartzok < 0) {
if (SDL_VideoDriverName(driver, sizeof driver) == NULL || strncmp(driver, "Quartz", sizeof driver))
quartzok = true;
else {
// Quartz driver bug prevents cursor changing in SDL 1.2.11 and later
const SDL_version *vp = SDL_Linked_Version();
quartzok = SDL_VERSIONNUM(vp->major, vp->minor, vp->patch) <= SDL_VERSIONNUM(1, 2, 10);
}
}
return quartzok;
}
#endif
@ -2220,6 +2236,7 @@ static inline void do_video_refresh(void)
SDL_FreeCursor(sdl_cursor);
sdl_cursor = SDL_CreateCursor(MacCursor + 4, MacCursor + 36, 16, 16, MacCursor[2], MacCursor[3]);
if (sdl_cursor) {
SDL_ShowCursor(private_data == NULL || private_data->cursorVisible);
SDL_SetCursor(sdl_cursor);
#ifdef WIN32
// XXX Windows apparently needs an extra mouse event to

View File

@ -118,10 +118,14 @@ struct VidLocals{
uint32 maxGammaTableSize; // Biggest gamma table allocated
uint32 saveVidParms;
bool luminanceMapping; // Luminance mapping on/off
int32 cursorX; // Hardware cursor state. Unused, but must be remembered
bool cursorHardware; // True if using hardware cursor
int32 cursorX; // Hardware cursor state
int32 cursorY;
uint32 cursorVisible;
uint32 cursorSet;
bool cursorHotFlag;
uint8 cursorHotX;
uint8 cursorHotY;
uint32 vslServiceID; // VSL interrupt service ID
bool interruptsEnabled; // VBL interrupts on/off
uint32 regEntryID; // Mac address of the service owner

View File

@ -21,7 +21,6 @@
/*
* TODO
* - check for supported modes ???
* - window mode "hardware" cursor hotspot
*/
#include <stdio.h>
@ -142,6 +141,17 @@ bool VideoSnapshot(int xsize, int ysize, uint8 *p)
}
/*
* Determine whether we should use the hardware or software cursor, and return true for the former, false for the latter.
* Currently we use the hardware cursor if we can, but perhaps this can be made a preference someday.
*/
static bool UseHardwareCursor(void)
{
return video_can_change_cursor();
}
/*
* Video driver open routine
*/
@ -157,10 +167,14 @@ static int16 VideoOpen(uint32 pb, VidLocals *csSave)
csSave->savePage = 0;
csSave->saveVidParms = 0; // Add the right table
csSave->luminanceMapping = false;
csSave->cursorHardware = UseHardwareCursor();
csSave->cursorX = 0;
csSave->cursorY = 0;
csSave->cursorVisible = 0;
csSave->cursorSet = 0;
csSave->cursorHotFlag = false;
csSave->cursorHotX = 0;
csSave->cursorHotY = 0;
// Find and set default gamma table
csSave->gammaTable = 0;
@ -416,35 +430,44 @@ static int16 VideoControl(uint32 pb, VidLocals *csSave)
case cscSetHardwareCursor: {
// D(bug("SetHardwareCursor\n"));
if (!csSave->cursorHardware)
return controlErr;
csSave->cursorSet = false;
bool changed = false;
// Get cursor data even on a screen, to set the right cursor image when switching back to a window
// Image
uint32 cursor = ReadMacInt32(param); // Pointer to CursorImage
uint32 pmhandle = ReadMacInt32(cursor + ciCursorPixMap);
if (pmhandle == 0 || ReadMacInt32(pmhandle) == 0)
return controlErr;
uint32 pixmap = ReadMacInt32(pmhandle);
if (memcmp(MacCursor + 4, Mac2HostAddr(ReadMacInt32(pixmap)), 32)) {
memcpy(MacCursor + 4, Mac2HostAddr(ReadMacInt32(pixmap)), 32);
changed = true;
}
// XXX: only certain image formats are handled properly at the moment
uint16 rowBytes = ReadMacInt16(pixmap + 4) & 0x7FFF;
if (rowBytes != 2)
return controlErr;
// Mask
uint32 bmhandle = ReadMacInt32(cursor + ciCursorBitMask);
if (bmhandle == 0 || ReadMacInt32(bmhandle) == 0)
return controlErr;
uint32 bitmap = ReadMacInt32(bmhandle);
// Get cursor data even on a screen, to set the right cursor image when switching back to a window.
// Hotspot is stale, but will be fixed by the next call to DrawHardwareCursor, which is likely to
// occur immediately hereafter.
if (memcmp(MacCursor + 4, Mac2HostAddr(ReadMacInt32(pixmap)), 32)) {
memcpy(MacCursor + 4, Mac2HostAddr(ReadMacInt32(pixmap)), 32);
changed = true;
}
if (memcmp(MacCursor + 4 + 32, Mac2HostAddr(ReadMacInt32(bitmap)), 32)) {
memcpy(MacCursor + 4 + 32, Mac2HostAddr(ReadMacInt32(bitmap)), 32);
changed = true;
}
// Hotspot (!! this doesn't work)
MacCursor[2] = ReadMacInt8(0x885);
MacCursor[3] = ReadMacInt8(0x887);
// Set new cursor image
if (!video_can_change_cursor())
return controlErr;
@ -452,15 +475,78 @@ static int16 VideoControl(uint32 pb, VidLocals *csSave)
video_set_cursor();
csSave->cursorSet = true;
csSave->cursorHotFlag = true;
return noErr;
}
case cscDrawHardwareCursor:
case cscDrawHardwareCursor: {
// D(bug("DrawHardwareCursor\n"));
if (!csSave->cursorHardware)
return controlErr;
int32 oldX = csSave->cursorX;
int32 oldY = csSave->cursorY;
uint32 oldVisible = csSave->cursorVisible;
csSave->cursorX = ReadMacInt32(param + csCursorX);
csSave->cursorY = ReadMacInt32(param + csCursorY);
csSave->cursorVisible = ReadMacInt32(param + csCursorVisible);
bool changed = (csSave->cursorVisible != oldVisible);
// If this is the first DrawHardwareCursor call since the cursor was last set (via SetHardwareCursor),
// attempt to set an appropriate cursor hotspot. SetHardwareCursor itself does not know what the
// hotspot should be; it knows only the cursor image and mask. The hotspot is known only to the caller,
// and we have to try to infer it here. The usual sequence of calls when changing the cursor is:
//
// DrawHardwareCursor with (oldX, oldY, invisible)
// SetHardwareCursor with (cursor)
// DrawHardwareCursor with (newX, newY, visible)
//
// The key thing to note is that the sequence is intended not to change the current screen pixel location
// indicated by the hotspot. Thus, the difference between (newX, newY) and (oldX, oldY) reflects precisely
// the difference between the old cursor hotspot and the new one. For example, if you change from a
// cursor whose hotspot is (1, 1) to one whose hotspot is (7, 4), then you must adjust the cursor position
// by (-6, -3) in order for the same screen pixel to remain under the new hotspot.
//
// Alas, on rare occasions this heuristic can fail, and if you did nothing else you could even get stuck
// with the wrong hotspot from then on. To address that possibility, we force the hotspot to (1, 1)
// whenever the cursor being drawn is the standard arrow. Thus, while it is very unlikely that you will
// ever have the wrong hotspot, if you do, it is easy to recover.
if (csSave->cursorHotFlag) {
csSave->cursorHotFlag = false;
D(bug("old hotspot (%d, %d)\n", csSave->cursorHotX, csSave->cursorHotY));
static uint8 arrow[] = {
0x00, 0x00, 0x40, 0x00, 0x60, 0x00, 0x70, 0x00, 0x78, 0x00, 0x7C, 0x00, 0x7E, 0x00, 0x7F, 0x00,
0x7F, 0x80, 0x7C, 0x00, 0x6C, 0x00, 0x46, 0x00, 0x06, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00,
};
if (memcmp(MacCursor + 4, arrow, 32) == 0) {
csSave->cursorHotX = 1;
csSave->cursorHotY = 1;
} else if (csSave->cursorX != oldX || csSave->cursorY != oldY) {
int32 hotX = csSave->cursorHotX + (oldX - csSave->cursorX);
int32 hotY = csSave->cursorHotY + (oldY - csSave->cursorY);
if (0 <= hotX && hotX <= 15 && 0 <= hotY && hotY <= 15) {
csSave->cursorHotX = hotX;
csSave->cursorHotY = hotY;
}
}
if (MacCursor[2] != csSave->cursorHotX || MacCursor[3] != csSave->cursorHotY) {
MacCursor[2] = csSave->cursorHotX;
MacCursor[3] = csSave->cursorHotY;
changed = true;
}
D(bug("new hotspot (%d, %d)\n", csSave->cursorHotX, csSave->cursorHotY));
}
if (changed && video_can_change_cursor())
video_set_cursor();
return noErr;
}
case 43: { // Driver Gestalt
uint32 sel = ReadMacInt32(pb + csParam);
@ -859,11 +945,15 @@ static int16 VideoStatus(uint32 pb, VidLocals *csSave)
case cscSupportsHardwareCursor:
D(bug("SupportsHardwareCursor\n"));
WriteMacInt32(param, 1);
WriteMacInt32(param, csSave->cursorHardware);
return noErr;
case cscGetHardwareCursorDrawState:
D(bug("GetHardwareCursorDrawState\n"));
if (!csSave->cursorHardware)
return statusErr;
WriteMacInt32(param + csCursorX, csSave->cursorX);
WriteMacInt32(param + csCursorY, csSave->cursorY);
WriteMacInt32(param + csCursorVisible, csSave->cursorVisible);