#include "../../SDL_internal.h"
#if SDL_VIDEO_DRIVER_KMSDRM
#include "SDL_kmsdrmvideo.h"
#include "SDL_kmsdrmmouse.h"
#include "SDL_kmsdrmdyn.h"
#include "../../events/SDL_mouse_c.h"
#include "../../events/default_cursor.h"
#include "../SDL_pixels_c.h"
static SDL_Cursor *KMSDRM_CreateDefaultCursor(void);
static SDL_Cursor *KMSDRM_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y);
static int KMSDRM_ShowCursor(SDL_Cursor * cursor);
static void KMSDRM_MoveCursor(SDL_Cursor * cursor);
static void KMSDRM_FreeCursor(SDL_Cursor * cursor);
static void KMSDRM_WarpMouse(SDL_Window * window, int x, int y);
static int KMSDRM_WarpMouseGlobal(int x, int y);
static SDL_Cursor *
KMSDRM_CreateDefaultCursor(void)
{
return SDL_CreateCursor(default_cdata, default_cmask, DEFAULT_CWIDTH, DEFAULT_CHEIGHT, DEFAULT_CHOTX, DEFAULT_CHOTY);
}
void
KMSDRM_DestroyCursorBO (_THIS, SDL_VideoDisplay *display)
{
SDL_DisplayData *dispdata = (SDL_DisplayData *) display->driverdata;
if (dispdata->cursor_bo) {
KMSDRM_gbm_bo_destroy(dispdata->cursor_bo);
dispdata->cursor_bo = NULL;
dispdata->cursor_bo_drm_fd = -1;
}
}
void
KMSDRM_CreateCursorBO (SDL_VideoDisplay *display) {
SDL_VideoDevice *dev = SDL_GetVideoDevice();
SDL_VideoData *viddata = ((SDL_VideoData *)dev->driverdata);
SDL_DisplayData *dispdata = (SDL_DisplayData *) display->driverdata;
if (!KMSDRM_gbm_device_is_format_supported(viddata->gbm_dev,
GBM_FORMAT_ARGB8888,
GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE))
{
SDL_SetError("Unsupported pixel format for cursor");
return;
}
if (KMSDRM_drmGetCap(viddata->drm_fd,
DRM_CAP_CURSOR_WIDTH, &dispdata->cursor_w) ||
KMSDRM_drmGetCap(viddata->drm_fd, DRM_CAP_CURSOR_HEIGHT,
&dispdata->cursor_h))
{
SDL_SetError("Could not get the recommended GBM cursor size");
return;
}
if (dispdata->cursor_w == 0 || dispdata->cursor_h == 0) {
SDL_SetError("Could not get an usable GBM cursor size");
return;
}
dispdata->cursor_bo = KMSDRM_gbm_bo_create(viddata->gbm_dev,
dispdata->cursor_w, dispdata->cursor_h,
GBM_FORMAT_ARGB8888, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE | GBM_BO_USE_LINEAR);
if (!dispdata->cursor_bo) {
SDL_SetError("Could not create GBM cursor BO");
return;
}
dispdata->cursor_bo_drm_fd = viddata->drm_fd;
}
static int
KMSDRM_RemoveCursorFromBO(SDL_VideoDisplay *display)
{
int ret = 0;
SDL_DisplayData *dispdata = (SDL_DisplayData *) display->driverdata;
SDL_VideoDevice *video_device = SDL_GetVideoDevice();
SDL_VideoData *viddata = ((SDL_VideoData *)video_device->driverdata);
ret = KMSDRM_drmModeSetCursor(viddata->drm_fd,
dispdata->crtc->crtc_id, 0, 0, 0);
if (ret) {
ret = SDL_SetError("Could not hide current cursor with drmModeSetCursor().");
}
return ret;
}
static int
KMSDRM_DumpCursorToBO(SDL_VideoDisplay *display, SDL_Cursor *cursor)
{
SDL_DisplayData *dispdata = (SDL_DisplayData *) display->driverdata;
KMSDRM_CursorData *curdata = (KMSDRM_CursorData *) cursor->driverdata;
SDL_VideoDevice *video_device = SDL_GetVideoDevice();
SDL_VideoData *viddata = ((SDL_VideoData *)video_device->driverdata);
uint32_t bo_handle;
size_t bo_stride;
size_t bufsize;
uint8_t *ready_buffer = NULL;
uint8_t *src_row;
int i;
int ret;
if (!curdata || !dispdata->cursor_bo) {
return SDL_SetError("Cursor or display not initialized properly.");
}
bo_stride = KMSDRM_gbm_bo_get_stride(dispdata->cursor_bo);
bufsize = bo_stride * dispdata->cursor_h;
ready_buffer = (uint8_t*)SDL_calloc(1, bufsize);
if (!ready_buffer) {
ret = SDL_OutOfMemory();
goto cleanup;
}
for (i = 0; i < curdata->h; i++) {
src_row = &((uint8_t*)curdata->buffer)[i * curdata->w * 4];
SDL_memcpy(ready_buffer + (i * bo_stride), src_row, 4 * curdata->w);
}
if (KMSDRM_gbm_bo_write(dispdata->cursor_bo, ready_buffer, bufsize)) {
ret = SDL_SetError("Could not write to GBM cursor BO");
goto cleanup;
}
bo_handle = KMSDRM_gbm_bo_get_handle(dispdata->cursor_bo).u32;
if (curdata->hot_x == 0 && curdata->hot_y == 0) {
ret = KMSDRM_drmModeSetCursor(viddata->drm_fd, dispdata->crtc->crtc_id,
bo_handle, dispdata->cursor_w, dispdata->cursor_h);
} else {
ret = KMSDRM_drmModeSetCursor2(viddata->drm_fd, dispdata->crtc->crtc_id,
bo_handle, dispdata->cursor_w, dispdata->cursor_h, curdata->hot_x, curdata->hot_y);
}
if (ret) {
ret = SDL_SetError("Failed to set DRM cursor.");
goto cleanup;
}
if (ret) {
ret = SDL_SetError("Failed to reset cursor position.");
goto cleanup;
}
cleanup:
if (ready_buffer) {
SDL_free(ready_buffer);
}
return ret;
}
static void
KMSDRM_FreeCursor(SDL_Cursor * cursor)
{
KMSDRM_CursorData *curdata;
if (cursor) {
curdata = (KMSDRM_CursorData *) cursor->driverdata;
if (curdata->buffer) {
SDL_free(curdata->buffer);
curdata->buffer = NULL;
}
if (cursor->driverdata) {
SDL_free(cursor->driverdata);
}
SDL_free(cursor);
}
}
static SDL_Cursor *
KMSDRM_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
{
KMSDRM_CursorData *curdata;
SDL_Cursor *cursor, *ret;
curdata = NULL;
ret = NULL;
cursor = (SDL_Cursor *) SDL_calloc(1, sizeof(*cursor));
if (!cursor) {
SDL_OutOfMemory();
goto cleanup;
}
curdata = (KMSDRM_CursorData *) SDL_calloc(1, sizeof(*curdata));
if (!curdata) {
SDL_OutOfMemory();
goto cleanup;
}
curdata->hot_x = hot_x;
curdata->hot_y = hot_y;
curdata->w = surface->w;
curdata->h = surface->h;
curdata->buffer = NULL;
curdata->buffer_pitch = surface->w;
curdata->buffer_size = surface->w * surface->h * 4;
curdata->buffer = (uint32_t*)SDL_malloc(curdata->buffer_size);
if (!curdata->buffer) {
SDL_OutOfMemory();
goto cleanup;
}
SDL_PremultiplyAlpha(surface->w, surface->h,
surface->format->format, surface->pixels, surface->pitch,
SDL_PIXELFORMAT_ARGB8888, curdata->buffer, surface->w * 4);
cursor->driverdata = curdata;
ret = cursor;
cleanup:
if (ret == NULL) {
if (curdata) {
if (curdata->buffer) {
SDL_free(curdata->buffer);
}
SDL_free(curdata);
}
if (cursor) {
SDL_free(cursor);
}
}
return ret;
}
static int
KMSDRM_ShowCursor(SDL_Cursor * cursor)
{
SDL_VideoDisplay *display;
SDL_Window *window;
SDL_Mouse *mouse;
int num_displays, i;
int ret = 0;
mouse = SDL_GetMouse();
if (!mouse) {
return SDL_SetError("No mouse.");
}
window = mouse->focus;
if (!window || !cursor) {
num_displays = SDL_GetNumVideoDisplays();
for (i = 0; i < num_displays; i++) {
display = SDL_GetDisplay(i);
ret = KMSDRM_RemoveCursorFromBO(display);
}
} else {
display = SDL_GetDisplayForWindow(window);
if (display) {
if (cursor) {
ret = KMSDRM_DumpCursorToBO(display, cursor);
} else {
ret = KMSDRM_RemoveCursorFromBO(display);
}
}
}
return ret;
}
static void
KMSDRM_WarpMouse(SDL_Window * window, int x, int y)
{
KMSDRM_WarpMouseGlobal(x, y);
}
static int
KMSDRM_WarpMouseGlobal(int x, int y)
{
SDL_Mouse *mouse = SDL_GetMouse();
if (mouse && mouse->cur_cursor && mouse->focus) {
SDL_Window *window = mouse->focus;
SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 0, x, y);
if (dispdata->cursor_bo) {
int ret = 0;
ret = KMSDRM_drmModeMoveCursor(dispdata->cursor_bo_drm_fd, dispdata->crtc->crtc_id, x, y);
if (ret) {
SDL_SetError("drmModeMoveCursor() failed.");
}
return ret;
} else {
return SDL_SetError("Cursor not initialized properly.");
}
} else {
return SDL_SetError("No mouse or current cursor.");
}
}
void
KMSDRM_InitMouse(_THIS, SDL_VideoDisplay *display)
{
SDL_Mouse *mouse = SDL_GetMouse();
SDL_DisplayData *dispdata = (SDL_DisplayData *) display->driverdata;
mouse->CreateCursor = KMSDRM_CreateCursor;
mouse->ShowCursor = KMSDRM_ShowCursor;
mouse->MoveCursor = KMSDRM_MoveCursor;
mouse->FreeCursor = KMSDRM_FreeCursor;
mouse->WarpMouse = KMSDRM_WarpMouse;
mouse->WarpMouseGlobal = KMSDRM_WarpMouseGlobal;
if (!dispdata->default_cursor_init) {
SDL_SetDefaultCursor(KMSDRM_CreateDefaultCursor());
dispdata->default_cursor_init = SDL_TRUE;
}
}
void
KMSDRM_QuitMouse(_THIS)
{
}
static void
KMSDRM_MoveCursor(SDL_Cursor * cursor)
{
SDL_Mouse *mouse = SDL_GetMouse();
int ret = 0;
if (mouse && mouse->cur_cursor && mouse->focus) {
SDL_Window *window = mouse->focus;
SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
if (!dispdata->cursor_bo) {
SDL_SetError("Cursor not initialized properly.");
return;
}
ret = KMSDRM_drmModeMoveCursor(dispdata->cursor_bo_drm_fd, dispdata->crtc->crtc_id, mouse->x, mouse->y);
if (ret) {
SDL_SetError("drmModeMoveCursor() failed.");
}
}
}
#endif