4ea976b3be21ed2623bea0d4af6e3b8b3ad33f26
[platform/upstream/SDL.git] / src / video / raspberry / SDL_rpimouse.c
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 {
57     return SDL_CreateCursor(default_cdata, default_cmask, DEFAULT_CWIDTH, DEFAULT_CHEIGHT, DEFAULT_CHOTX, DEFAULT_CHOTY);
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;
65     SDL_Cursor *cursor;
66     int ret;
67     VC_RECT_T dst_rect;
68     Uint32 dummy;
69         
70     SDL_assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888);
71     SDL_assert(surface->pitch == surface->w * 4);
72     
73     cursor = (SDL_Cursor *) SDL_calloc(1, sizeof(*cursor));
74     if (cursor == NULL) {
75         SDL_OutOfMemory();
76         return NULL;
77     }
78     curdata = (RPI_CursorData *) SDL_calloc(1, sizeof(*curdata));
79     if (curdata == NULL) {
80         SDL_OutOfMemory();
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;
119     SDL_DisplayData *data;
120     VC_DISPMANX_ALPHA_T alpha = {  DISPMANX_FLAGS_ALPHA_FROM_SOURCE /* flags */ , 255 /*opacity 0->255*/,  0 /* mask */ };
121     uint32_t layer = SDL_RPI_MOUSELAYER;
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
173         env = SDL_GetHint(SDL_HINT_RPI_VIDEO_LAYER);
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
351 RPI_InitMouse(_THIS)
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
369 RPI_QuitMouse(_THIS)
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: */