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