#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_RPI
#include "SDL_rpivideo.h"
#include "SDL_rpimouse.h"
#include "../SDL_sysvideo.h"
#include "../../events/SDL_mouse_c.h"
#include "../../events/default_cursor.h"
#define ELEMENT_CHANGE_LAYER (1 << 0)
#define ELEMENT_CHANGE_OPACITY (1 << 1)
#define ELEMENT_CHANGE_DEST_RECT (1 << 2)
#define ELEMENT_CHANGE_SRC_RECT (1 << 3)
#define ELEMENT_CHANGE_MASK_RESOURCE (1 << 4)
#define ELEMENT_CHANGE_TRANSFORM (1 << 5)
static SDL_Cursor *RPI_CreateDefaultCursor(void);
static SDL_Cursor *RPI_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y);
static bool RPI_ShowCursor(SDL_Cursor *cursor);
static bool RPI_MoveCursor(SDL_Cursor *cursor);
static void RPI_FreeCursor(SDL_Cursor *cursor);
static SDL_Cursor *global_cursor;
static SDL_Cursor *RPI_CreateDefaultCursor(void)
{
return SDL_CreateCursor(default_cdata, default_cmask, DEFAULT_CWIDTH, DEFAULT_CHEIGHT, DEFAULT_CHOTX, DEFAULT_CHOTY);
}
static SDL_Cursor *RPI_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y)
{
int rc;
SDL_CursorData *curdata;
SDL_Cursor *cursor;
VC_RECT_T dst_rect;
Uint32 dummy;
SDL_assert(surface->format == SDL_PIXELFORMAT_ARGB8888);
SDL_assert(surface->pitch == surface->w * 4);
cursor = (SDL_Cursor *)SDL_calloc(1, sizeof(*cursor));
if (!cursor) {
return NULL;
}
curdata = (SDL_CursorData *)SDL_calloc(1, sizeof(*curdata));
if (!curdata) {
SDL_free(cursor);
return NULL;
}
curdata->hot_x = hot_x;
curdata->hot_y = hot_y;
curdata->w = surface->w;
curdata->h = surface->h;
curdata->resource = vc_dispmanx_resource_create(VC_IMAGE_ARGB8888, surface->w | (surface->pitch << 16), surface->h | (surface->h << 16), &dummy);
SDL_assert(curdata->resource);
vc_dispmanx_rect_set(&dst_rect, 0, 0, curdata->w, curdata->h);
rc = vc_dispmanx_resource_write_data(curdata->resource, VC_IMAGE_ARGB8888, surface->pitch, surface->pixels, &dst_rect);
SDL_assert(rc == DISPMANX_SUCCESS);
cursor->internal = curdata;
return cursor;
}
static bool RPI_ShowCursor(SDL_Cursor *cursor)
{
int rc;
DISPMANX_UPDATE_HANDLE_T update;
SDL_CursorData *curdata;
VC_RECT_T src_rect, dst_rect;
SDL_Mouse *mouse = SDL_GetMouse();
SDL_DisplayData *data;
VC_DISPMANX_ALPHA_T alpha = { DISPMANX_FLAGS_ALPHA_FROM_SOURCE , 255 , 0 };
uint32_t layer = SDL_RPI_MOUSELAYER;
const char *hint;
if (cursor != global_cursor) {
if (global_cursor) {
curdata = global_cursor->internal;
if (curdata && curdata->element > DISPMANX_NO_HANDLE) {
update = vc_dispmanx_update_start(0);
SDL_assert(update);
rc = vc_dispmanx_element_remove(update, curdata->element);
SDL_assert(rc == DISPMANX_SUCCESS);
rc = vc_dispmanx_update_submit_sync(update);
SDL_assert(rc == DISPMANX_SUCCESS);
curdata->element = DISPMANX_NO_HANDLE;
}
}
global_cursor = cursor;
}
if (!cursor) {
return true;
}
curdata = cursor->internal;
if (!curdata) {
return false;
}
if (!mouse->focus) {
return false;
}
data = SDL_GetDisplayDriverDataForWindow(mouse->focus);
if (!data) {
return false;
}
if (curdata->element == DISPMANX_NO_HANDLE) {
vc_dispmanx_rect_set(&src_rect, 0, 0, curdata->w << 16, curdata->h << 16);
vc_dispmanx_rect_set(&dst_rect, (int)mouse->x - curdata->hot_x, (int)mouse->y - curdata->hot_y, curdata->w, curdata->h);
update = vc_dispmanx_update_start(0);
SDL_assert(update);
hint = SDL_GetHint(SDL_HINT_RPI_VIDEO_LAYER);
if (hint) {
layer = SDL_atoi(hint) + 1;
}
curdata->element = vc_dispmanx_element_add(update,
data->dispman_display,
layer,
&dst_rect,
curdata->resource,
&src_rect,
DISPMANX_PROTECTION_NONE,
&alpha,
DISPMANX_NO_HANDLE, DISPMANX_NO_ROTATE);
SDL_assert(curdata->element > DISPMANX_NO_HANDLE);
rc = vc_dispmanx_update_submit_sync(update);
SDL_assert(rc == DISPMANX_SUCCESS);
}
return true;
}
static void RPI_FreeCursor(SDL_Cursor *cursor)
{
int rc;
DISPMANX_UPDATE_HANDLE_T update;
SDL_CursorData *curdata;
if (cursor) {
curdata = cursor->internal;
if (curdata) {
if (curdata->element != DISPMANX_NO_HANDLE) {
update = vc_dispmanx_update_start(0);
SDL_assert(update);
rc = vc_dispmanx_element_remove(update, curdata->element);
SDL_assert(rc == DISPMANX_SUCCESS);
rc = vc_dispmanx_update_submit_sync(update);
SDL_assert(rc == DISPMANX_SUCCESS);
}
if (curdata->resource != DISPMANX_NO_HANDLE) {
rc = vc_dispmanx_resource_delete(curdata->resource);
SDL_assert(rc == DISPMANX_SUCCESS);
}
SDL_free(cursor->internal);
}
SDL_free(cursor);
if (cursor == global_cursor) {
global_cursor = NULL;
}
}
}
static bool RPI_WarpMouseGlobalGraphically(float x, float y)
{
int rc;
SDL_CursorData *curdata;
DISPMANX_UPDATE_HANDLE_T update;
VC_RECT_T dst_rect;
VC_RECT_T src_rect;
SDL_Mouse *mouse = SDL_GetMouse();
if (!mouse || !mouse->cur_cursor || !mouse->cur_cursor->internal) {
return true;
}
curdata = mouse->cur_cursor->internal;
if (curdata->element == DISPMANX_NO_HANDLE) {
return true;
}
update = vc_dispmanx_update_start(0);
if (!update) {
return true;
}
src_rect.x = 0;
src_rect.y = 0;
src_rect.width = curdata->w << 16;
src_rect.height = curdata->h << 16;
dst_rect.x = (int)x - curdata->hot_x;
dst_rect.y = (int)y - curdata->hot_y;
dst_rect.width = curdata->w;
dst_rect.height = curdata->h;
rc = vc_dispmanx_element_change_attributes(
update,
curdata->element,
0,
0,
0,
&dst_rect,
&src_rect,
DISPMANX_NO_HANDLE,
DISPMANX_NO_ROTATE);
if (rc != DISPMANX_SUCCESS) {
return SDL_SetError("vc_dispmanx_element_change_attributes() failed");
}
rc = vc_dispmanx_update_submit(update, 0, NULL);
if (rc != DISPMANX_SUCCESS) {
return SDL_SetError("vc_dispmanx_update_submit() failed");
}
return true;
}
static bool RPI_WarpMouseGlobal(float x, float y)
{
SDL_Mouse *mouse = SDL_GetMouse();
if (!mouse || !mouse->cur_cursor || !mouse->cur_cursor->internal) {
return true;
}
SDL_SendMouseMotion(0, mouse->focus, SDL_GLOBAL_MOUSE_ID, false, x, y);
return RPI_WarpMouseGlobalGraphically(x, y);
}
static bool RPI_WarpMouse(SDL_Window *window, float x, float y)
{
return RPI_WarpMouseGlobal(x, y);
}
void RPI_InitMouse(SDL_VideoDevice *_this)
{
SDL_Mouse *mouse = SDL_GetMouse();
mouse->CreateCursor = RPI_CreateCursor;
mouse->ShowCursor = RPI_ShowCursor;
mouse->MoveCursor = RPI_MoveCursor;
mouse->FreeCursor = RPI_FreeCursor;
mouse->WarpMouse = RPI_WarpMouse;
mouse->WarpMouseGlobal = RPI_WarpMouseGlobal;
SDL_SetDefaultCursor(RPI_CreateDefaultCursor());
}
void RPI_QuitMouse(SDL_VideoDevice *_this)
{
}
static bool RPI_MoveCursor(SDL_Cursor *cursor)
{
SDL_Mouse *mouse = SDL_GetMouse();
return RPI_WarpMouseGlobalGraphically(mouse->x, mouse->y);
}
#endif