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