sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_rpimouse.c (12111B)


      1 /*
      2   Simple DirectMedia Layer
      3   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
      4 
      5   This software is provided 'as-is', without any express or implied
      6   warranty.  In no event will the authors be held liable for any damages
      7   arising from the use of this software.
      8 
      9   Permission is granted to anyone to use this software for any purpose,
     10   including commercial applications, and to alter it and redistribute it
     11   freely, subject to the following restrictions:
     12 
     13   1. The origin of this software must not be misrepresented; you must not
     14      claim that you wrote the original software. If you use this software
     15      in a product, an acknowledgment in the product documentation would be
     16      appreciated but is not required.
     17   2. Altered source versions must be plainly marked as such, and must not be
     18      misrepresented as being the original software.
     19   3. This notice may not be removed or altered from any source distribution.
     20 */
     21 #include "../../SDL_internal.h"
     22 
     23 #if SDL_VIDEO_DRIVER_RPI
     24 
     25 #include "SDL_surface.h"
     26 #include "SDL_hints.h"
     27 
     28 #include "SDL_rpivideo.h"
     29 #include "SDL_rpimouse.h"
     30 
     31 #include "../SDL_sysvideo.h"
     32 #include "../../events/SDL_mouse_c.h"
     33 #include "../../events/default_cursor.h"
     34 
     35 /* Copied from vc_vchi_dispmanx.h which is bugged and tries to include a non existing file */
     36 /* Attributes changes flag mask */
     37 #define ELEMENT_CHANGE_LAYER          (1<<0)
     38 #define ELEMENT_CHANGE_OPACITY        (1<<1)
     39 #define ELEMENT_CHANGE_DEST_RECT      (1<<2)
     40 #define ELEMENT_CHANGE_SRC_RECT       (1<<3)
     41 #define ELEMENT_CHANGE_MASK_RESOURCE  (1<<4)
     42 #define ELEMENT_CHANGE_TRANSFORM      (1<<5)
     43 /* End copied from vc_vchi_dispmanx.h */
     44 
     45 static SDL_Cursor *RPI_CreateDefaultCursor(void);
     46 static SDL_Cursor *RPI_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y);
     47 static int RPI_ShowCursor(SDL_Cursor * cursor);
     48 static void RPI_MoveCursor(SDL_Cursor * cursor);
     49 static void RPI_FreeCursor(SDL_Cursor * cursor);
     50 static void RPI_WarpMouse(SDL_Window * window, int x, int y);
     51 static int RPI_WarpMouseGlobal(int x, int y);
     52 
     53 static SDL_Cursor *global_cursor;
     54 
     55 static SDL_Cursor *
     56 RPI_CreateDefaultCursor(void)
     57 {
     58     return SDL_CreateCursor(default_cdata, default_cmask, DEFAULT_CWIDTH, DEFAULT_CHEIGHT, DEFAULT_CHOTX, DEFAULT_CHOTY);
     59 }
     60 
     61 /* Create a cursor from a surface */
     62 static SDL_Cursor *
     63 RPI_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
     64 {
     65     RPI_CursorData *curdata;
     66     SDL_Cursor *cursor;
     67     int ret;
     68     VC_RECT_T dst_rect;
     69     Uint32 dummy;
     70         
     71     SDL_assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888);
     72     SDL_assert(surface->pitch == surface->w * 4);
     73     
     74     cursor = (SDL_Cursor *) SDL_calloc(1, sizeof(*cursor));
     75     if (cursor == NULL) {
     76         SDL_OutOfMemory();
     77         return NULL;
     78     }
     79     curdata = (RPI_CursorData *) SDL_calloc(1, sizeof(*curdata));
     80     if (curdata == NULL) {
     81         SDL_OutOfMemory();
     82         SDL_free(cursor);
     83         return NULL;
     84     }
     85 
     86     curdata->hot_x = hot_x;
     87     curdata->hot_y = hot_y;
     88     curdata->w = surface->w;
     89     curdata->h = surface->h;
     90     
     91     /* This usage is inspired by Wayland/Weston RPI code, how they figured this out is anyone's guess */
     92     curdata->resource = vc_dispmanx_resource_create(VC_IMAGE_ARGB8888, surface->w | (surface->pitch << 16), surface->h | (surface->h << 16), &dummy);
     93     SDL_assert(curdata->resource);
     94     vc_dispmanx_rect_set(&dst_rect, 0, 0, curdata->w, curdata->h);
     95     /* A note from Weston: 
     96      * vc_dispmanx_resource_write_data() ignores ifmt,
     97      * rect.x, rect.width, and uses stride only for computing
     98      * the size of the transfer as rect.height * stride.
     99      * Therefore we can only write rows starting at x=0.
    100      */
    101     ret = vc_dispmanx_resource_write_data(curdata->resource, VC_IMAGE_ARGB8888, surface->pitch, surface->pixels, &dst_rect);
    102     SDL_assert (ret == DISPMANX_SUCCESS);
    103     
    104     cursor->driverdata = curdata;
    105     
    106     return cursor;
    107 
    108 }
    109 
    110 /* Show the specified cursor, or hide if cursor is NULL */
    111 static int
    112 RPI_ShowCursor(SDL_Cursor * cursor)
    113 {
    114     int ret;
    115     DISPMANX_UPDATE_HANDLE_T update;
    116     RPI_CursorData *curdata;
    117     VC_RECT_T src_rect, dst_rect;
    118     SDL_Mouse *mouse;
    119     SDL_VideoDisplay *display;
    120     SDL_DisplayData *data;
    121     VC_DISPMANX_ALPHA_T alpha = {  DISPMANX_FLAGS_ALPHA_FROM_SOURCE /* flags */ , 255 /*opacity 0->255*/,  0 /* mask */ };
    122     uint32_t layer = SDL_RPI_MOUSELAYER;
    123     const char *env;
    124 
    125     mouse = SDL_GetMouse();
    126     if (mouse == NULL) {
    127         return -1;
    128     }
    129     
    130     if (cursor != global_cursor) {
    131         if (global_cursor != NULL) {
    132             curdata = (RPI_CursorData *) global_cursor->driverdata;
    133             if (curdata && curdata->element > DISPMANX_NO_HANDLE) {
    134                 update = vc_dispmanx_update_start(0);
    135                 SDL_assert(update);
    136                 ret = vc_dispmanx_element_remove(update, curdata->element);
    137                 SDL_assert(ret == DISPMANX_SUCCESS);
    138                 ret = vc_dispmanx_update_submit_sync(update);
    139                 SDL_assert(ret == DISPMANX_SUCCESS);
    140                 curdata->element = DISPMANX_NO_HANDLE;
    141             }
    142         }
    143         global_cursor = cursor;
    144     }
    145 
    146     if (cursor == NULL) {
    147         return 0;
    148     }
    149     
    150     curdata = (RPI_CursorData *) cursor->driverdata;
    151     if (curdata == NULL) {
    152         return -1;
    153     }
    154     
    155     if (mouse->focus == NULL) {
    156         return -1;
    157     }
    158 
    159     display = SDL_GetDisplayForWindow(mouse->focus);
    160     if (display == NULL) {
    161         return -1;
    162     }
    163     
    164     data = (SDL_DisplayData*) display->driverdata;
    165     if (data == NULL) {
    166         return -1;
    167     }
    168     
    169     if (curdata->element == DISPMANX_NO_HANDLE) {
    170         vc_dispmanx_rect_set(&src_rect, 0, 0, curdata->w << 16, curdata->h << 16);
    171         vc_dispmanx_rect_set(&dst_rect, mouse->x - curdata->hot_x, mouse->y - curdata->hot_y, curdata->w, curdata->h);
    172         
    173         update = vc_dispmanx_update_start(0);
    174         SDL_assert(update);
    175 
    176         env = SDL_GetHint(SDL_HINT_RPI_VIDEO_LAYER);
    177         if (env) {
    178             layer = SDL_atoi(env) + 1;
    179         }
    180 
    181         curdata->element = vc_dispmanx_element_add(update,
    182                                                     data->dispman_display,
    183                                                     layer,
    184                                                     &dst_rect,
    185                                                     curdata->resource,
    186                                                     &src_rect,
    187                                                     DISPMANX_PROTECTION_NONE,
    188                                                     &alpha,
    189                                                     DISPMANX_NO_HANDLE, // clamp
    190                                                     DISPMANX_NO_ROTATE);
    191         SDL_assert(curdata->element > DISPMANX_NO_HANDLE);
    192         ret = vc_dispmanx_update_submit_sync(update);
    193         SDL_assert(ret == DISPMANX_SUCCESS);
    194     }
    195     
    196     return 0;
    197 }
    198 
    199 /* Free a window manager cursor */
    200 static void
    201 RPI_FreeCursor(SDL_Cursor * cursor)
    202 {
    203     int ret;
    204     DISPMANX_UPDATE_HANDLE_T update;
    205     RPI_CursorData *curdata;
    206     
    207     if (cursor != NULL) {
    208         curdata = (RPI_CursorData *) cursor->driverdata;
    209         
    210         if (curdata != NULL) {
    211             if (curdata->element != DISPMANX_NO_HANDLE) {
    212                 update = vc_dispmanx_update_start(0);
    213                 SDL_assert(update);
    214                 ret = vc_dispmanx_element_remove(update, curdata->element);
    215                 SDL_assert(ret == DISPMANX_SUCCESS);
    216                 ret = vc_dispmanx_update_submit_sync(update);
    217                 SDL_assert(ret == DISPMANX_SUCCESS);
    218             }
    219             
    220             if (curdata->resource != DISPMANX_NO_HANDLE) {
    221                 ret = vc_dispmanx_resource_delete(curdata->resource);
    222                 SDL_assert(ret == DISPMANX_SUCCESS);
    223             }
    224         
    225             SDL_free(cursor->driverdata);
    226         }
    227         SDL_free(cursor);
    228         if (cursor == global_cursor) {
    229             global_cursor = NULL;
    230         }
    231     }
    232 }
    233 
    234 /* Warp the mouse to (x,y) */
    235 static void
    236 RPI_WarpMouse(SDL_Window * window, int x, int y)
    237 {
    238     RPI_WarpMouseGlobal(x, y);
    239 }
    240 
    241 /* Warp the mouse to (x,y) */
    242 static int
    243 RPI_WarpMouseGlobal(int x, int y)
    244 {
    245     RPI_CursorData *curdata;
    246     DISPMANX_UPDATE_HANDLE_T update;
    247     int ret;
    248     VC_RECT_T dst_rect;
    249     VC_RECT_T src_rect;
    250     SDL_Mouse *mouse = SDL_GetMouse();
    251     
    252     if (mouse == NULL || mouse->cur_cursor == NULL || mouse->cur_cursor->driverdata == NULL) {
    253         return 0;
    254     }
    255 
    256     /* Update internal mouse position. */
    257     SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 0, x, y);
    258 
    259     curdata = (RPI_CursorData *) mouse->cur_cursor->driverdata;
    260     if (curdata->element == DISPMANX_NO_HANDLE) {
    261         return 0;
    262     }
    263 
    264     update = vc_dispmanx_update_start(0);
    265     if (!update) {
    266         return 0;
    267     }
    268 
    269     src_rect.x = 0;
    270     src_rect.y = 0;
    271     src_rect.width  = curdata->w << 16;
    272     src_rect.height = curdata->h << 16;
    273     dst_rect.x = x - curdata->hot_x;
    274     dst_rect.y = y - curdata->hot_y;
    275     dst_rect.width  = curdata->w;
    276     dst_rect.height = curdata->h;
    277 
    278     ret = vc_dispmanx_element_change_attributes(
    279         update,
    280         curdata->element,
    281         0,
    282         0,
    283         0,
    284         &dst_rect,
    285         &src_rect,
    286         DISPMANX_NO_HANDLE,
    287         DISPMANX_NO_ROTATE);
    288     if (ret != DISPMANX_SUCCESS) {
    289         return SDL_SetError("vc_dispmanx_element_change_attributes() failed");
    290     }
    291 
    292     /* Submit asynchronously, otherwise the peformance suffers a lot */
    293     ret = vc_dispmanx_update_submit(update, 0, NULL);
    294     if (ret != DISPMANX_SUCCESS) {
    295         return SDL_SetError("vc_dispmanx_update_submit() failed");
    296     }
    297     return 0;
    298 }
    299 
    300 /* Warp the mouse to (x,y) */
    301 static int
    302 RPI_WarpMouseGlobalGraphicOnly(int x, int y)
    303 {
    304     RPI_CursorData *curdata;
    305     DISPMANX_UPDATE_HANDLE_T update;
    306     int ret;
    307     VC_RECT_T dst_rect;
    308     VC_RECT_T src_rect;
    309     SDL_Mouse *mouse = SDL_GetMouse();
    310     
    311     if (mouse == NULL || mouse->cur_cursor == NULL || mouse->cur_cursor->driverdata == NULL) {
    312         return 0;
    313     }
    314 
    315     curdata = (RPI_CursorData *) mouse->cur_cursor->driverdata;
    316     if (curdata->element == DISPMANX_NO_HANDLE) {
    317         return 0;
    318     }
    319 
    320     update = vc_dispmanx_update_start(0);
    321     if (!update) {
    322         return 0;
    323     }
    324 
    325     src_rect.x = 0;
    326     src_rect.y = 0;
    327     src_rect.width  = curdata->w << 16;
    328     src_rect.height = curdata->h << 16;
    329     dst_rect.x = x - curdata->hot_x;
    330     dst_rect.y = y - curdata->hot_y;
    331     dst_rect.width  = curdata->w;
    332     dst_rect.height = curdata->h;
    333 
    334     ret = vc_dispmanx_element_change_attributes(
    335         update,
    336         curdata->element,
    337         0,
    338         0,
    339         0,
    340         &dst_rect,
    341         &src_rect,
    342         DISPMANX_NO_HANDLE,
    343         DISPMANX_NO_ROTATE);
    344     if (ret != DISPMANX_SUCCESS) {
    345         return SDL_SetError("vc_dispmanx_element_change_attributes() failed");
    346     }
    347 
    348     /* Submit asynchronously, otherwise the peformance suffers a lot */
    349     ret = vc_dispmanx_update_submit(update, 0, NULL);
    350     if (ret != DISPMANX_SUCCESS) {
    351         return SDL_SetError("vc_dispmanx_update_submit() failed");
    352     }
    353     return 0;
    354 }
    355 
    356 void
    357 RPI_InitMouse(_THIS)
    358 {
    359     /* FIXME: Using UDEV it should be possible to scan all mice 
    360      * but there's no point in doing so as there's no multimice support...yet!
    361      */
    362     SDL_Mouse *mouse = SDL_GetMouse();
    363 
    364     mouse->CreateCursor = RPI_CreateCursor;
    365     mouse->ShowCursor = RPI_ShowCursor;
    366     mouse->MoveCursor = RPI_MoveCursor;
    367     mouse->FreeCursor = RPI_FreeCursor;
    368     mouse->WarpMouse = RPI_WarpMouse;
    369     mouse->WarpMouseGlobal = RPI_WarpMouseGlobal;
    370 
    371     SDL_SetDefaultCursor(RPI_CreateDefaultCursor());
    372 }
    373 
    374 void
    375 RPI_QuitMouse(_THIS)
    376 {
    377 }
    378 
    379 /* This is called when a mouse motion event occurs */
    380 static void
    381 RPI_MoveCursor(SDL_Cursor * cursor)
    382 {
    383     SDL_Mouse *mouse = SDL_GetMouse();
    384     /* We must NOT call SDL_SendMouseMotion() on the next call or we will enter recursivity, 
    385      * so we create a version of WarpMouseGlobal without it. */
    386     RPI_WarpMouseGlobalGraphicOnly(mouse->x, mouse->y);
    387 }
    388 
    389 #endif /* SDL_VIDEO_DRIVER_RPI */
    390 
    391 /* vi: set ts=4 sw=4 expandtab: */