SDL  2.0
SDL_rpimouse.c
Go to the documentation of this file.
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_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  if (cursor == global_cursor) {
230  global_cursor = NULL;
231  }
232  }
233 }
234 
235 /* Warp the mouse to (x,y) */
236 static void
237 RPI_WarpMouse(SDL_Window * window, int x, int y)
238 {
239  RPI_WarpMouseGlobal(x, y);
240 }
241 
242 /* Warp the mouse to (x,y) */
243 static int
244 RPI_WarpMouseGlobal(int x, int y)
245 {
246  RPI_CursorData *curdata;
247  DISPMANX_UPDATE_HANDLE_T update;
248  int ret;
249  VC_RECT_T dst_rect;
250  VC_RECT_T src_rect;
251  SDL_Mouse *mouse = SDL_GetMouse();
252 
253  if (mouse == NULL || mouse->cur_cursor == NULL || mouse->cur_cursor->driverdata == NULL) {
254  return 0;
255  }
256 
257  /* Update internal mouse position. */
258  SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 0, x, y);
259 
260  curdata = (RPI_CursorData *) mouse->cur_cursor->driverdata;
261  if (curdata->element == DISPMANX_NO_HANDLE) {
262  return 0;
263  }
264 
265  update = vc_dispmanx_update_start(0);
266  if (!update) {
267  return 0;
268  }
269 
270  src_rect.x = 0;
271  src_rect.y = 0;
272  src_rect.width = curdata->w << 16;
273  src_rect.height = curdata->h << 16;
274  dst_rect.x = x - curdata->hot_x;
275  dst_rect.y = y - curdata->hot_y;
276  dst_rect.width = curdata->w;
277  dst_rect.height = curdata->h;
278 
279  ret = vc_dispmanx_element_change_attributes(
280  update,
281  curdata->element,
282  0,
283  0,
284  0,
285  &dst_rect,
286  &src_rect,
287  DISPMANX_NO_HANDLE,
288  DISPMANX_NO_ROTATE);
289  if (ret != DISPMANX_SUCCESS) {
290  return SDL_SetError("vc_dispmanx_element_change_attributes() failed");
291  }
292 
293  /* Submit asynchronously, otherwise the peformance suffers a lot */
294  ret = vc_dispmanx_update_submit(update, 0, NULL);
295  if (ret != DISPMANX_SUCCESS) {
296  return SDL_SetError("vc_dispmanx_update_submit() failed");
297  }
298  return 0;
299 }
300 
301 /* Warp the mouse to (x,y) */
302 static int
303 RPI_WarpMouseGlobalGraphicOnly(int x, int y)
304 {
305  RPI_CursorData *curdata;
306  DISPMANX_UPDATE_HANDLE_T update;
307  int ret;
308  VC_RECT_T dst_rect;
309  VC_RECT_T src_rect;
310  SDL_Mouse *mouse = SDL_GetMouse();
311 
312  if (mouse == NULL || mouse->cur_cursor == NULL || mouse->cur_cursor->driverdata == NULL) {
313  return 0;
314  }
315 
316  curdata = (RPI_CursorData *) mouse->cur_cursor->driverdata;
317  if (curdata->element == DISPMANX_NO_HANDLE) {
318  return 0;
319  }
320 
321  update = vc_dispmanx_update_start(0);
322  if (!update) {
323  return 0;
324  }
325 
326  src_rect.x = 0;
327  src_rect.y = 0;
328  src_rect.width = curdata->w << 16;
329  src_rect.height = curdata->h << 16;
330  dst_rect.x = x - curdata->hot_x;
331  dst_rect.y = y - curdata->hot_y;
332  dst_rect.width = curdata->w;
333  dst_rect.height = curdata->h;
334 
335  ret = vc_dispmanx_element_change_attributes(
336  update,
337  curdata->element,
338  0,
339  0,
340  0,
341  &dst_rect,
342  &src_rect,
343  DISPMANX_NO_HANDLE,
344  DISPMANX_NO_ROTATE);
345  if (ret != DISPMANX_SUCCESS) {
346  return SDL_SetError("vc_dispmanx_element_change_attributes() failed");
347  }
348 
349  /* Submit asynchronously, otherwise the peformance suffers a lot */
350  ret = vc_dispmanx_update_submit(update, 0, NULL);
351  if (ret != DISPMANX_SUCCESS) {
352  return SDL_SetError("vc_dispmanx_update_submit() failed");
353  }
354  return 0;
355 }
356 
357 void
359 {
360  /* FIXME: Using UDEV it should be possible to scan all mice
361  * but there's no point in doing so as there's no multimice support...yet!
362  */
363  SDL_Mouse *mouse = SDL_GetMouse();
364 
365  mouse->CreateCursor = RPI_CreateCursor;
366  mouse->ShowCursor = RPI_ShowCursor;
367  mouse->MoveCursor = RPI_MoveCursor;
368  mouse->FreeCursor = RPI_FreeCursor;
369  mouse->WarpMouse = RPI_WarpMouse;
370  mouse->WarpMouseGlobal = RPI_WarpMouseGlobal;
371 
372  SDL_SetDefaultCursor(RPI_CreateDefaultCursor());
373 }
374 
375 void
377 {
378 }
379 
380 /* This is called when a mouse motion event occurs */
381 static void
382 RPI_MoveCursor(SDL_Cursor * cursor)
383 {
384  SDL_Mouse *mouse = SDL_GetMouse();
385  /* We must NOT call SDL_SendMouseMotion() on the next call or we will enter recursivity,
386  * so we create a version of WarpMouseGlobal without it. */
387  RPI_WarpMouseGlobalGraphicOnly(mouse->x, mouse->y);
388 }
389 
390 #endif /* SDL_VIDEO_DRIVER_RPI */
391 
392 /* vi: set ts=4 sw=4 expandtab: */
SDL_GetMouse
SDL_Mouse * SDL_GetMouse(void)
Definition: SDL_mouse.c:170
SDL_Mouse::WarpMouseGlobal
int(* WarpMouseGlobal)(int x, int y)
Definition: SDL_mouse_c.h:64
SDL_Surface
A collection of pixels used in software blitting.
Definition: SDL_surface.h:71
SDL_Cursor
Definition: SDL_mouse_c.h:31
if
set set set set set set set macro pixldst1 abits if abits op else op endif endm macro pixldst2 abits if abits op else op endif endm macro pixldst4 abits if abits op else op endif endm macro pixldst0 abits op endm macro pixldst3 mem_operand op endm macro pixldst30 mem_operand op endm macro pixldst abits if abits elseif abits elseif abits elseif abits elseif abits pixldst0 abits else pixldst0 abits pixldst0 abits pixldst0 abits pixldst0 abits endif elseif abits else pixldst0 abits pixldst0 abits endif elseif abits else error unsupported bpp *numpix else pixst endif endm macro pixld1_s mem_operand if asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl elseif asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl else error unsupported endif endm macro pixld2_s mem_operand if mov asr add asl add asl mov asr sub UNIT_X add asl mov asr add asl add asl mov asr add UNIT_X add asl else pixld1_s mem_operand pixld1_s mem_operand endif endm macro pixld0_s mem_operand if asr adds SRC_WIDTH_FIXED bpl add asl elseif asr adds SRC_WIDTH_FIXED bpl add asl endif endm macro pixld_s_internal mem_operand if mem_operand pixld2_s mem_operand pixdeinterleave basereg elseif mem_operand elseif mem_operand elseif mem_operand elseif mem_operand pixld0_s mem_operand else pixld0_s mem_operand pixld0_s mem_operand pixld0_s mem_operand pixld0_s mem_operand endif elseif mem_operand else pixld0_s mem_operand pixld0_s mem_operand endif elseif mem_operand else error unsupported mem_operand if bpp mem_operand endif endm macro vuzp8 reg2 vuzp d d &reg2 endm macro vzip8 reg2 vzip d d &reg2 endm macro pixdeinterleave basereg basereg basereg basereg basereg endif endm macro pixinterleave basereg basereg basereg basereg basereg endif endm macro PF boost_increment endif if endif PF tst PF addne PF subne PF cmp ORIG_W if endif if endif if endif PF subge ORIG_W PF subges if endif if endif if endif endif endm macro cache_preload_simple endif if dst_r_bpp pld[DST_R, #(PREFETCH_DISTANCE_SIMPLE *dst_r_bpp/8)] endif if mask_bpp pld if[MASK, #(PREFETCH_DISTANCE_SIMPLE *mask_bpp/8)] endif endif endm macro fetch_mask_pixblock pixld mask_basereg pixblock_size MASK endm macro ensure_destination_ptr_alignment process_pixblock_tail_head if beq irp skip1(dst_w_bpp<=(lowbit *8)) &&((lowbit *8)<(pixblock_size *dst_w_bpp)) .if lowbit< 16 tst DST_R
Definition: pixman-arm-neon-asm.h:469
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:1189
SDL_surface.h
SDL_GetDisplayForWindow
SDL_VideoDisplay * SDL_GetDisplayForWindow(SDL_Window *window)
Definition: SDL_video.c:1110
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
Uint32
uint32_t Uint32
Definition: SDL_stdinc.h:203
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:75
alpha
GLfloat GLfloat GLfloat alpha
Definition: SDL_opengl_glext.h:415
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:293
SDL_assert.h
_THIS
#define _THIS
Definition: SDL_alsa_audio.h:31
SDL_Mouse
Definition: SDL_mouse_c.h:44
SDL_PIXELFORMAT_ARGB8888
@ SDL_PIXELFORMAT_ARGB8888
Definition: SDL_pixels.h:251
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:140
SDL_DisplayData
Definition: SDL_cocoamodes.h:27
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:159
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:127
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:1059
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