src/video/raspberry/SDL_rpimouse.c
author Sam Lantinga
Sat, 02 Jan 2016 10:10:34 -0800
changeset 9998 f67cf37e9cd4
parent 9808 64c8ca7fcf16
child 10101 075d70623646
permissions -rw-r--r--
Updated copyright to 2016
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2016 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_assert.h"
    26 #include "SDL_surface.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 *
    54 RPI_CreateDefaultCursor(void)
    55 {
    56     return SDL_CreateCursor(default_cdata, default_cmask, DEFAULT_CWIDTH, DEFAULT_CHEIGHT, DEFAULT_CHOTX, DEFAULT_CHOTY);
    57 }
    58 
    59 /* Create a cursor from a surface */
    60 static SDL_Cursor *
    61 RPI_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
    62 {
    63     RPI_CursorData *curdata;
    64     SDL_Cursor *cursor;
    65     int ret;
    66     VC_RECT_T dst_rect;
    67     Uint32 dummy;
    68         
    69     SDL_assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888);
    70     SDL_assert(surface->pitch == surface->w * 4);
    71     
    72     cursor = (SDL_Cursor *) SDL_calloc(1, sizeof(*cursor));
    73     curdata = (RPI_CursorData *) SDL_calloc(1, sizeof(*curdata));
    74 
    75     curdata->hot_x = hot_x;
    76     curdata->hot_y = hot_y;
    77     curdata->w = surface->w;
    78     curdata->h = surface->h;
    79     
    80     /* This usage is inspired by Wayland/Weston RPI code, how they figured this out is anyone's guess */
    81     curdata->resource = vc_dispmanx_resource_create( VC_IMAGE_ARGB8888, surface->w | (surface->pitch << 16), surface->h | (surface->h << 16), &dummy );
    82     SDL_assert(curdata->resource);
    83     vc_dispmanx_rect_set( &dst_rect, 0, 0, curdata->w, curdata->h);
    84     /* A note from Weston: 
    85      * vc_dispmanx_resource_write_data() ignores ifmt,
    86      * rect.x, rect.width, and uses stride only for computing
    87      * the size of the transfer as rect.height * stride.
    88      * Therefore we can only write rows starting at x=0.
    89      */
    90     ret = vc_dispmanx_resource_write_data( curdata->resource, VC_IMAGE_ARGB8888, surface->pitch, surface->pixels, &dst_rect );
    91     SDL_assert ( ret == DISPMANX_SUCCESS );
    92     
    93     cursor->driverdata = curdata;
    94     
    95     return cursor;
    96 
    97 }
    98 
    99 /* Show the specified cursor, or hide if cursor is NULL */
   100 static int
   101 RPI_ShowCursor(SDL_Cursor * cursor)
   102 {
   103     int ret;
   104     DISPMANX_UPDATE_HANDLE_T update;
   105     RPI_CursorData *curdata;
   106     VC_RECT_T src_rect, dst_rect;
   107     SDL_Mouse *mouse;
   108     SDL_VideoDisplay *display;
   109     SDL_DisplayData *data;
   110     VC_DISPMANX_ALPHA_T alpha = {  DISPMANX_FLAGS_ALPHA_FROM_SOURCE /* flags */ , 255 /*opacity 0->255*/,  0 /* mask */ };
   111     
   112     mouse = SDL_GetMouse();
   113     if (mouse == NULL) {
   114         return -1;
   115     }
   116     
   117     if (cursor == NULL) {
   118         /* FIXME: We hide the current mouse's cursor, what we actually need is *_HideCursor */
   119 
   120         if ( mouse->cur_cursor != NULL && mouse->cur_cursor->driverdata != NULL) {
   121             curdata = (RPI_CursorData *) mouse->cur_cursor->driverdata;
   122             if (curdata->element > DISPMANX_NO_HANDLE) {
   123                 update = vc_dispmanx_update_start( 10 );
   124                 SDL_assert( update );
   125                 ret = vc_dispmanx_element_remove( update, curdata->element );
   126                 SDL_assert( ret == DISPMANX_SUCCESS );
   127                 ret = vc_dispmanx_update_submit_sync( update );
   128                 SDL_assert( ret == DISPMANX_SUCCESS );
   129                 curdata->element = DISPMANX_NO_HANDLE;
   130             }
   131         }
   132         return 0;
   133     }
   134     
   135     curdata = (RPI_CursorData *) cursor->driverdata;
   136     if (curdata == NULL) {
   137         return -1;
   138     }
   139     
   140     if (mouse->focus == NULL) {
   141         return -1;
   142     }
   143     
   144     display = SDL_GetDisplayForWindow(mouse->focus);
   145     if (display == NULL) {
   146         return -1;
   147     }
   148     
   149     data = (SDL_DisplayData*) display->driverdata;
   150     if (data == NULL) {
   151         return -1;
   152     }
   153     
   154     if (curdata->element == DISPMANX_NO_HANDLE) {
   155         vc_dispmanx_rect_set( &src_rect, 0, 0, curdata->w << 16, curdata->h << 16 );
   156         vc_dispmanx_rect_set( &dst_rect, 0, 0, curdata->w, curdata->h);
   157         
   158         update = vc_dispmanx_update_start( 10 );
   159         SDL_assert( update );
   160 
   161         curdata->element = vc_dispmanx_element_add( update,
   162                                                     data->dispman_display,
   163                                                     SDL_RPI_MOUSELAYER, // layer
   164                                                     &dst_rect,
   165                                                     curdata->resource,
   166                                                     &src_rect,
   167                                                     DISPMANX_PROTECTION_NONE,
   168                                                     &alpha,
   169                                                     DISPMANX_NO_HANDLE, // clamp
   170                                                     VC_IMAGE_ROT0 );
   171         SDL_assert( curdata->element > DISPMANX_NO_HANDLE);
   172         ret = vc_dispmanx_update_submit_sync( update );
   173         SDL_assert( ret == DISPMANX_SUCCESS );
   174     }
   175     
   176     return 0;
   177 }
   178 
   179 /* Free a window manager cursor */
   180 static void
   181 RPI_FreeCursor(SDL_Cursor * cursor)
   182 {
   183     int ret;
   184     DISPMANX_UPDATE_HANDLE_T update;
   185     RPI_CursorData *curdata;
   186     
   187     if (cursor != NULL) {
   188         curdata = (RPI_CursorData *) cursor->driverdata;
   189         
   190         if (curdata != NULL) {
   191             if (curdata->element != DISPMANX_NO_HANDLE) {
   192                 update = vc_dispmanx_update_start( 10 );
   193                 SDL_assert( update );
   194                 ret = vc_dispmanx_element_remove( update, curdata->element );
   195                 SDL_assert( ret == DISPMANX_SUCCESS );
   196                 ret = vc_dispmanx_update_submit_sync( update );
   197                 SDL_assert( ret == DISPMANX_SUCCESS );
   198             }
   199             
   200             if (curdata->resource != DISPMANX_NO_HANDLE) {
   201                 ret = vc_dispmanx_resource_delete( curdata->resource );
   202                 SDL_assert( ret == DISPMANX_SUCCESS );
   203             }
   204         
   205             SDL_free(cursor->driverdata);
   206         }
   207         SDL_free(cursor);
   208     }
   209 }
   210 
   211 /* Warp the mouse to (x,y) */
   212 static void
   213 RPI_WarpMouse(SDL_Window * window, int x, int y)
   214 {
   215     RPI_WarpMouseGlobal(x, y);
   216 }
   217 
   218 /* Warp the mouse to (x,y) */
   219 static int
   220 RPI_WarpMouseGlobal(int x, int y)
   221 {
   222     RPI_CursorData *curdata;
   223     DISPMANX_UPDATE_HANDLE_T update;
   224     VC_RECT_T dst_rect;
   225     SDL_Mouse *mouse = SDL_GetMouse();
   226     
   227     if (mouse != NULL && mouse->cur_cursor != NULL && mouse->cur_cursor->driverdata != NULL) {
   228         curdata = (RPI_CursorData *) mouse->cur_cursor->driverdata;
   229         if (curdata->element != DISPMANX_NO_HANDLE) {
   230             int ret;
   231             update = vc_dispmanx_update_start( 10 );
   232             SDL_assert( update );
   233             vc_dispmanx_rect_set( &dst_rect, x, y, curdata->w, curdata->h);
   234             ret = vc_dispmanx_element_change_attributes(
   235                 update,
   236                 curdata->element,
   237                 ELEMENT_CHANGE_DEST_RECT,
   238                 0,
   239                 0,
   240                 &dst_rect,
   241                 NULL,
   242                 DISPMANX_NO_HANDLE,
   243                 DISPMANX_NO_ROTATE);
   244             SDL_assert( ret == DISPMANX_SUCCESS );
   245             /* Submit asynchronously, otherwise the peformance suffers a lot */
   246             ret = vc_dispmanx_update_submit( update, 0, NULL );
   247             SDL_assert( ret == DISPMANX_SUCCESS );
   248             return (ret == DISPMANX_SUCCESS) ? 0 : -1;
   249         }
   250     }    
   251 
   252     return -1;  /* !!! FIXME: this should SDL_SetError() somewhere. */
   253 }
   254 
   255 void
   256 RPI_InitMouse(_THIS)
   257 {
   258     /* FIXME: Using UDEV it should be possible to scan all mice 
   259      * but there's no point in doing so as there's no multimice support...yet!
   260      */
   261     SDL_Mouse *mouse = SDL_GetMouse();
   262 
   263     mouse->CreateCursor = RPI_CreateCursor;
   264     mouse->ShowCursor = RPI_ShowCursor;
   265     mouse->MoveCursor = RPI_MoveCursor;
   266     mouse->FreeCursor = RPI_FreeCursor;
   267     mouse->WarpMouse = RPI_WarpMouse;
   268     mouse->WarpMouseGlobal = RPI_WarpMouseGlobal;
   269 
   270     SDL_SetDefaultCursor(RPI_CreateDefaultCursor());
   271 }
   272 
   273 void
   274 RPI_QuitMouse(_THIS)
   275 {
   276     
   277 }
   278 
   279 /* This is called when a mouse motion event occurs */
   280 static void
   281 RPI_MoveCursor(SDL_Cursor * cursor)
   282 {
   283     SDL_Mouse *mouse = SDL_GetMouse();
   284     RPI_WarpMouse(mouse->focus, mouse->x, mouse->y);
   285 }
   286 
   287 #endif /* SDL_VIDEO_DRIVER_RPI */
   288 
   289 /* vi: set ts=4 sw=4 expandtab: */