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_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 {
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;
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) {
77  return NULL;
78  }
79  curdata = (RPI_CursorData *) SDL_calloc(1, sizeof(*curdata));
80  if (curdata == NULL) {
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;
121  VC_DISPMANX_ALPHA_T alpha = { DISPMANX_FLAGS_ALPHA_FROM_SOURCE /* flags */ , 255 /*opacity 0->255*/, 0 /* mask */ };
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 
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 
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
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
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: */
#define _THIS
#define SDL_assert(condition)
Definition: SDL_assert.h:171
unsigned int uint32_t
#define SDL_SetError
#define SDL_CreateCursor
#define SDL_free
#define SDL_atoi
#define SDL_calloc
#define SDL_GetHint
#define SDL_OutOfMemory()
Definition: SDL_error.h:88
#define SDL_HINT_RPI_VIDEO_LAYER
Tell SDL which Dispmanx layer to use on a Raspberry PI.
Definition: SDL_hints.h:1190
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
SDL_Mouse * SDL_GetMouse(void)
Definition: SDL_mouse.c:175
void SDL_SetDefaultCursor(SDL_Cursor *cursor)
Definition: SDL_mouse.c:164
int SDL_SendMouseMotion(SDL_Window *window, SDL_MouseID mouseID, int relative, int x, int y)
Definition: SDL_mouse.c:298
GLint GLint GLint GLint GLint GLint y
Definition: SDL_opengl.h:1574
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
GLint GLint GLint GLint GLint x
Definition: SDL_opengl.h:1574
GLfloat GLfloat GLfloat alpha
GLenum GLuint GLint GLint layer
@ SDL_PIXELFORMAT_ARGB8888
Definition: SDL_pixels.h:257
void RPI_QuitMouse(_THIS)
void RPI_InitMouse(_THIS)
#define SDL_RPI_MOUSELAYER
Definition: SDL_rpivideo.h:60
uint32_t Uint32
Definition: SDL_stdinc.h:209
SDL_VideoDisplay * SDL_GetDisplayForWindow(SDL_Window *window)
Definition: SDL_video.c:1130
#define NULL
Definition: begin_code.h:163
#define DEFAULT_CHOTY
static const unsigned char default_cdata[]
#define DEFAULT_CHEIGHT
#define DEFAULT_CHOTX
#define DEFAULT_CWIDTH
static const unsigned char default_cmask[]
EGLSurface surface
Definition: eglext.h:248
EGLSurface EGLNativeWindowType * window
Definition: eglext.h:1025
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
void * driverdata
Definition: SDL_mouse_c.h:33
SDL_MouseID mouseID
Definition: SDL_mouse_c.h:76
SDL_Cursor * cur_cursor
Definition: SDL_mouse_c.h:105
void(* WarpMouse)(SDL_Window *window, int x, int y)
Definition: SDL_mouse_c.h:61
SDL_Cursor *(* CreateCursor)(SDL_Surface *surface, int hot_x, int hot_y)
Definition: SDL_mouse_c.h:46
void(* FreeCursor)(SDL_Cursor *cursor)
Definition: SDL_mouse_c.h:58
int(* ShowCursor)(SDL_Cursor *cursor)
Definition: SDL_mouse_c.h:52
SDL_Window * focus
Definition: SDL_mouse_c.h:77
int(* WarpMouseGlobal)(int x, int y)
Definition: SDL_mouse_c.h:64
void(* MoveCursor)(SDL_Cursor *cursor)
Definition: SDL_mouse_c.h:55
A collection of pixels used in software blitting.
Definition: SDL_surface.h:71
The type used to identify a window.
Definition: SDL_sysvideo.h:75
SDL_Cursor * cursor
Definition: testwm2.c:40