Add animated cursor support
Adds support for animated cursors on Cocoa, Wayland, Win32, and X11. testcursor can take a semicolon separated list of filenames and load an animated cursor from them.
This commit is contained in:
@@ -130,6 +130,17 @@ typedef enum SDL_MouseWheelDirection
|
|||||||
SDL_MOUSEWHEEL_FLIPPED /**< The scroll direction is flipped / natural */
|
SDL_MOUSEWHEEL_FLIPPED /**< The scroll direction is flipped / natural */
|
||||||
} SDL_MouseWheelDirection;
|
} SDL_MouseWheelDirection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animated cursor frame info.
|
||||||
|
*
|
||||||
|
* \since This struct is available since SDL 3.4.0.
|
||||||
|
*/
|
||||||
|
typedef struct SDL_CursorFrameInfo
|
||||||
|
{
|
||||||
|
SDL_Surface *surface; /**< The surface data for this frame */
|
||||||
|
Uint32 duration; /**< The frame duration in milliseconds (a duration of 0 is infinite) */
|
||||||
|
} SDL_CursorFrameInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A bitmask of pressed mouse buttons, as reported by SDL_GetMouseState, etc.
|
* A bitmask of pressed mouse buttons, as reported by SDL_GetMouseState, etc.
|
||||||
*
|
*
|
||||||
@@ -565,6 +576,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_CaptureMouse(bool enabled);
|
|||||||
*
|
*
|
||||||
* \since This function is available since SDL 3.2.0.
|
* \since This function is available since SDL 3.2.0.
|
||||||
*
|
*
|
||||||
|
* \sa SDL_CreateAnimatedCursor
|
||||||
* \sa SDL_CreateColorCursor
|
* \sa SDL_CreateColorCursor
|
||||||
* \sa SDL_CreateSystemCursor
|
* \sa SDL_CreateSystemCursor
|
||||||
* \sa SDL_DestroyCursor
|
* \sa SDL_DestroyCursor
|
||||||
@@ -600,6 +612,7 @@ extern SDL_DECLSPEC SDL_Cursor * SDLCALL SDL_CreateCursor(const Uint8 *data,
|
|||||||
* \since This function is available since SDL 3.2.0.
|
* \since This function is available since SDL 3.2.0.
|
||||||
*
|
*
|
||||||
* \sa SDL_AddSurfaceAlternateImage
|
* \sa SDL_AddSurfaceAlternateImage
|
||||||
|
* \sa SDL_CreateAnimatedCursor
|
||||||
* \sa SDL_CreateCursor
|
* \sa SDL_CreateCursor
|
||||||
* \sa SDL_CreateSystemCursor
|
* \sa SDL_CreateSystemCursor
|
||||||
* \sa SDL_DestroyCursor
|
* \sa SDL_DestroyCursor
|
||||||
@@ -609,6 +622,57 @@ extern SDL_DECLSPEC SDL_Cursor * SDLCALL SDL_CreateColorCursor(SDL_Surface *surf
|
|||||||
int hot_x,
|
int hot_x,
|
||||||
int hot_y);
|
int hot_y);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an animated color cursor.
|
||||||
|
*
|
||||||
|
* Animated cursors are composed of a sequential array of frames, specified
|
||||||
|
* as surfaces and durations in an array of SDL_CursorFrameInfo structs.
|
||||||
|
* The hot spot coordinates are universal to all frames, and all frames must
|
||||||
|
* have the same dimensions.
|
||||||
|
*
|
||||||
|
* Frame durations are specified in milliseconds. A duration of 0 implies an
|
||||||
|
* infinite frame time, and the animation will stop on that frame. To create
|
||||||
|
* a one-shot animation, set the duration of the last frame in the sequence
|
||||||
|
* to 0.
|
||||||
|
*
|
||||||
|
* If this function is passed surfaces with alternate representations added
|
||||||
|
* with SDL_AddSurfaceAlternateImage(), the surfaces will be interpreted as the
|
||||||
|
* content to be used for 100% display scale, and the alternate
|
||||||
|
* representations will be used for high DPI situations. For example, if the
|
||||||
|
* original surfaces are 32x32, then on a 2x macOS display or 200% display scale
|
||||||
|
* on Windows, a 64x64 version of the image will be used, if available. If a
|
||||||
|
* matching version of the image isn't available, the closest larger size
|
||||||
|
* image will be downscaled to the appropriate size and be used instead, if
|
||||||
|
* available. Otherwise, the closest smaller image will be upscaled and be
|
||||||
|
* used instead.
|
||||||
|
*
|
||||||
|
* If the underlying platform does not support animated cursors, this function
|
||||||
|
* will fall back to creating a static color cursor using the first frame in
|
||||||
|
* the sequence.
|
||||||
|
*
|
||||||
|
* \param frames an array of cursor images composing the animation.
|
||||||
|
* \param frame_count the number of frames in the sequence.
|
||||||
|
* \param hot_x the x position of the cursor hot spot.
|
||||||
|
* \param hot_y the y position of the cursor hot spot.
|
||||||
|
* \returns the new cursor on success or NULL on failure; call SDL_GetError()
|
||||||
|
* for more information.
|
||||||
|
*
|
||||||
|
* \threadsafety This function should only be called on the main thread.
|
||||||
|
*
|
||||||
|
* \since This function is available since SDL 3.4.0.
|
||||||
|
*
|
||||||
|
* \sa SDL_AddSurfaceAlternateImage
|
||||||
|
* \sa SDL_CreateCursor
|
||||||
|
* \sa SDL_CreateColorCursor
|
||||||
|
* \sa SDL_CreateSystemCursor
|
||||||
|
* \sa SDL_DestroyCursor
|
||||||
|
* \sa SDL_SetCursor
|
||||||
|
*/
|
||||||
|
extern SDL_DECLSPEC SDL_Cursor *SDLCALL SDL_CreateAnimatedCursor(SDL_CursorFrameInfo *frames,
|
||||||
|
int frame_count,
|
||||||
|
int hot_x,
|
||||||
|
int hot_y);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a system cursor.
|
* Create a system cursor.
|
||||||
*
|
*
|
||||||
@@ -687,6 +751,7 @@ extern SDL_DECLSPEC SDL_Cursor * SDLCALL SDL_GetDefaultCursor(void);
|
|||||||
*
|
*
|
||||||
* \since This function is available since SDL 3.2.0.
|
* \since This function is available since SDL 3.2.0.
|
||||||
*
|
*
|
||||||
|
* \sa SDL_CreateAnimatedCursor
|
||||||
* \sa SDL_CreateColorCursor
|
* \sa SDL_CreateColorCursor
|
||||||
* \sa SDL_CreateCursor
|
* \sa SDL_CreateCursor
|
||||||
* \sa SDL_CreateSystemCursor
|
* \sa SDL_CreateSystemCursor
|
||||||
|
|||||||
@@ -1266,6 +1266,7 @@ SDL3_0.0.0 {
|
|||||||
SDL_SavePNG;
|
SDL_SavePNG;
|
||||||
SDL_GetSystemPageSize;
|
SDL_GetSystemPageSize;
|
||||||
SDL_GetPenDeviceType;
|
SDL_GetPenDeviceType;
|
||||||
|
SDL_CreateAnimatedCursor;
|
||||||
# extra symbols go here (don't modify this line)
|
# extra symbols go here (don't modify this line)
|
||||||
local: *;
|
local: *;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1292,3 +1292,4 @@
|
|||||||
#define SDL_SavePNG SDL_SavePNG_REAL
|
#define SDL_SavePNG SDL_SavePNG_REAL
|
||||||
#define SDL_GetSystemPageSize SDL_GetSystemPageSize_REAL
|
#define SDL_GetSystemPageSize SDL_GetSystemPageSize_REAL
|
||||||
#define SDL_GetPenDeviceType SDL_GetPenDeviceType_REAL
|
#define SDL_GetPenDeviceType SDL_GetPenDeviceType_REAL
|
||||||
|
#define SDL_CreateAnimatedCursor SDL_CreateAnimatedCursor_REAL
|
||||||
|
|||||||
@@ -1300,3 +1300,4 @@ SDL_DYNAPI_PROC(bool,SDL_SavePNG_IO,(SDL_Surface *a,SDL_IOStream *b,bool c),(a,b
|
|||||||
SDL_DYNAPI_PROC(bool,SDL_SavePNG,(SDL_Surface *a,const char *b),(a,b),return)
|
SDL_DYNAPI_PROC(bool,SDL_SavePNG,(SDL_Surface *a,const char *b),(a,b),return)
|
||||||
SDL_DYNAPI_PROC(int,SDL_GetSystemPageSize,(void),(),return)
|
SDL_DYNAPI_PROC(int,SDL_GetSystemPageSize,(void),(),return)
|
||||||
SDL_DYNAPI_PROC(SDL_PenDeviceType,SDL_GetPenDeviceType,(SDL_PenID a),(a),return)
|
SDL_DYNAPI_PROC(SDL_PenDeviceType,SDL_GetPenDeviceType,(SDL_PenID a),(a),return)
|
||||||
|
SDL_DYNAPI_PROC(SDL_Cursor*,SDL_CreateAnimatedCursor,(SDL_CursorFrameInfo *a,int b,int c,int d),(a,b,c,d),return)
|
||||||
|
|||||||
@@ -1552,6 +1552,103 @@ SDL_Cursor *SDL_CreateCursor(const Uint8 *data, const Uint8 *mask, int w, int h,
|
|||||||
return cursor;
|
return cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SDL_Cursor *SDL_CreateAnimatedCursor(SDL_CursorFrameInfo *frames, int frame_count, int hot_x, int hot_y)
|
||||||
|
{
|
||||||
|
SDL_Mouse *mouse = SDL_GetMouse();
|
||||||
|
SDL_Cursor *cursor = NULL;
|
||||||
|
|
||||||
|
CHECK_PARAM(!frames) {
|
||||||
|
SDL_InvalidParamError("frames");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK_PARAM(!frame_count) {
|
||||||
|
SDL_InvalidParamError("frame_count");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to a static cursor if the platform doesn't support animated cursors.
|
||||||
|
if (!mouse->CreateAnimatedCursor) {
|
||||||
|
// If there is a frame with infinite duration, use it; otherwise, use the first.
|
||||||
|
for (int i = 0; i < frame_count; ++i) {
|
||||||
|
if (!frames[i].duration) {
|
||||||
|
return SDL_CreateColorCursor(frames[i].surface, hot_x, hot_y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return SDL_CreateColorCursor(frames[0].surface, hot_x, hot_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow specifying the hot spot via properties on the surface
|
||||||
|
SDL_PropertiesID props = SDL_GetSurfaceProperties(frames[0].surface);
|
||||||
|
hot_x = (int)SDL_GetNumberProperty(props, SDL_PROP_SURFACE_HOTSPOT_X_NUMBER, hot_x);
|
||||||
|
hot_y = (int)SDL_GetNumberProperty(props, SDL_PROP_SURFACE_HOTSPOT_Y_NUMBER, hot_y);
|
||||||
|
|
||||||
|
// Sanity check the hot spot
|
||||||
|
CHECK_PARAM((hot_x < 0) || (hot_y < 0) ||
|
||||||
|
(hot_x >= frames[0].surface->w) || (hot_y >= frames[0].surface->h)) {
|
||||||
|
SDL_SetError("Cursor hot spot doesn't lie within cursor");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK_PARAM(!frames[0].surface) {
|
||||||
|
SDL_SetError("Null surface in frame 0");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isstack;
|
||||||
|
SDL_CursorFrameInfo *temp_frames = SDL_small_alloc(SDL_CursorFrameInfo, frame_count, &isstack);
|
||||||
|
if (!temp_frames) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
SDL_memset(temp_frames, 0, sizeof(SDL_CursorFrameInfo) * frame_count);
|
||||||
|
|
||||||
|
const int w = frames[0].surface->w;
|
||||||
|
const int h = frames[0].surface->h;
|
||||||
|
|
||||||
|
for (int i = 0; i < frame_count; ++i) {
|
||||||
|
CHECK_PARAM(!frames[i].surface) {
|
||||||
|
SDL_SetError("Null surface in frame %i", i);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
// All cursor images should be the same size.
|
||||||
|
CHECK_PARAM(frames[i].surface->w != w || frames[i].surface->h != h) {
|
||||||
|
SDL_SetError("All frames in an animated sequence must have the same dimensions");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frames[i].surface->format == SDL_PIXELFORMAT_ARGB8888) {
|
||||||
|
temp_frames[i].surface = frames[i].surface;
|
||||||
|
} else {
|
||||||
|
SDL_Surface *temp = SDL_ConvertSurface(frames[i].surface, SDL_PIXELFORMAT_ARGB8888);
|
||||||
|
if (!temp) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
temp_frames[i].surface = temp;
|
||||||
|
}
|
||||||
|
temp_frames[i].duration = frames[i].duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor = mouse->CreateAnimatedCursor(temp_frames, frame_count, hot_x, hot_y);
|
||||||
|
if (cursor) {
|
||||||
|
cursor->next = mouse->cursors;
|
||||||
|
mouse->cursors = cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
// Clean up any temporary converted surfaces.
|
||||||
|
for (int i = 0; i < frame_count; ++i) {
|
||||||
|
if (temp_frames[i].surface && frames[i].surface != temp_frames[i].surface) {
|
||||||
|
SDL_DestroySurface(temp_frames[i].surface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_small_free(temp_frames, isstack);
|
||||||
|
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
SDL_Cursor *SDL_CreateColorCursor(SDL_Surface *surface, int hot_x, int hot_y)
|
SDL_Cursor *SDL_CreateColorCursor(SDL_Surface *surface, int hot_x, int hot_y)
|
||||||
{
|
{
|
||||||
SDL_Mouse *mouse = SDL_GetMouse();
|
SDL_Mouse *mouse = SDL_GetMouse();
|
||||||
|
|||||||
@@ -60,6 +60,9 @@ typedef struct
|
|||||||
// Create a cursor from a surface
|
// Create a cursor from a surface
|
||||||
SDL_Cursor *(*CreateCursor)(SDL_Surface *surface, int hot_x, int hot_y);
|
SDL_Cursor *(*CreateCursor)(SDL_Surface *surface, int hot_x, int hot_y);
|
||||||
|
|
||||||
|
// Create an animated cursor from a sequence of surfaces
|
||||||
|
SDL_Cursor *(*CreateAnimatedCursor)(SDL_CursorFrameInfo *frames, int frame_count, int hot_x, int hot_y);
|
||||||
|
|
||||||
// Create a system cursor
|
// Create a system cursor
|
||||||
SDL_Cursor *(*CreateSystemCursor)(SDL_SystemCursor id);
|
SDL_Cursor *(*CreateSystemCursor)(SDL_SystemCursor id);
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,19 @@ extern void Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent *event);
|
|||||||
extern void Cocoa_HandleMouseWarp(CGFloat x, CGFloat y);
|
extern void Cocoa_HandleMouseWarp(CGFloat x, CGFloat y);
|
||||||
extern void Cocoa_QuitMouse(SDL_VideoDevice *_this);
|
extern void Cocoa_QuitMouse(SDL_VideoDevice *_this);
|
||||||
|
|
||||||
|
struct SDL_CursorData
|
||||||
|
{
|
||||||
|
NSTimer *frameTimer;
|
||||||
|
int current_frame;
|
||||||
|
|
||||||
|
int num_cursors;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
void *cursor;
|
||||||
|
Uint32 duration;
|
||||||
|
} frames[];
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
// Whether we've seen a cursor warp since the last move event.
|
// Whether we've seen a cursor warp since the last move event.
|
||||||
|
|||||||
@@ -66,27 +66,59 @@
|
|||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
static SDL_Cursor *Cocoa_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y)
|
static SDL_Cursor *Cocoa_CreateAnimatedCursor(SDL_CursorFrameInfo *frames, int frame_count, int hot_x, int hot_y)
|
||||||
{
|
{
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
NSImage *nsimage;
|
NSImage *nsimage;
|
||||||
NSCursor *nscursor = NULL;
|
NSCursor *nscursor = NULL;
|
||||||
SDL_Cursor *cursor = NULL;
|
SDL_Cursor *cursor = NULL;
|
||||||
|
|
||||||
nsimage = Cocoa_CreateImage(surface);
|
cursor = SDL_calloc(1, sizeof(*cursor));
|
||||||
|
if (cursor) {
|
||||||
|
SDL_CursorData *cdata = SDL_calloc(1, sizeof(*cdata) + (sizeof(*cdata->frames) * frame_count));
|
||||||
|
if (!cdata) {
|
||||||
|
SDL_free(cursor);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor->internal = cdata;
|
||||||
|
|
||||||
|
for (int i = 0; i < frame_count; ++i) {
|
||||||
|
nsimage = Cocoa_CreateImage(frames[i].surface);
|
||||||
if (nsimage) {
|
if (nsimage) {
|
||||||
nscursor = [[NSCursor alloc] initWithImage:nsimage hotSpot:NSMakePoint(hot_x, hot_y)];
|
nscursor = [[NSCursor alloc] initWithImage:nsimage hotSpot:NSMakePoint(hot_x, hot_y)];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nscursor) {
|
if (nscursor) {
|
||||||
cursor = SDL_calloc(1, sizeof(*cursor));
|
++cdata->num_cursors;
|
||||||
if (cursor) {
|
cdata->frames[i].cursor = (void *)CFBridgingRetain(nscursor);
|
||||||
cursor->internal = (void *)CFBridgingRetain(nscursor);
|
cdata->frames[i].duration = frames[i].duration;
|
||||||
|
} else {
|
||||||
|
for (int j = 0; j < i; ++j) {
|
||||||
|
CFBridgingRelease(cdata->frames[i].cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_free(cdata);
|
||||||
|
SDL_free(cursor);
|
||||||
|
cursor = NULL;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return cursor;
|
return cursor;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SDL_Cursor *Cocoa_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y)
|
||||||
|
{
|
||||||
|
SDL_CursorFrameInfo frame = {
|
||||||
|
surface, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
return Cocoa_CreateAnimatedCursor(&frame, 1, hot_x, hot_y);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* there are .pdf files of some of the cursors we need, installed by default on macOS, but not available through NSCursor.
|
/* there are .pdf files of some of the cursors we need, installed by default on macOS, but not available through NSCursor.
|
||||||
@@ -204,8 +236,11 @@ static SDL_Cursor *Cocoa_CreateSystemCursor(SDL_SystemCursor id)
|
|||||||
if (nscursor) {
|
if (nscursor) {
|
||||||
cursor = SDL_calloc(1, sizeof(*cursor));
|
cursor = SDL_calloc(1, sizeof(*cursor));
|
||||||
if (cursor) {
|
if (cursor) {
|
||||||
|
SDL_CursorData *cdata = SDL_calloc(1, sizeof(*cdata) + sizeof(*cdata->frames));
|
||||||
// We'll free it later, so retain it here
|
// We'll free it later, so retain it here
|
||||||
cursor->internal = (void *)CFBridgingRetain(nscursor);
|
cursor->internal = cdata;
|
||||||
|
cdata->frames[0].cursor = (void *)CFBridgingRetain(nscursor);
|
||||||
|
cdata->num_cursors = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,7 +257,14 @@ static SDL_Cursor *Cocoa_CreateDefaultCursor(void)
|
|||||||
static void Cocoa_FreeCursor(SDL_Cursor *cursor)
|
static void Cocoa_FreeCursor(SDL_Cursor *cursor)
|
||||||
{
|
{
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
CFBridgingRelease((void *)cursor->internal);
|
SDL_CursorData *cdata = cursor->internal;
|
||||||
|
if (cdata->frameTimer) {
|
||||||
|
[cdata->frameTimer invalidate];
|
||||||
|
}
|
||||||
|
for (int i = 0; i < cdata->num_cursors; ++i) {
|
||||||
|
CFBridgingRelease(cdata->frames[i].cursor);
|
||||||
|
}
|
||||||
|
SDL_free(cdata);
|
||||||
SDL_free(cursor);
|
SDL_free(cursor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -232,6 +274,14 @@ static bool Cocoa_ShowCursor(SDL_Cursor *cursor)
|
|||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
SDL_VideoDevice *device = SDL_GetVideoDevice();
|
SDL_VideoDevice *device = SDL_GetVideoDevice();
|
||||||
SDL_Window *window = (device ? device->windows : NULL);
|
SDL_Window *window = (device ? device->windows : NULL);
|
||||||
|
|
||||||
|
SDL_CursorData *cdata = cursor->internal;
|
||||||
|
cdata->current_frame = 0;
|
||||||
|
if (cdata->frameTimer) {
|
||||||
|
[cdata->frameTimer invalidate];
|
||||||
|
cdata->frameTimer = nil;
|
||||||
|
}
|
||||||
|
|
||||||
for (; window != NULL; window = window->next) {
|
for (; window != NULL; window = window->next) {
|
||||||
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal;
|
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal;
|
||||||
if (data) {
|
if (data) {
|
||||||
@@ -381,6 +431,7 @@ bool Cocoa_InitMouse(SDL_VideoDevice *_this)
|
|||||||
|
|
||||||
mouse->internal = data;
|
mouse->internal = data;
|
||||||
mouse->CreateCursor = Cocoa_CreateCursor;
|
mouse->CreateCursor = Cocoa_CreateCursor;
|
||||||
|
mouse->CreateAnimatedCursor = Cocoa_CreateAnimatedCursor;
|
||||||
mouse->CreateSystemCursor = Cocoa_CreateSystemCursor;
|
mouse->CreateSystemCursor = Cocoa_CreateSystemCursor;
|
||||||
mouse->ShowCursor = Cocoa_ShowCursor;
|
mouse->ShowCursor = Cocoa_ShowCursor;
|
||||||
mouse->FreeCursor = Cocoa_FreeCursor;
|
mouse->FreeCursor = Cocoa_FreeCursor;
|
||||||
|
|||||||
@@ -761,12 +761,44 @@ static void Cocoa_WaitForMiniaturizable(SDL_Window *window)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void Cocoa_IncrementCursorFrame(void)
|
||||||
|
{
|
||||||
|
SDL_Mouse *mouse = SDL_GetMouse();
|
||||||
|
|
||||||
|
if (mouse->cur_cursor) {
|
||||||
|
SDL_CursorData *cdata = mouse->cur_cursor->internal;
|
||||||
|
cdata->current_frame = (cdata->current_frame + 1) % cdata->num_cursors;
|
||||||
|
|
||||||
|
SDL_Window *focus = SDL_GetMouseFocus();
|
||||||
|
if (focus) {
|
||||||
|
SDL_CocoaWindowData *_data = (__bridge SDL_CocoaWindowData *)focus->internal;
|
||||||
|
[_data.nswindow invalidateCursorRectsForView:_data.sdlContentView];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static NSCursor *Cocoa_GetDesiredCursor(void)
|
static NSCursor *Cocoa_GetDesiredCursor(void)
|
||||||
{
|
{
|
||||||
SDL_Mouse *mouse = SDL_GetMouse();
|
SDL_Mouse *mouse = SDL_GetMouse();
|
||||||
|
|
||||||
if (mouse->cursor_visible && mouse->cur_cursor && !mouse->relative_mode) {
|
if (mouse->cursor_visible && mouse->cur_cursor && !mouse->relative_mode) {
|
||||||
return (__bridge NSCursor *)mouse->cur_cursor->internal;
|
SDL_CursorData *cdata = mouse->cur_cursor->internal;
|
||||||
|
|
||||||
|
if (cdata) {
|
||||||
|
if (cdata->num_cursors > 1 && cdata->frames[cdata->current_frame].duration && !cdata->frameTimer) {
|
||||||
|
const NSTimeInterval interval = cdata->frames[cdata->current_frame].duration * 0.001;
|
||||||
|
cdata->frameTimer = [NSTimer timerWithTimeInterval:interval
|
||||||
|
repeats:NO
|
||||||
|
block:^(NSTimer *timer) {
|
||||||
|
cdata->frameTimer = nil;
|
||||||
|
Cocoa_IncrementCursorFrame();
|
||||||
|
}];
|
||||||
|
|
||||||
|
[[NSRunLoop currentRunLoop] addTimer:cdata->frameTimer forMode:NSRunLoopCommonModes];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (__bridge NSCursor *)cdata->frames[cdata->current_frame].cursor;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return [NSCursor invisibleCursor];
|
return [NSCursor invisibleCursor];
|
||||||
|
|||||||
@@ -55,6 +55,8 @@ typedef struct
|
|||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
int hot_x;
|
int hot_x;
|
||||||
int hot_y;
|
int hot_y;
|
||||||
struct wl_list scaled_cursor_cache;
|
struct wl_list scaled_cursor_cache;
|
||||||
@@ -304,7 +306,7 @@ static struct wl_buffer *Wayland_SeatGetCursorFrame(SDL_WaylandSeat *seat, int f
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void cursor_frame_done(void *data, struct wl_callback *cb, uint32_t time);
|
static void cursor_frame_done(void *data, struct wl_callback *cb, uint32_t time);
|
||||||
struct wl_callback_listener cursor_frame_listener = {
|
static const struct wl_callback_listener cursor_frame_listener = {
|
||||||
cursor_frame_done
|
cursor_frame_done
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -323,10 +325,6 @@ static void cursor_frame_done(void *data, struct wl_callback *cb, uint32_t time)
|
|||||||
Uint32 advance = 0;
|
Uint32 advance = 0;
|
||||||
int next = seat->pointer.cursor_state.current_frame;
|
int next = seat->pointer.cursor_state.current_frame;
|
||||||
|
|
||||||
wl_callback_destroy(cb);
|
|
||||||
seat->pointer.cursor_state.frame_callback = wl_surface_frame(seat->pointer.cursor_state.surface);
|
|
||||||
wl_callback_add_listener(seat->pointer.cursor_state.frame_callback, &cursor_frame_listener, data);
|
|
||||||
|
|
||||||
seat->pointer.cursor_state.current_frame_time_ms += elapsed;
|
seat->pointer.cursor_state.current_frame_time_ms += elapsed;
|
||||||
|
|
||||||
// Calculate the next frame based on the elapsed duration.
|
// Calculate the next frame based on the elapsed duration.
|
||||||
@@ -340,6 +338,15 @@ static void cursor_frame_done(void *data, struct wl_callback *cb, uint32_t time)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wl_callback_destroy(cb);
|
||||||
|
seat->pointer.cursor_state.frame_callback = NULL;
|
||||||
|
|
||||||
|
// Don't queue another callback if this frame time is infinite.
|
||||||
|
if (frames[next]) {
|
||||||
|
seat->pointer.cursor_state.frame_callback = wl_surface_frame(seat->pointer.cursor_state.surface);
|
||||||
|
wl_callback_add_listener(seat->pointer.cursor_state.frame_callback, &cursor_frame_listener, data);
|
||||||
|
}
|
||||||
|
|
||||||
seat->pointer.cursor_state.current_frame_time_ms -= advance;
|
seat->pointer.cursor_state.current_frame_time_ms -= advance;
|
||||||
seat->pointer.cursor_state.last_frame_callback_time_ms = now;
|
seat->pointer.cursor_state.last_frame_callback_time_ms = now;
|
||||||
seat->pointer.cursor_state.current_frame = next;
|
seat->pointer.cursor_state.current_frame = next;
|
||||||
@@ -538,6 +545,11 @@ static Wayland_ScaledCustomCursor *Wayland_CacheScaledCustomCursor(SDL_CursorDat
|
|||||||
for (int i = 0; i < cursor->num_frames; ++i) {
|
for (int i = 0; i < cursor->num_frames; ++i) {
|
||||||
if (!surface) {
|
if (!surface) {
|
||||||
surface = SDL_GetSurfaceImage(cursor->cursor_data.custom.sdl_cursor_surfaces[i], (float)scale);
|
surface = SDL_GetSurfaceImage(cursor->cursor_data.custom.sdl_cursor_surfaces[i], (float)scale);
|
||||||
|
if (!surface) {
|
||||||
|
Wayland_ReleaseSHMPool(cache->shmPool);
|
||||||
|
SDL_free(cache);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wayland requires premultiplied alpha for its surfaces.
|
// Wayland requires premultiplied alpha for its surfaces.
|
||||||
@@ -564,7 +576,8 @@ static bool Wayland_GetCustomCursor(SDL_CursorData *cursor, SDL_WaylandSeat *sea
|
|||||||
SDL_Window *focus = SDL_GetMouseFocus();
|
SDL_Window *focus = SDL_GetMouseFocus();
|
||||||
double scale_factor = 1.0;
|
double scale_factor = 1.0;
|
||||||
|
|
||||||
if (focus && SDL_SurfaceHasAlternateImages(custom_cursor->sdl_cursor_surfaces[0])) {
|
// If the surfaces were released, there are no scaled images.
|
||||||
|
if (focus && custom_cursor->sdl_cursor_surfaces[0]) {
|
||||||
scale_factor = focus->internal->scale_factor;
|
scale_factor = focus->internal->scale_factor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -580,42 +593,83 @@ static bool Wayland_GetCustomCursor(SDL_CursorData *cursor, SDL_WaylandSeat *sea
|
|||||||
|
|
||||||
seat->pointer.cursor_state.cursor_handle = c;
|
seat->pointer.cursor_state.cursor_handle = c;
|
||||||
*scale = SDL_ceil(scale_factor) == scale_factor ? (int)scale_factor : 0;
|
*scale = SDL_ceil(scale_factor) == scale_factor ? (int)scale_factor : 0;
|
||||||
*dst_width = custom_cursor->sdl_cursor_surfaces[0]->w;
|
*dst_width = custom_cursor->width;
|
||||||
*dst_height = custom_cursor->sdl_cursor_surfaces[0]->h;
|
*dst_height = custom_cursor->height;
|
||||||
*hot_x = custom_cursor->hot_x;
|
*hot_x = custom_cursor->hot_x;
|
||||||
*hot_y = custom_cursor->hot_y;
|
*hot_y = custom_cursor->hot_y;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SDL_Cursor *Wayland_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y)
|
static SDL_Cursor *Wayland_CreateAnimatedCursor(SDL_CursorFrameInfo *frames, int frame_count, int hot_x, int hot_y)
|
||||||
{
|
{
|
||||||
SDL_Cursor *cursor = SDL_calloc(1, sizeof(*cursor));
|
SDL_Cursor *cursor = SDL_calloc(1, sizeof(*cursor));
|
||||||
|
|
||||||
if (cursor) {
|
if (cursor) {
|
||||||
SDL_CursorData *data = SDL_calloc(1, sizeof(*data) + sizeof(SDL_Surface *));
|
SDL_CursorData *data = SDL_calloc(1, sizeof(*data) + (sizeof(SDL_Surface *) * frame_count));
|
||||||
if (!data) {
|
if (!data) {
|
||||||
SDL_free(cursor);
|
SDL_free(cursor);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data->frame_durations_ms = SDL_calloc(frame_count, sizeof(Uint32));
|
||||||
|
if (!data->frame_durations_ms) {
|
||||||
|
SDL_free(data);
|
||||||
|
SDL_free(cursor);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
cursor->internal = data;
|
cursor->internal = data;
|
||||||
WAYLAND_wl_list_init(&data->cursor_data.custom.scaled_cursor_cache);
|
WAYLAND_wl_list_init(&data->cursor_data.custom.scaled_cursor_cache);
|
||||||
data->num_frames = 1;
|
data->cursor_data.custom.width = frames[0].surface->w;
|
||||||
|
data->cursor_data.custom.height = frames[0].surface->h;
|
||||||
data->cursor_data.custom.hot_x = hot_x;
|
data->cursor_data.custom.hot_x = hot_x;
|
||||||
data->cursor_data.custom.hot_y = hot_y;
|
data->cursor_data.custom.hot_y = hot_y;
|
||||||
|
data->num_frames = frame_count;
|
||||||
|
|
||||||
data->cursor_data.custom.sdl_cursor_surfaces[0] = surface;
|
for (int i = 0; i < frame_count; ++i) {
|
||||||
++surface->refcount;
|
data->frame_durations_ms[i] = frames[i].duration;
|
||||||
|
if (data->total_duration_ms < SDL_MAX_UINT32) {
|
||||||
|
if (data->frame_durations_ms[i] > 0) {
|
||||||
|
data->total_duration_ms += data->frame_durations_ms[i];
|
||||||
|
} else {
|
||||||
|
data->total_duration_ms = SDL_MAX_UINT32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data->cursor_data.custom.sdl_cursor_surfaces[i] = frames[i].surface;
|
||||||
|
++frames[i].surface->refcount;
|
||||||
|
}
|
||||||
|
|
||||||
// If the cursor has only one size, just prepare it now.
|
// If the cursor has only one size, just prepare it now.
|
||||||
if (!SDL_SurfaceHasAlternateImages(surface)) {
|
if (!SDL_SurfaceHasAlternateImages(frames[0].surface)) {
|
||||||
Wayland_CacheScaledCustomCursor(data, 1.0);
|
bool success = !!Wayland_CacheScaledCustomCursor(data, 1.0);
|
||||||
|
|
||||||
|
// Done with the surfaces.
|
||||||
|
for (int i = 0; i < frame_count; ++i) {
|
||||||
|
SDL_DestroySurface(data->cursor_data.custom.sdl_cursor_surfaces[i]);
|
||||||
|
data->cursor_data.custom.sdl_cursor_surfaces[i] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
SDL_free(data);
|
||||||
|
SDL_free(cursor);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return cursor;
|
return cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SDL_Cursor *Wayland_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y)
|
||||||
|
{
|
||||||
|
SDL_CursorFrameInfo frame = {
|
||||||
|
surface, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
return Wayland_CreateAnimatedCursor(&frame, 1, hot_x, hot_y);
|
||||||
|
}
|
||||||
|
|
||||||
static SDL_Cursor *Wayland_CreateSystemCursor(SDL_SystemCursor id)
|
static SDL_Cursor *Wayland_CreateSystemCursor(SDL_SystemCursor id)
|
||||||
{
|
{
|
||||||
SDL_Cursor *cursor = SDL_calloc(1, sizeof(*cursor));
|
SDL_Cursor *cursor = SDL_calloc(1, sizeof(*cursor));
|
||||||
@@ -660,7 +714,6 @@ static void Wayland_FreeCursorData(SDL_CursorData *d)
|
|||||||
wl_surface_attach(seat->pointer.cursor_state.surface, NULL, 0, 0);
|
wl_surface_attach(seat->pointer.cursor_state.surface, NULL, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
seat->pointer.current_cursor = NULL;
|
seat->pointer.current_cursor = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1120,6 +1173,7 @@ void Wayland_InitMouse(void)
|
|||||||
SDL_Mouse *mouse = SDL_GetMouse();
|
SDL_Mouse *mouse = SDL_GetMouse();
|
||||||
|
|
||||||
mouse->CreateCursor = Wayland_CreateCursor;
|
mouse->CreateCursor = Wayland_CreateCursor;
|
||||||
|
mouse->CreateAnimatedCursor = Wayland_CreateAnimatedCursor;
|
||||||
mouse->CreateSystemCursor = Wayland_CreateSystemCursor;
|
mouse->CreateSystemCursor = Wayland_CreateSystemCursor;
|
||||||
mouse->ShowCursor = Wayland_ShowCursor;
|
mouse->ShowCursor = Wayland_ShowCursor;
|
||||||
mouse->FreeCursor = Wayland_FreeCursor;
|
mouse->FreeCursor = Wayland_FreeCursor;
|
||||||
|
|||||||
@@ -31,6 +31,66 @@
|
|||||||
#include "../../joystick/usb_ids.h"
|
#include "../../joystick/usb_ids.h"
|
||||||
#include "../../core/windows/SDL_windows.h" // for checking windows version
|
#include "../../core/windows/SDL_windows.h" // for checking windows version
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
|
||||||
|
#define RIFF_FOURCC(c0, c1, c2, c3) \
|
||||||
|
((DWORD)(BYTE)(c0) | ((DWORD)(BYTE)(c1) << 8) | \
|
||||||
|
((DWORD)(BYTE)(c2) << 16) | ((DWORD)(BYTE)(c3) << 24))
|
||||||
|
|
||||||
|
#define ANI_FLAG_ICON 0x1
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
BYTE bWidth;
|
||||||
|
BYTE bHeight;
|
||||||
|
BYTE bColorCount;
|
||||||
|
BYTE bReserved;
|
||||||
|
WORD xHotspot;
|
||||||
|
WORD yHotspot;
|
||||||
|
DWORD dwDIBSize;
|
||||||
|
DWORD dwDIBOffset;
|
||||||
|
} CURSORICONFILEDIRENTRY;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
WORD idReserved;
|
||||||
|
WORD idType;
|
||||||
|
WORD idCount;
|
||||||
|
CURSORICONFILEDIRENTRY idEntries;
|
||||||
|
} CURSORICONFILEDIR;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
DWORD chunkType; // 'icon'
|
||||||
|
DWORD chunkSize;
|
||||||
|
|
||||||
|
CURSORICONFILEDIR icon_info;
|
||||||
|
BITMAPINFOHEADER bmi_header;
|
||||||
|
} ANIMICONINFO;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
DWORD riffID;
|
||||||
|
DWORD riffSizeof;
|
||||||
|
|
||||||
|
DWORD aconChunkID; // 'ACON'
|
||||||
|
DWORD aniChunkID; // 'anih'
|
||||||
|
DWORD aniSizeof; // sizeof(ANIHEADER) = 36 bytes
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
DWORD cbSizeof; // sizeof(ANIHEADER) = 36 bytes.
|
||||||
|
DWORD frames; // Number of frames in the frame list.
|
||||||
|
DWORD steps; // Number of steps in the animation loop.
|
||||||
|
DWORD width; // Width
|
||||||
|
DWORD height; // Height
|
||||||
|
DWORD bpp; // bpp
|
||||||
|
DWORD planes; // Not used
|
||||||
|
DWORD jifRate; // Default display rate, in jiffies (1/60s)
|
||||||
|
DWORD fl; // AF_ICON should be set. AF_SEQUENCE is optional
|
||||||
|
} ANIHEADER;
|
||||||
|
} RIFFHEADER;
|
||||||
|
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
typedef struct CachedCursor
|
typedef struct CachedCursor
|
||||||
{
|
{
|
||||||
@@ -41,11 +101,12 @@ typedef struct CachedCursor
|
|||||||
|
|
||||||
struct SDL_CursorData
|
struct SDL_CursorData
|
||||||
{
|
{
|
||||||
SDL_Surface *surface;
|
|
||||||
int hot_x;
|
int hot_x;
|
||||||
int hot_y;
|
int hot_y;
|
||||||
|
int num_frames;
|
||||||
CachedCursor *cache;
|
CachedCursor *cache;
|
||||||
HCURSOR cursor;
|
HCURSOR cursor;
|
||||||
|
SDL_CursorFrameInfo frames[1];
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
@@ -236,6 +297,141 @@ cleanup:
|
|||||||
return hcursor;
|
return hcursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Windows doesn't have an API to easily create animated cursors from a sequence of images,
|
||||||
|
* so we have to build an animated cursor resource file in memory and load it.
|
||||||
|
*/
|
||||||
|
static HCURSOR WIN_CreateAnimatedCursorInternal(SDL_CursorFrameInfo *frames, int frame_count, int hot_x, int hot_y, float scale)
|
||||||
|
{
|
||||||
|
static const double WIN32_JIFFY = 1000.0 / 60.0;
|
||||||
|
SDL_Surface *surface = NULL;
|
||||||
|
bool use_scaled_surfaces = scale != 1.0f;
|
||||||
|
|
||||||
|
if (use_scaled_surfaces) {
|
||||||
|
surface = SDL_GetSurfaceImage(frames[0].surface, scale);
|
||||||
|
} else {
|
||||||
|
surface = frames[0].surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since XP and still as of Win11, Windows cursors have a hard size limit of 256x256.
|
||||||
|
if (!surface || surface->w > 256 || surface->h > 256) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DWORD image_data_size = surface->w * surface->pitch * 2;
|
||||||
|
const DWORD total_image_data_size = image_data_size * frame_count;
|
||||||
|
const DWORD alloc_size = sizeof(RIFFHEADER) + (sizeof(DWORD) * (5 + frame_count)) + (sizeof(ANIMICONINFO) * frame_count) + total_image_data_size;
|
||||||
|
const int w = surface->w;
|
||||||
|
const int h = surface->h;
|
||||||
|
|
||||||
|
hot_x = (int)SDL_round(hot_x * scale);
|
||||||
|
hot_y = (int)SDL_round(hot_y * scale);
|
||||||
|
|
||||||
|
BYTE *membase = SDL_malloc(alloc_size);
|
||||||
|
if (!membase) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
RIFFHEADER *riff = (RIFFHEADER *)membase;
|
||||||
|
riff->riffID = RIFF_FOURCC('R', 'I', 'F', 'F');
|
||||||
|
riff->riffSizeof = alloc_size - (sizeof(DWORD) * 2); // The total size, minus the RIFF header DWORDs.
|
||||||
|
riff->aconChunkID = RIFF_FOURCC('A', 'C', 'O', 'N');
|
||||||
|
riff->aniChunkID = RIFF_FOURCC('a', 'n', 'i', 'h');
|
||||||
|
riff->aniSizeof = sizeof(riff->ANIHEADER);
|
||||||
|
riff->ANIHEADER.cbSizeof = sizeof(riff->ANIHEADER);
|
||||||
|
riff->ANIHEADER.frames = frame_count;
|
||||||
|
riff->ANIHEADER.steps = frame_count;
|
||||||
|
riff->ANIHEADER.width = w;
|
||||||
|
riff->ANIHEADER.height = h;
|
||||||
|
riff->ANIHEADER.bpp = 32;
|
||||||
|
riff->ANIHEADER.planes = 1;
|
||||||
|
riff->ANIHEADER.jifRate = 1;
|
||||||
|
riff->ANIHEADER.fl = ANI_FLAG_ICON;
|
||||||
|
|
||||||
|
DWORD *dwptr = (DWORD *)(membase + sizeof(*riff));
|
||||||
|
|
||||||
|
// Rate chunk
|
||||||
|
*dwptr++ = RIFF_FOURCC('r', 'a', 't', 'e');
|
||||||
|
*dwptr++ = sizeof(DWORD) * frame_count;
|
||||||
|
for (int i = 0; i < frame_count; ++i) {
|
||||||
|
// Animated Win32 cursors are in jiffy units, and one jiffy is 1/60 of a second.
|
||||||
|
*dwptr++ = frames[i].duration ? SDL_lround(frames[i].duration / WIN32_JIFFY) : 0xFFFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Frame list chunk
|
||||||
|
*dwptr++ = RIFF_FOURCC('L', 'I', 'S', 'T');
|
||||||
|
*dwptr++ = (sizeof(ANIMICONINFO) * frame_count) + total_image_data_size + sizeof(DWORD);
|
||||||
|
*dwptr++ = RIFF_FOURCC('f', 'r', 'a', 'm');
|
||||||
|
|
||||||
|
BYTE *icon_data = (BYTE *)dwptr;
|
||||||
|
|
||||||
|
for (int i = 0; i < frame_count; ++i) {
|
||||||
|
if (!surface) {
|
||||||
|
if (use_scaled_surfaces) {
|
||||||
|
surface = SDL_GetSurfaceImage(frames[i].surface, scale);
|
||||||
|
if (!surface) {
|
||||||
|
SDL_free(membase);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
surface = frames[i].surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cursor data is double height (DIB and mask), and has a max width and height of 256 (represented by a value of 0).
|
||||||
|
* https://devblogs.microsoft.com/oldnewthing/20101018-00/?p=12513
|
||||||
|
*/
|
||||||
|
ANIMICONINFO *icon_info = (ANIMICONINFO *)icon_data;
|
||||||
|
icon_info->chunkType = RIFF_FOURCC('i', 'c', 'o', 'n');
|
||||||
|
icon_info->chunkSize = sizeof(ANIMICONINFO) + image_data_size - (sizeof(DWORD) * 2);
|
||||||
|
icon_info->icon_info.idReserved = 0;
|
||||||
|
icon_info->icon_info.idType = 2;
|
||||||
|
icon_info->icon_info.idCount = 1;
|
||||||
|
icon_info->icon_info.idEntries.bWidth = w < 256 ? w : 0; // 0 means a width of 256
|
||||||
|
icon_info->icon_info.idEntries.bHeight = h < 256 ? h : 0; // 0 means a height of 256
|
||||||
|
icon_info->icon_info.idEntries.bColorCount = 0;
|
||||||
|
icon_info->icon_info.idEntries.bReserved = 0;
|
||||||
|
icon_info->icon_info.idEntries.xHotspot = hot_x;
|
||||||
|
icon_info->icon_info.idEntries.yHotspot = hot_y;
|
||||||
|
icon_info->icon_info.idEntries.dwDIBSize = image_data_size;
|
||||||
|
icon_info->icon_info.idEntries.dwDIBOffset = offsetof(ANIMICONINFO, bmi_header) - (sizeof(DWORD) * 2);
|
||||||
|
icon_info->bmi_header.biSize = sizeof(BITMAPINFOHEADER);
|
||||||
|
icon_info->bmi_header.biWidth = w;
|
||||||
|
icon_info->bmi_header.biHeight = h * 2;
|
||||||
|
icon_info->bmi_header.biPlanes = 1;
|
||||||
|
icon_info->bmi_header.biBitCount = 32;
|
||||||
|
icon_info->bmi_header.biCompression = BI_RGB;
|
||||||
|
icon_info->bmi_header.biSizeImage = 0;
|
||||||
|
icon_info->bmi_header.biXPelsPerMeter = 0;
|
||||||
|
icon_info->bmi_header.biYPelsPerMeter = 0;
|
||||||
|
icon_info->bmi_header.biClrUsed = 0;
|
||||||
|
icon_info->bmi_header.biClrImportant = 0;
|
||||||
|
|
||||||
|
icon_data += sizeof(ANIMICONINFO);
|
||||||
|
|
||||||
|
// Cursor DIB images are stored bottom-up and double height: the bitmap, and the mask
|
||||||
|
const Uint8 *pix = frames[i].surface->pixels;
|
||||||
|
pix += (frames[i].surface->h - 1) * frames[i].surface->pitch;
|
||||||
|
for (int j = 0; j < frames[i].surface->h; j++) {
|
||||||
|
SDL_memcpy(icon_data, pix, frames[i].surface->pitch);
|
||||||
|
pix -= frames[i].surface->pitch;
|
||||||
|
icon_data += frames[i].surface->pitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should we generate mask data here?
|
||||||
|
icon_data += (image_data_size / 2);
|
||||||
|
|
||||||
|
if (use_scaled_surfaces) {
|
||||||
|
SDL_DestroySurface(surface);
|
||||||
|
}
|
||||||
|
surface = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
HCURSOR hcursor = (HCURSOR)CreateIconFromResource(membase, alloc_size, FALSE, 0x00030000);
|
||||||
|
SDL_free(membase);
|
||||||
|
|
||||||
|
return hcursor;
|
||||||
|
}
|
||||||
|
|
||||||
static SDL_Cursor *WIN_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y)
|
static SDL_Cursor *WIN_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y)
|
||||||
{
|
{
|
||||||
if (!SDL_SurfaceHasAlternateImages(surface)) {
|
if (!SDL_SurfaceHasAlternateImages(surface)) {
|
||||||
@@ -256,13 +452,45 @@ static SDL_Cursor *WIN_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y)
|
|||||||
}
|
}
|
||||||
data->hot_x = hot_x;
|
data->hot_x = hot_x;
|
||||||
data->hot_y = hot_y;
|
data->hot_y = hot_y;
|
||||||
data->surface = surface;
|
data->num_frames = 1;
|
||||||
|
data->frames[0].surface = surface;
|
||||||
++surface->refcount;
|
++surface->refcount;
|
||||||
cursor->internal = data;
|
cursor->internal = data;
|
||||||
}
|
}
|
||||||
return cursor;
|
return cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SDL_Cursor *WIN_CreateAnimatedCursor(SDL_CursorFrameInfo *frames, int frame_count, int hot_x, int hot_y)
|
||||||
|
{
|
||||||
|
if (!SDL_SurfaceHasAlternateImages(frames[0].surface)) {
|
||||||
|
HCURSOR hcursor = WIN_CreateAnimatedCursorInternal(frames, frame_count, hot_x, hot_y, 1.0f);
|
||||||
|
if (!hcursor) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return WIN_CreateCursorAndData(hcursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dynamically generate cursors at the appropriate DPI
|
||||||
|
SDL_Cursor *cursor = (SDL_Cursor *)SDL_calloc(1, sizeof(*cursor));
|
||||||
|
if (cursor) {
|
||||||
|
SDL_CursorData *data = (SDL_CursorData *)SDL_calloc(1, sizeof(*data) + (sizeof(SDL_CursorFrameInfo) * (frame_count - 1)));
|
||||||
|
if (!data) {
|
||||||
|
SDL_free(cursor);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
data->hot_x = hot_x;
|
||||||
|
data->hot_y = hot_y;
|
||||||
|
data->num_frames = 1;
|
||||||
|
for (int i = 0; i < frame_count; ++i) {
|
||||||
|
data->frames[i].surface = frames[i].surface;
|
||||||
|
data->frames[i].duration = frames[i].duration;
|
||||||
|
++frames[i].surface->refcount;
|
||||||
|
}
|
||||||
|
cursor->internal = data;
|
||||||
|
}
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
static SDL_Cursor *WIN_CreateBlankCursor(void)
|
static SDL_Cursor *WIN_CreateBlankCursor(void)
|
||||||
{
|
{
|
||||||
SDL_Cursor *cursor = NULL;
|
SDL_Cursor *cursor = NULL;
|
||||||
@@ -356,8 +584,8 @@ static void WIN_FreeCursor(SDL_Cursor *cursor)
|
|||||||
{
|
{
|
||||||
SDL_CursorData *data = cursor->internal;
|
SDL_CursorData *data = cursor->internal;
|
||||||
|
|
||||||
if (data->surface) {
|
for (int i = 0; i < data->num_frames; ++i) {
|
||||||
SDL_DestroySurface(data->surface);
|
SDL_DestroySurface(data->frames[i].surface);
|
||||||
}
|
}
|
||||||
while (data->cache) {
|
while (data->cache) {
|
||||||
CachedCursor *entry = data->cache;
|
CachedCursor *entry = data->cache;
|
||||||
@@ -390,12 +618,14 @@ static HCURSOR GetCachedCursor(SDL_Cursor *cursor)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Need to create a cursor for this content scale
|
|
||||||
SDL_Surface *surface = NULL;
|
|
||||||
HCURSOR hcursor = NULL;
|
|
||||||
CachedCursor *entry = NULL;
|
CachedCursor *entry = NULL;
|
||||||
|
HCURSOR hcursor = NULL;
|
||||||
|
|
||||||
surface = SDL_GetSurfaceImage(data->surface, scale);
|
// Need to create a cursor for this content scale
|
||||||
|
if (data->num_frames == 1) {
|
||||||
|
SDL_Surface *surface = NULL;
|
||||||
|
|
||||||
|
surface = SDL_GetSurfaceImage(data->frames[0].surface, scale);
|
||||||
if (!surface) {
|
if (!surface) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
@@ -403,9 +633,13 @@ static HCURSOR GetCachedCursor(SDL_Cursor *cursor)
|
|||||||
int hot_x = (int)SDL_round(data->hot_x * scale);
|
int hot_x = (int)SDL_round(data->hot_x * scale);
|
||||||
int hot_y = (int)SDL_round(data->hot_y * scale);
|
int hot_y = (int)SDL_round(data->hot_y * scale);
|
||||||
hcursor = WIN_CreateHCursor(surface, hot_x, hot_y);
|
hcursor = WIN_CreateHCursor(surface, hot_x, hot_y);
|
||||||
|
SDL_DestroySurface(surface);
|
||||||
if (!hcursor) {
|
if (!hcursor) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
hcursor = WIN_CreateAnimatedCursorInternal(data->frames, data->num_frames, data->hot_x, data->hot_y, scale);
|
||||||
|
}
|
||||||
|
|
||||||
entry = (CachedCursor *)SDL_malloc(sizeof(*entry));
|
entry = (CachedCursor *)SDL_malloc(sizeof(*entry));
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
@@ -416,14 +650,9 @@ static HCURSOR GetCachedCursor(SDL_Cursor *cursor)
|
|||||||
entry->next = data->cache;
|
entry->next = data->cache;
|
||||||
data->cache = entry;
|
data->cache = entry;
|
||||||
|
|
||||||
SDL_DestroySurface(surface);
|
|
||||||
|
|
||||||
return hcursor;
|
return hcursor;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
if (surface) {
|
|
||||||
SDL_DestroySurface(surface);
|
|
||||||
}
|
|
||||||
if (hcursor) {
|
if (hcursor) {
|
||||||
DestroyCursor(hcursor);
|
DestroyCursor(hcursor);
|
||||||
}
|
}
|
||||||
@@ -440,7 +669,7 @@ static bool WIN_ShowCursor(SDL_Cursor *cursor)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (cursor) {
|
if (cursor) {
|
||||||
if (cursor->internal->surface) {
|
if (cursor->internal->num_frames) {
|
||||||
SDL_cursor = GetCachedCursor(cursor);
|
SDL_cursor = GetCachedCursor(cursor);
|
||||||
} else {
|
} else {
|
||||||
SDL_cursor = cursor->internal->cursor;
|
SDL_cursor = cursor->internal->cursor;
|
||||||
@@ -644,6 +873,7 @@ void WIN_InitMouse(SDL_VideoDevice *_this)
|
|||||||
SDL_Mouse *mouse = SDL_GetMouse();
|
SDL_Mouse *mouse = SDL_GetMouse();
|
||||||
|
|
||||||
mouse->CreateCursor = WIN_CreateCursor;
|
mouse->CreateCursor = WIN_CreateCursor;
|
||||||
|
mouse->CreateAnimatedCursor = WIN_CreateAnimatedCursor;
|
||||||
mouse->CreateSystemCursor = WIN_CreateSystemCursor;
|
mouse->CreateSystemCursor = WIN_CreateSystemCursor;
|
||||||
mouse->ShowCursor = WIN_ShowCursor;
|
mouse->ShowCursor = WIN_ShowCursor;
|
||||||
mouse->FreeCursor = WIN_FreeCursor;
|
mouse->FreeCursor = WIN_FreeCursor;
|
||||||
|
|||||||
@@ -116,6 +116,44 @@ static Cursor X11_CreateXCursorCursor(SDL_Surface *surface, int hot_x, int hot_y
|
|||||||
|
|
||||||
return cursor;
|
return cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Cursor X11_CreateAnimatedXCursorCursor(SDL_CursorFrameInfo *frames, int num_frames, int hot_x, int hot_y)
|
||||||
|
{
|
||||||
|
Display *display = GetDisplay();
|
||||||
|
Cursor cursor = None;
|
||||||
|
XcursorImage *image;
|
||||||
|
XcursorImages *images;
|
||||||
|
|
||||||
|
images = X11_XcursorImagesCreate(num_frames);
|
||||||
|
if (!images) {
|
||||||
|
SDL_OutOfMemory();
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < num_frames; ++i) {
|
||||||
|
image = X11_XcursorImageCreate(frames[i].surface->w, frames[i].surface->h);
|
||||||
|
if (!image) {
|
||||||
|
SDL_OutOfMemory();
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
image->xhot = hot_x;
|
||||||
|
image->yhot = hot_y;
|
||||||
|
image->delay = frames[i].duration;
|
||||||
|
|
||||||
|
SDL_assert(frames[i].surface->format == SDL_PIXELFORMAT_ARGB8888);
|
||||||
|
SDL_assert(frames[i].surface->pitch == frames[i].surface->w * 4);
|
||||||
|
SDL_memcpy(image->pixels, frames[i].surface->pixels, (size_t)frames[i].surface->h * frames[i].surface->pitch);
|
||||||
|
|
||||||
|
images->images[i] = image;
|
||||||
|
images->nimage++;
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor = X11_XcursorImagesLoadCursor(display, images);
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
X11_XcursorImagesDestroy(images);
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
#endif // SDL_VIDEO_DRIVER_X11_XCURSOR
|
#endif // SDL_VIDEO_DRIVER_X11_XCURSOR
|
||||||
|
|
||||||
static Cursor X11_CreatePixmapCursor(SDL_Surface *surface, int hot_x, int hot_y)
|
static Cursor X11_CreatePixmapCursor(SDL_Surface *surface, int hot_x, int hot_y)
|
||||||
@@ -219,6 +257,22 @@ static SDL_Cursor *X11_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y)
|
|||||||
return X11_CreateCursorAndData(x11_cursor);
|
return X11_CreateCursorAndData(x11_cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SDL_Cursor *X11_CreateAnimatedCursor(SDL_CursorFrameInfo *frames, int num_frames, int hot_x, int hot_y)
|
||||||
|
{
|
||||||
|
Cursor x11_cursor = None;
|
||||||
|
|
||||||
|
#ifdef SDL_VIDEO_DRIVER_X11_XCURSOR
|
||||||
|
if (SDL_X11_HAVE_XCURSOR) {
|
||||||
|
x11_cursor = X11_CreateAnimatedXCursorCursor(frames, num_frames, hot_x, hot_y);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (x11_cursor == None) {
|
||||||
|
x11_cursor = X11_CreatePixmapCursor(frames[0].surface, hot_x, hot_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
return X11_CreateCursorAndData(x11_cursor);;
|
||||||
|
}
|
||||||
|
|
||||||
static unsigned int GetLegacySystemCursorShape(SDL_SystemCursor id)
|
static unsigned int GetLegacySystemCursorShape(SDL_SystemCursor id)
|
||||||
{
|
{
|
||||||
switch (id) {
|
switch (id) {
|
||||||
@@ -499,6 +553,7 @@ void X11_InitMouse(SDL_VideoDevice *_this)
|
|||||||
SDL_Mouse *mouse = SDL_GetMouse();
|
SDL_Mouse *mouse = SDL_GetMouse();
|
||||||
|
|
||||||
mouse->CreateCursor = X11_CreateCursor;
|
mouse->CreateCursor = X11_CreateCursor;
|
||||||
|
mouse->CreateAnimatedCursor = X11_CreateAnimatedCursor;
|
||||||
mouse->CreateSystemCursor = X11_CreateSystemCursor;
|
mouse->CreateSystemCursor = X11_CreateSystemCursor;
|
||||||
mouse->ShowCursor = X11_ShowCursor;
|
mouse->ShowCursor = X11_ShowCursor;
|
||||||
mouse->FreeCursor = X11_FreeCursor;
|
mouse->FreeCursor = X11_FreeCursor;
|
||||||
|
|||||||
@@ -277,8 +277,11 @@ SDL_X11_SYM(int,ipUnallocateAndSendData,(ChannelPtr a,IPCard b))
|
|||||||
#ifdef SDL_VIDEO_DRIVER_X11_XCURSOR
|
#ifdef SDL_VIDEO_DRIVER_X11_XCURSOR
|
||||||
SDL_X11_MODULE(XCURSOR)
|
SDL_X11_MODULE(XCURSOR)
|
||||||
SDL_X11_SYM(XcursorImage*,XcursorImageCreate,(int a,int b))
|
SDL_X11_SYM(XcursorImage*,XcursorImageCreate,(int a,int b))
|
||||||
|
SDL_X11_SYM(XcursorImages*,XcursorImagesCreate,(int a))
|
||||||
SDL_X11_SYM(void,XcursorImageDestroy,(XcursorImage *a))
|
SDL_X11_SYM(void,XcursorImageDestroy,(XcursorImage *a))
|
||||||
|
SDL_X11_SYM(void,XcursorImagesDestroy,(XcursorImages *a))
|
||||||
SDL_X11_SYM(Cursor,XcursorImageLoadCursor,(Display *a,const XcursorImage *b))
|
SDL_X11_SYM(Cursor,XcursorImageLoadCursor,(Display *a,const XcursorImage *b))
|
||||||
|
SDL_X11_SYM(Cursor,XcursorImagesLoadCursor,(Display *a,const XcursorImages *b))
|
||||||
SDL_X11_SYM(Cursor,XcursorLibraryLoadCursor,(Display *a, const char *b))
|
SDL_X11_SYM(Cursor,XcursorLibraryLoadCursor,(Display *a, const char *b))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -171,11 +171,51 @@ static SDL_Surface *load_image(const char *file)
|
|||||||
static SDL_Cursor *init_color_cursor(const char *file)
|
static SDL_Cursor *init_color_cursor(const char *file)
|
||||||
{
|
{
|
||||||
SDL_Cursor *cursor = NULL;
|
SDL_Cursor *cursor = NULL;
|
||||||
SDL_Surface *surface = load_image(file);
|
SDL_CursorFrameInfo *frames = NULL;
|
||||||
if (surface) {
|
int frame_cnt = 0;
|
||||||
cursor = SDL_CreateColorCursor(surface, 0, 0);
|
int i;
|
||||||
SDL_DestroySurface(surface);
|
|
||||||
|
char *str = SDL_strdup(file);
|
||||||
|
if (!str) {
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *saveptr = NULL;
|
||||||
|
char *token = SDL_strtok_r(str, ";", &saveptr);
|
||||||
|
while(token != NULL) {
|
||||||
|
SDL_Surface *img = load_image(token);
|
||||||
|
if (!img) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
frames = SDL_realloc(frames, (frame_cnt + 1) * sizeof(SDL_CursorFrameInfo));
|
||||||
|
if (!frames) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
frames[frame_cnt].surface = img;
|
||||||
|
frames[frame_cnt++].duration = 150;
|
||||||
|
|
||||||
|
token = SDL_strtok_r(NULL, ";", &saveptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frame_cnt == 1) {
|
||||||
|
cursor = SDL_CreateColorCursor(frames[0].surface, 0, 0);
|
||||||
|
} else {
|
||||||
|
cursor = SDL_CreateAnimatedCursor(frames, frame_cnt, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
if (frames) {
|
||||||
|
for (i = 0; i < frame_cnt; ++i) {
|
||||||
|
SDL_DestroySurface(frames[i].surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_free(frames);
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_free(str);
|
||||||
|
|
||||||
return cursor;
|
return cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,10 +257,78 @@ static SDL_Cursor *init_system_cursor(const char *image[])
|
|||||||
return SDL_CreateCursor(data, mask, 32, 32, hot_x, hot_y);
|
return SDL_CreateCursor(data, mask, 32, 32, hot_x, hot_y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SDL_Cursor *init_animated_cursor(const char *image[], bool oneshot)
|
||||||
|
{
|
||||||
|
int row, col;
|
||||||
|
SDL_Surface *surface, *invsurface;
|
||||||
|
Uint32 *pixels, *invpixels;
|
||||||
|
SDL_CursorFrameInfo frames[6];
|
||||||
|
int hot_x = 0;
|
||||||
|
int hot_y = 0;
|
||||||
|
|
||||||
|
surface = SDL_CreateSurface(32, 32, SDL_PIXELFORMAT_ARGB8888);
|
||||||
|
if (!surface) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
invsurface = SDL_CreateSurface(32, 32, SDL_PIXELFORMAT_ARGB8888);
|
||||||
|
if (!invsurface) {
|
||||||
|
SDL_DestroySurface(surface);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (row = 4; row < 36; ++row) {
|
||||||
|
pixels = (Uint32 *)((Uint8 *)surface->pixels + ((row - 4) * surface->pitch));
|
||||||
|
invpixels = (Uint32 *)((Uint8 *)invsurface->pixels + ((row - 4) * surface->pitch));
|
||||||
|
for (col = 0; col < 32; ++col) {
|
||||||
|
switch (image[row][col]) {
|
||||||
|
case 'X':
|
||||||
|
pixels[col] = 0xFFFFFFFF;
|
||||||
|
invpixels[col] = 0xFF000000;
|
||||||
|
break;
|
||||||
|
case '.':
|
||||||
|
pixels[col] = 0xFF000000;
|
||||||
|
invpixels[col] = 0xFFFFFFFF;
|
||||||
|
break;
|
||||||
|
case ' ':
|
||||||
|
pixels[col] = 0;
|
||||||
|
invpixels[col] = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int frame_count = 2;
|
||||||
|
|
||||||
|
frames[0].surface = surface;
|
||||||
|
frames[0].duration = 100;
|
||||||
|
|
||||||
|
frames[1].surface = invsurface;
|
||||||
|
frames[1].duration = 100;
|
||||||
|
|
||||||
|
if (oneshot) {
|
||||||
|
frames[2].surface = surface;
|
||||||
|
frames[2].duration = 200;
|
||||||
|
|
||||||
|
frames[3].surface = invsurface;
|
||||||
|
frames[3].duration = 300;
|
||||||
|
|
||||||
|
frames[4].surface = surface;
|
||||||
|
frames[4].duration = 400;
|
||||||
|
|
||||||
|
frames[5].surface = invsurface;
|
||||||
|
frames[5].duration = 0;
|
||||||
|
|
||||||
|
frame_count = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SDL_CreateAnimatedCursor(frames, frame_count, hot_x, hot_y);
|
||||||
|
}
|
||||||
|
|
||||||
static SDLTest_CommonState *state;
|
static SDLTest_CommonState *state;
|
||||||
static int done;
|
static int done;
|
||||||
static SDL_Cursor *cursors[3 + SDL_SYSTEM_CURSOR_COUNT];
|
static SDL_Cursor *cursors[5 + SDL_SYSTEM_CURSOR_COUNT];
|
||||||
static SDL_SystemCursor cursor_types[3 + SDL_SYSTEM_CURSOR_COUNT];
|
static SDL_SystemCursor cursor_types[5 + SDL_SYSTEM_CURSOR_COUNT];
|
||||||
static int num_cursors;
|
static int num_cursors;
|
||||||
static int current_cursor;
|
static int current_cursor;
|
||||||
static bool show_cursor;
|
static bool show_cursor;
|
||||||
@@ -257,6 +365,12 @@ static void loop(void)
|
|||||||
SDL_SetCursor(cursors[current_cursor]);
|
SDL_SetCursor(cursors[current_cursor]);
|
||||||
|
|
||||||
switch ((int)cursor_types[current_cursor]) {
|
switch ((int)cursor_types[current_cursor]) {
|
||||||
|
case (SDL_SystemCursor)-3:
|
||||||
|
SDL_Log("Animated custom cursor (one-shot)");
|
||||||
|
break;
|
||||||
|
case (SDL_SystemCursor)-2:
|
||||||
|
SDL_Log("Animated custom cursor");
|
||||||
|
break;
|
||||||
case (SDL_SystemCursor)-1:
|
case (SDL_SystemCursor)-1:
|
||||||
SDL_Log("Custom cursor");
|
SDL_Log("Custom cursor");
|
||||||
break;
|
break;
|
||||||
@@ -405,13 +519,22 @@ int main(int argc, char *argv[])
|
|||||||
num_cursors = 0;
|
num_cursors = 0;
|
||||||
|
|
||||||
if (color_cursor) {
|
if (color_cursor) {
|
||||||
SDL_Surface *icon = load_image(color_cursor);
|
/* Only load the first file in the list for the icon. */
|
||||||
|
char *icon_str = SDL_strdup(color_cursor);
|
||||||
|
if (icon_str) {
|
||||||
|
char *tok = SDL_strchr(icon_str, ';');
|
||||||
|
if (tok) {
|
||||||
|
*tok = '\0';
|
||||||
|
}
|
||||||
|
SDL_Surface *icon = load_image(icon_str);
|
||||||
|
SDL_free(icon_str);
|
||||||
if (icon) {
|
if (icon) {
|
||||||
for (i = 0; i < state->num_windows; ++i) {
|
for (i = 0; i < state->num_windows; ++i) {
|
||||||
SDL_SetWindowIcon(state->windows[i], icon);
|
SDL_SetWindowIcon(state->windows[i], icon);
|
||||||
}
|
}
|
||||||
SDL_DestroySurface(icon);
|
SDL_DestroySurface(icon);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cursor = init_color_cursor(color_cursor);
|
cursor = init_color_cursor(color_cursor);
|
||||||
if (cursor) {
|
if (cursor) {
|
||||||
@@ -435,6 +558,20 @@ int main(int argc, char *argv[])
|
|||||||
num_cursors++;
|
num_cursors++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cursor = init_animated_cursor(arrow, false);
|
||||||
|
if (cursor) {
|
||||||
|
cursors[num_cursors] = cursor;
|
||||||
|
cursor_types[num_cursors] = (SDL_SystemCursor)-2;
|
||||||
|
num_cursors++;
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor = init_animated_cursor(arrow, true);
|
||||||
|
if (cursor) {
|
||||||
|
cursors[num_cursors] = cursor;
|
||||||
|
cursor_types[num_cursors] = (SDL_SystemCursor)-3;
|
||||||
|
num_cursors++;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < SDL_SYSTEM_CURSOR_COUNT; ++i) {
|
for (i = 0; i < SDL_SYSTEM_CURSOR_COUNT; ++i) {
|
||||||
cursor = SDL_CreateSystemCursor((SDL_SystemCursor)i);
|
cursor = SDL_CreateSystemCursor((SDL_SystemCursor)i);
|
||||||
if (cursor) {
|
if (cursor) {
|
||||||
|
|||||||
Reference in New Issue
Block a user