2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2009 Sam Lantinga
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 #include "SDL_config.h"
24 /* General cursor handling code for SDL */
26 #include "SDL_mutex.h"
27 #include "SDL_video.h"
28 #include "SDL_mouse.h"
30 #include "SDL_sysvideo.h"
31 #include "SDL_cursor_c.h"
32 #include "SDL_pixels_c.h"
33 #include "default_cursor.h"
34 #include "../events/SDL_sysevents.h"
35 #include "../events/SDL_events_c.h"
37 /* These are static for our cursor handling code */
38 volatile int SDL_cursorstate = CURSOR_VISIBLE;
39 SDL_Cursor *SDL_cursor = NULL;
40 static SDL_Cursor *SDL_defcursor = NULL;
41 SDL_mutex *SDL_cursorlock = NULL;
43 /* Public functions */
44 void SDL_CursorQuit(void)
46 if ( SDL_cursor != NULL ) {
49 SDL_cursorstate &= ~CURSOR_VISIBLE;
50 if ( SDL_cursor != SDL_defcursor ) {
51 SDL_FreeCursor(SDL_cursor);
54 if ( SDL_defcursor != NULL ) {
55 cursor = SDL_defcursor;
57 SDL_FreeCursor(cursor);
60 if ( SDL_cursorlock != NULL ) {
61 SDL_DestroyMutex(SDL_cursorlock);
62 SDL_cursorlock = NULL;
65 int SDL_CursorInit(Uint32 multithreaded)
67 /* We don't have mouse focus, and the cursor isn't drawn yet */
69 SDL_cursorstate = CURSOR_VISIBLE;
72 /* Create the default cursor */
73 if ( SDL_defcursor == NULL ) {
74 SDL_defcursor = SDL_CreateCursor(default_cdata, default_cmask,
75 DEFAULT_CWIDTH, DEFAULT_CHEIGHT,
76 DEFAULT_CHOTX, DEFAULT_CHOTY);
77 SDL_SetCursor(SDL_defcursor);
80 /* Create a lock if necessary */
81 if ( multithreaded ) {
82 SDL_cursorlock = SDL_CreateMutex();
89 /* Multi-thread support for cursors */
90 #ifndef SDL_LockCursor
91 void SDL_LockCursor(void)
93 if ( SDL_cursorlock ) {
94 SDL_mutexP(SDL_cursorlock);
98 #ifndef SDL_UnlockCursor
99 void SDL_UnlockCursor(void)
101 if ( SDL_cursorlock ) {
102 SDL_mutexV(SDL_cursorlock);
107 /* Software cursor drawing support */
108 SDL_Cursor * SDL_CreateCursor (Uint8 *data, Uint8 *mask,
109 int w, int h, int hot_x, int hot_y)
111 SDL_VideoDevice *video = current_video;
116 /* Make sure the width is a multiple of 8 */
119 /* Sanity check the hot spot */
120 if ( (hot_x < 0) || (hot_y < 0) || (hot_x >= w) || (hot_y >= h) ) {
121 SDL_SetError("Cursor hot spot doesn't lie within cursor");
125 /* Allocate memory for the cursor */
126 cursor = (SDL_Cursor *)SDL_malloc(sizeof *cursor);
127 if ( cursor == NULL ) {
136 cursor->hot_x = hot_x;
137 cursor->hot_y = hot_y;
138 cursor->data = (Uint8 *)SDL_malloc((w/8)*h*2);
139 cursor->mask = cursor->data+((w/8)*h);
140 cursor->save[0] = (Uint8 *)SDL_malloc(savelen*2);
141 cursor->save[1] = cursor->save[0] + savelen;
142 cursor->wm_cursor = NULL;
143 if ( ! cursor->data || ! cursor->save[0] ) {
144 SDL_FreeCursor(cursor);
148 for ( i=((w/8)*h)-1; i>=0; --i ) {
149 cursor->data[i] = data[i];
150 cursor->mask[i] = mask[i] | data[i];
152 SDL_memset(cursor->save[0], 0, savelen*2);
154 /* If the window manager gives us a good cursor, we're done! */
155 if ( video->CreateWMCursor ) {
156 cursor->wm_cursor = video->CreateWMCursor(video, data, mask,
159 cursor->wm_cursor = NULL;
164 /* SDL_SetCursor(NULL) can be used to force the cursor redraw,
165 if this is desired for any reason. This is used when setting
166 the video mode and when the SDL window gains the mouse focus.
168 void SDL_SetCursor (SDL_Cursor *cursor)
170 SDL_VideoDevice *video = current_video;
171 SDL_VideoDevice *this = current_video;
173 /* Make sure that the video subsystem has been initialized */
178 /* Prevent the event thread from moving the mouse */
181 /* Set the new cursor */
182 if ( cursor && (cursor != SDL_cursor) ) {
183 /* Erase the current mouse position */
184 if ( SHOULD_DRAWCURSOR(SDL_cursorstate) ) {
185 SDL_EraseCursor(SDL_VideoSurface);
186 } else if ( video->MoveWMCursor ) {
187 /* If the video driver is moving the cursor directly,
188 it needs to hide the old cursor before (possibly)
189 showing the new one. (But don't erase NULL cursor)
191 if ( SDL_cursor && video->ShowWMCursor ) {
192 video->ShowWMCursor(this, NULL);
198 /* Draw the new mouse cursor */
199 if ( SDL_cursor && (SDL_cursorstate&CURSOR_VISIBLE) ) {
200 /* Use window manager cursor if possible */
201 int show_wm_cursor = 0;
202 if ( SDL_cursor->wm_cursor && video->ShowWMCursor ) {
203 show_wm_cursor = video->ShowWMCursor(this, SDL_cursor->wm_cursor);
205 if ( show_wm_cursor ) {
206 SDL_cursorstate &= ~CURSOR_USINGSW;
208 SDL_cursorstate |= CURSOR_USINGSW;
209 if ( video->ShowWMCursor ) {
210 video->ShowWMCursor(this, NULL);
213 SDL_GetMouseState(&x, &y);
214 SDL_cursor->area.x = (x - SDL_cursor->hot_x);
215 SDL_cursor->area.y = (y - SDL_cursor->hot_y);
217 SDL_DrawCursor(SDL_VideoSurface);
220 /* Erase window manager mouse (cursor not visible) */
221 if ( SDL_cursor && (SDL_cursorstate & CURSOR_USINGSW) ) {
222 SDL_EraseCursor(SDL_VideoSurface);
225 if ( video->ShowWMCursor ) {
226 video->ShowWMCursor(this, NULL);
234 SDL_Cursor * SDL_GetCursor (void)
239 void SDL_FreeCursor (SDL_Cursor *cursor)
242 if ( cursor == SDL_cursor ) {
243 SDL_SetCursor(SDL_defcursor);
245 if ( cursor != SDL_defcursor ) {
246 SDL_VideoDevice *video = current_video;
247 SDL_VideoDevice *this = current_video;
249 if ( cursor->data ) {
250 SDL_free(cursor->data);
252 if ( cursor->save[0] ) {
253 SDL_free(cursor->save[0]);
255 if ( video && cursor->wm_cursor ) {
256 if ( video->FreeWMCursor ) {
257 video->FreeWMCursor(this, cursor->wm_cursor);
265 int SDL_ShowCursor (int toggle)
269 showing = (SDL_cursorstate & CURSOR_VISIBLE);
273 SDL_cursorstate |= CURSOR_VISIBLE;
275 SDL_cursorstate &= ~CURSOR_VISIBLE;
278 if ( (SDL_cursorstate & CURSOR_VISIBLE) != showing ) {
279 SDL_VideoDevice *video = current_video;
280 SDL_VideoDevice *this = current_video;
283 if ( video && video->CheckMouseMode ) {
284 video->CheckMouseMode(this);
288 /* Query current state */ ;
290 return(showing ? 1 : 0);
293 void SDL_WarpMouse (Uint16 x, Uint16 y)
295 SDL_VideoDevice *video = current_video;
296 SDL_VideoDevice *this = current_video;
298 if ( !video || !SDL_PublicSurface ) {
299 SDL_SetError("A video mode must be set before warping mouse");
303 /* If we have an offset video mode, offset the mouse coordinates */
304 if (this->screen->pitch == 0) {
305 x += this->screen->offset / this->screen->format->BytesPerPixel;
306 y += this->screen->offset;
308 x += (this->screen->offset % this->screen->pitch) /
309 this->screen->format->BytesPerPixel;
310 y += (this->screen->offset / this->screen->pitch);
313 /* This generates a mouse motion event */
314 if ( video->WarpWMCursor ) {
315 video->WarpWMCursor(this, x, y);
317 SDL_PrivateMouseMotion(0, 0, x, y);
321 void SDL_MoveCursor(int x, int y)
323 SDL_VideoDevice *video = current_video;
325 /* Erase and update the current mouse position */
326 if ( SHOULD_DRAWCURSOR(SDL_cursorstate) ) {
327 /* Erase and redraw mouse cursor in new position */
329 SDL_EraseCursor(SDL_VideoSurface);
330 SDL_cursor->area.x = (x - SDL_cursor->hot_x);
331 SDL_cursor->area.y = (y - SDL_cursor->hot_y);
332 SDL_DrawCursor(SDL_VideoSurface);
334 } else if ( video->MoveWMCursor ) {
335 video->MoveWMCursor(video, x, y);
339 /* Keep track of the current cursor colors */
340 static int palette_changed = 1;
341 static Uint8 pixels8[2];
343 void SDL_CursorPaletteChanged(void)
348 void SDL_MouseRect(SDL_Rect *area)
352 *area = SDL_cursor->area;
361 clip_diff = (area->x+area->w)-SDL_VideoSurface->w;
362 if ( clip_diff > 0 ) {
363 area->w = area->w < clip_diff ? 0 : area->w-clip_diff;
365 clip_diff = (area->y+area->h)-SDL_VideoSurface->h;
366 if ( clip_diff > 0 ) {
367 area->h = area->h < clip_diff ? 0 : area->h-clip_diff;
371 static void SDL_DrawCursorFast(SDL_Surface *screen, SDL_Rect *area)
373 const Uint32 pixels[2] = { 0xFFFFFFFF, 0x00000000 };
378 data = SDL_cursor->data + area->y * SDL_cursor->area.w/8;
379 mask = SDL_cursor->mask + area->y * SDL_cursor->area.w/8;
380 switch (screen->format->BytesPerPixel) {
386 if ( palette_changed ) {
387 pixels8[0] = (Uint8)SDL_MapRGB(screen->format, 255, 255, 255);
388 pixels8[1] = (Uint8)SDL_MapRGB(screen->format, 0, 0, 0);
391 dst = (Uint8 *)screen->pixels +
392 (SDL_cursor->area.y+area->y)*screen->pitch +
394 dstskip = screen->pitch-area->w;
396 for ( h=area->h; h; h-- ) {
397 for ( w=area->w/8; w; w-- ) {
400 for ( i=0; i<8; ++i ) {
401 if ( maskb & 0x80 ) {
402 *dst = pixels8[datab>>7];
418 dst = (Uint16 *)screen->pixels +
419 (SDL_cursor->area.y+area->y)*screen->pitch/2 +
421 dstskip = (screen->pitch/2)-area->w;
423 for ( h=area->h; h; h-- ) {
424 for ( w=area->w/8; w; w-- ) {
427 for ( i=0; i<8; ++i ) {
428 if ( maskb & 0x80 ) {
429 *dst = (Uint16)pixels[datab>>7];
445 dst = (Uint8 *)screen->pixels +
446 (SDL_cursor->area.y+area->y)*screen->pitch +
447 SDL_cursor->area.x*3;
448 dstskip = screen->pitch-area->w*3;
450 for ( h=area->h; h; h-- ) {
451 for ( w=area->w/8; w; w-- ) {
454 for ( i=0; i<8; ++i ) {
455 if ( maskb & 0x80 ) {
456 SDL_memset(dst,pixels[datab>>7],3);
472 dst = (Uint32 *)screen->pixels +
473 (SDL_cursor->area.y+area->y)*screen->pitch/4 +
475 dstskip = (screen->pitch/4)-area->w;
477 for ( h=area->h; h; h-- ) {
478 for ( w=area->w/8; w; w-- ) {
481 for ( i=0; i<8; ++i ) {
482 if ( maskb & 0x80 ) {
483 *dst = pixels[datab>>7];
497 static void SDL_DrawCursorSlow(SDL_Surface *screen, SDL_Rect *area)
499 const Uint32 pixels[2] = { 0xFFFFFF, 0x000000 };
502 Uint8 *data, datab = 0;
503 Uint8 *mask, maskb = 0;
507 data = SDL_cursor->data + area->y * SDL_cursor->area.w/8;
508 mask = SDL_cursor->mask + area->y * SDL_cursor->area.w/8;
509 dstbpp = screen->format->BytesPerPixel;
510 dst = (Uint8 *)screen->pixels +
511 (SDL_cursor->area.y+area->y)*screen->pitch +
512 SDL_cursor->area.x*dstbpp;
513 dstskip = screen->pitch-SDL_cursor->area.w*dstbpp;
516 maxx = area->x+area->w;
517 if ( screen->format->BytesPerPixel == 1 ) {
518 if ( palette_changed ) {
519 pixels8[0] = (Uint8)SDL_MapRGB(screen->format, 255, 255, 255);
520 pixels8[1] = (Uint8)SDL_MapRGB(screen->format, 0, 0, 0);
523 for ( h=area->h; h; h-- ) {
524 for ( x=0; x<SDL_cursor->area.w; ++x ) {
529 if ( (x >= minx) && (x < maxx) ) {
530 if ( maskb & 0x80 ) {
531 SDL_memset(dst, pixels8[datab>>7], dstbpp);
541 for ( h=area->h; h; h-- ) {
542 for ( x=0; x<SDL_cursor->area.w; ++x ) {
547 if ( (x >= minx) && (x < maxx) ) {
548 if ( maskb & 0x80 ) {
549 SDL_memset(dst, pixels[datab>>7], dstbpp);
561 /* This handles the ugly work of converting the saved cursor background from
562 the pixel format of the shadow surface to that of the video surface.
563 This is only necessary when blitting from a shadow surface of a different
564 pixel format than the video surface, and using a software rendered cursor.
566 static void SDL_ConvertCursorSave(SDL_Surface *screen, int w, int h)
571 /* Make sure we can steal the blit mapping */
572 if ( screen->map->dst != SDL_VideoSurface ) {
576 /* Set up the blit information */
577 info.s_pixels = SDL_cursor->save[1];
581 info.d_pixels = SDL_cursor->save[0];
585 info.aux_data = screen->map->sw_data->aux_data;
586 info.src = screen->format;
587 info.table = screen->map->table;
588 info.dst = SDL_VideoSurface->format;
589 RunBlit = screen->map->sw_data->blit;
591 /* Run the actual software blit */
595 void SDL_DrawCursorNoLock(SDL_Surface *screen)
599 /* Get the mouse rectangle, clipped to the screen */
600 SDL_MouseRect(&area);
601 if ( (area.w == 0) || (area.h == 0) ) {
605 /* Copy mouse background */
606 { int w, h, screenbpp;
609 /* Set up the copy pointers */
610 screenbpp = screen->format->BytesPerPixel;
611 if ( (screen == SDL_VideoSurface) ||
612 FORMAT_EQUAL(screen->format, SDL_VideoSurface->format) ) {
613 dst = SDL_cursor->save[0];
615 dst = SDL_cursor->save[1];
617 src = (Uint8 *)screen->pixels + area.y * screen->pitch +
620 /* Perform the copy */
621 w = area.w*screenbpp;
624 SDL_memcpy(dst, src, w);
626 src += screen->pitch;
630 /* Draw the mouse cursor */
631 area.x -= SDL_cursor->area.x;
632 area.y -= SDL_cursor->area.y;
633 if ( (area.x == 0) && (area.w == SDL_cursor->area.w) ) {
634 SDL_DrawCursorFast(screen, &area);
636 SDL_DrawCursorSlow(screen, &area);
640 void SDL_DrawCursor(SDL_Surface *screen)
642 /* Lock the screen if necessary */
643 if ( screen == NULL ) {
646 if ( SDL_MUSTLOCK(screen) ) {
647 if ( SDL_LockSurface(screen) < 0 ) {
652 SDL_DrawCursorNoLock(screen);
654 /* Unlock the screen and update if necessary */
655 if ( SDL_MUSTLOCK(screen) ) {
656 SDL_UnlockSurface(screen);
658 if ( (screen == SDL_VideoSurface) &&
659 ((screen->flags & SDL_HWSURFACE) != SDL_HWSURFACE) ) {
660 SDL_VideoDevice *video = current_video;
661 SDL_VideoDevice *this = current_video;
664 SDL_MouseRect(&area);
666 /* This can be called before a video mode is set */
667 if ( video->UpdateRects ) {
668 video->UpdateRects(this, 1, &area);
673 void SDL_EraseCursorNoLock(SDL_Surface *screen)
677 /* Get the mouse rectangle, clipped to the screen */
678 SDL_MouseRect(&area);
679 if ( (area.w == 0) || (area.h == 0) ) {
683 /* Copy mouse background */
684 { int w, h, screenbpp;
687 /* Set up the copy pointers */
688 screenbpp = screen->format->BytesPerPixel;
689 if ( (screen == SDL_VideoSurface) ||
690 FORMAT_EQUAL(screen->format, SDL_VideoSurface->format) ) {
691 src = SDL_cursor->save[0];
693 src = SDL_cursor->save[1];
695 dst = (Uint8 *)screen->pixels + area.y * screen->pitch +
698 /* Perform the copy */
699 w = area.w*screenbpp;
702 SDL_memcpy(dst, src, w);
704 dst += screen->pitch;
707 /* Perform pixel conversion on cursor background */
708 if ( src > SDL_cursor->save[1] ) {
709 SDL_ConvertCursorSave(screen, area.w, area.h);
714 void SDL_EraseCursor(SDL_Surface *screen)
716 /* Lock the screen if necessary */
717 if ( screen == NULL ) {
720 if ( SDL_MUSTLOCK(screen) ) {
721 if ( SDL_LockSurface(screen) < 0 ) {
726 SDL_EraseCursorNoLock(screen);
728 /* Unlock the screen and update if necessary */
729 if ( SDL_MUSTLOCK(screen) ) {
730 SDL_UnlockSurface(screen);
732 if ( (screen == SDL_VideoSurface) &&
733 ((screen->flags & SDL_HWSURFACE) != SDL_HWSURFACE) ) {
734 SDL_VideoDevice *video = current_video;
735 SDL_VideoDevice *this = current_video;
738 SDL_MouseRect(&area);
739 if ( video->UpdateRects ) {
740 video->UpdateRects(this, 1, &area);
745 /* Reset the cursor on video mode change
746 FIXME: Keep track of all cursors, and reset them all.
748 void SDL_ResetCursor(void)
753 savelen = SDL_cursor->area.w*4*SDL_cursor->area.h;
754 SDL_cursor->area.x = 0;
755 SDL_cursor->area.y = 0;
756 SDL_memset(SDL_cursor->save[0], 0, savelen);