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: */