Mercurial > sdl-ios-xcode
diff src/video/SDL_cursor.c @ 0:74212992fb08
Initial revision
author | Sam Lantinga <slouken@lokigames.com> |
---|---|
date | Thu, 26 Apr 2001 16:45:43 +0000 |
parents | |
children | cf2af46e9e2a |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/video/SDL_cursor.c Thu Apr 26 16:45:43 2001 +0000 @@ -0,0 +1,743 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997, 1998, 1999, 2000, 2001 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@devolution.com +*/ + +#ifdef SAVE_RCSID +static char rcsid = + "@(#) $Id$"; +#endif + +/* General cursor handling code for SDL */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "SDL_mutex.h" +#include "SDL_error.h" +#include "SDL_video.h" +#include "SDL_mouse.h" +#include "SDL_blit.h" +#include "SDL_events_c.h" +#include "SDL_sysvideo.h" +#include "SDL_sysevents.h" +#include "SDL_cursor_c.h" +#include "SDL_pixels_c.h" +#include "default_cursor.h" + +/* These are static for our cursor handling code */ +volatile int SDL_cursorstate = CURSOR_VISIBLE; +SDL_Cursor *SDL_cursor = NULL; +static SDL_Cursor *SDL_defcursor = NULL; +SDL_mutex *SDL_cursorlock = NULL; + +/* Public functions */ +void SDL_CursorQuit(void) +{ + if ( SDL_cursor != NULL ) { + SDL_Cursor *cursor; + + SDL_cursorstate &= ~CURSOR_VISIBLE; + if ( SDL_cursor != SDL_defcursor ) { + SDL_FreeCursor(SDL_cursor); + } + SDL_cursor = NULL; + if ( SDL_defcursor != NULL ) { + cursor = SDL_defcursor; + SDL_defcursor = NULL; + SDL_FreeCursor(cursor); + } + } + if ( SDL_cursorlock != NULL ) { + SDL_DestroyMutex(SDL_cursorlock); + SDL_cursorlock = NULL; + } +} +int SDL_CursorInit(Uint32 multithreaded) +{ + /* We don't have mouse focus, and the cursor isn't drawn yet */ + SDL_cursorstate = CURSOR_VISIBLE; + + /* Create the default cursor */ + if ( SDL_defcursor == NULL ) { + SDL_defcursor = SDL_CreateCursor(default_cdata, default_cmask, + DEFAULT_CWIDTH, DEFAULT_CHEIGHT, + DEFAULT_CHOTX, DEFAULT_CHOTY); + SDL_SetCursor(SDL_defcursor); + } + + /* Create a lock if necessary */ + if ( multithreaded ) { + SDL_cursorlock = SDL_CreateMutex(); + } + + /* That's it! */ + return(0); +} + +/* Multi-thread support for cursors */ +#ifndef SDL_LockCursor +void SDL_LockCursor(void) +{ + if ( SDL_cursorlock ) { + SDL_mutexP(SDL_cursorlock); + } +} +#endif +#ifndef SDL_UnlockCursor +void SDL_UnlockCursor(void) +{ + if ( SDL_cursorlock ) { + SDL_mutexV(SDL_cursorlock); + } +} +#endif + +/* Software cursor drawing support */ +SDL_Cursor * SDL_CreateCursor (Uint8 *data, Uint8 *mask, + int w, int h, int hot_x, int hot_y) +{ + SDL_VideoDevice *video = current_video; + int savelen; + int i; + SDL_Cursor *cursor; + + /* Make sure the width is a multiple of 8 */ + w = ((w+7)&~7); + + /* Sanity check the hot spot */ + if ( (hot_x < 0) || (hot_y < 0) || (hot_x >= w) || (hot_y >= h) ) { + SDL_SetError("Cursor hot spot doesn't lie within cursor"); + return(NULL); + } + + /* Allocate memory for the cursor */ + cursor = (SDL_Cursor *)malloc(sizeof *cursor); + if ( cursor == NULL ) { + SDL_OutOfMemory(); + return(NULL); + } + savelen = (w*4)*h; + cursor->area.x = 0; + cursor->area.y = 0; + cursor->area.w = w; + cursor->area.h = h; + cursor->hot_x = hot_x; + cursor->hot_y = hot_y; + cursor->data = (Uint8 *)malloc((w/8)*h*2); + cursor->mask = cursor->data+((w/8)*h); + cursor->save[0] = (Uint8 *)malloc(savelen*2); + cursor->save[1] = cursor->save[0] + savelen; + cursor->wm_cursor = NULL; + if ( ! cursor->data || ! cursor->save[0] ) { + SDL_FreeCursor(cursor); + SDL_OutOfMemory(); + return(NULL); + } + for ( i=((w/8)*h)-1; i>=0; --i ) { + cursor->data[i] = data[i]; + cursor->mask[i] = mask[i] | data[i]; + } + memset(cursor->save[0], 0, savelen*2); + + /* If the window manager gives us a good cursor, we're done! */ + if ( video->CreateWMCursor ) { + cursor->wm_cursor = video->CreateWMCursor(video, data, mask, + w, h, hot_x, hot_y); + } else { + cursor->wm_cursor = NULL; + } + return(cursor); +} + +/* SDL_SetCursor(NULL) can be used to force the cursor redraw, + if this is desired for any reason. This is used when setting + the video mode and when the SDL window gains the mouse focus. + */ +void SDL_SetCursor (SDL_Cursor *cursor) +{ + SDL_VideoDevice *video = current_video; + SDL_VideoDevice *this = current_video; + + /* Make sure that the video subsystem has been initialized */ + if ( ! video ) { + return; + } + + /* Prevent the event thread from moving the mouse */ + SDL_LockCursor(); + + /* Set the new cursor */ + if ( cursor && (cursor != SDL_cursor) ) { + /* Erase the current mouse position */ + if ( SHOULD_DRAWCURSOR(SDL_cursorstate) ) { + SDL_EraseCursor(SDL_VideoSurface); + } else if ( video->MoveWMCursor ) { + /* If the video driver is moving the cursor directly, + it needs to hide the old cursor before (possibly) + showing the new one. (But don't erase NULL cursor) + */ + if ( SDL_cursor ) { + video->ShowWMCursor(this, NULL); + } + } + SDL_cursor = cursor; + } + + /* Draw the new mouse cursor */ + if ( SDL_cursor && (SDL_cursorstate&CURSOR_VISIBLE) ) { + /* Use window manager cursor if possible */ + if ( SDL_cursor->wm_cursor && + video->ShowWMCursor(this, SDL_cursor->wm_cursor) ) + SDL_cursorstate &= ~CURSOR_USINGSW; + else { + SDL_cursorstate |= CURSOR_USINGSW; + if ( video->ShowWMCursor ) { + video->ShowWMCursor(this, NULL); + } + { int x, y; + SDL_GetMouseState(&x, &y); + SDL_cursor->area.x = (x - SDL_cursor->hot_x); + SDL_cursor->area.y = (y - SDL_cursor->hot_y); + } + SDL_DrawCursor(SDL_VideoSurface); + } + } else { + /* Erase window manager mouse (cursor not visible) */ + if ( SDL_cursor && (SDL_cursorstate & CURSOR_USINGSW) ) { + SDL_EraseCursor(SDL_VideoSurface); + } else { + if ( video ) { + video->ShowWMCursor(this, NULL); + } + } + } + SDL_UnlockCursor(); +} + +SDL_Cursor * SDL_GetCursor (void) +{ + return(SDL_cursor); +} + +void SDL_FreeCursor (SDL_Cursor *cursor) +{ + if ( cursor ) { + if ( cursor == SDL_cursor ) { + SDL_SetCursor(SDL_defcursor); + } + if ( cursor != SDL_defcursor ) { + if ( cursor->data ) { + free(cursor->data); + } + if ( cursor->save[0] ) { + free(cursor->save[0]); + } + if ( cursor->wm_cursor ) { + SDL_VideoDevice *video = current_video; + SDL_VideoDevice *this = current_video; + video->FreeWMCursor(this, cursor->wm_cursor); + } + free(cursor); + } + } +} + +int SDL_ShowCursor (int toggle) +{ + int showing; + + showing = (SDL_cursorstate & CURSOR_VISIBLE); + if ( toggle >= 0 ) { + SDL_LockCursor(); + if ( toggle ) { + SDL_cursorstate |= CURSOR_VISIBLE; + } else { + SDL_cursorstate &= ~CURSOR_VISIBLE; + } + SDL_UnlockCursor(); + if ( (SDL_cursorstate & CURSOR_VISIBLE) != showing ) { + SDL_VideoDevice *video = current_video; + SDL_VideoDevice *this = current_video; + + SDL_SetCursor(NULL); + if ( video && video->CheckMouseMode ) { + video->CheckMouseMode(this); + } + } + } else { + /* Query current state */ ; + } + return(showing ? 1 : 0); +} + +void SDL_WarpMouse (Uint16 x, Uint16 y) +{ + SDL_VideoDevice *video = current_video; + SDL_VideoDevice *this = current_video; + + /* This generates a mouse motion event */ + if ( video->WarpWMCursor ) { + video->WarpWMCursor(this, x, y); + } else { + x += (this->screen->offset % this->screen->pitch) / + this->screen->format->BytesPerPixel; + y += (this->screen->offset / this->screen->pitch); + SDL_PrivateMouseMotion(0, 0, x, y); + } +} + +void SDL_MoveCursor(int x, int y) +{ + SDL_VideoDevice *video = current_video; + + /* Erase and update the current mouse position */ + if ( SHOULD_DRAWCURSOR(SDL_cursorstate) ) { + /* Erase and redraw mouse cursor in new position */ + SDL_LockCursor(); + SDL_EraseCursor(SDL_VideoSurface); + SDL_cursor->area.x = (x - SDL_cursor->hot_x); + SDL_cursor->area.y = (y - SDL_cursor->hot_y); + SDL_DrawCursor(SDL_VideoSurface); + SDL_UnlockCursor(); + } else if ( video->MoveWMCursor ) { + video->MoveWMCursor(video, x, y); + } +} + +/* Keep track of the current cursor colors */ +static int palette_changed = 1; +static Uint32 pixels8[2]; + +void SDL_CursorPaletteChanged(void) +{ + palette_changed = 1; +} + +void SDL_MouseRect(SDL_Rect *area) +{ + int clip_diff; + + *area = SDL_cursor->area; + if ( area->x < 0 ) { + area->w += area->x; + area->x = 0; + } + if ( area->y < 0 ) { + area->h += area->y; + area->y = 0; + } + clip_diff = (area->x+area->w)-SDL_VideoSurface->w; + if ( clip_diff > 0 ) { + area->w -= clip_diff; + } + clip_diff = (area->y+area->h)-SDL_VideoSurface->h; + if ( clip_diff > 0 ) { + area->h -= clip_diff; + } +} + +static void SDL_DrawCursorFast(SDL_Surface *screen, SDL_Rect *area) +{ + const Uint32 pixels[2] = { 0xFFFFFFFF, 0x00000000 }; + int i, w, h; + Uint8 *data, datab; + Uint8 *mask, maskb; + + data = SDL_cursor->data + area->y * SDL_cursor->area.w/8; + mask = SDL_cursor->mask + area->y * SDL_cursor->area.w/8; + switch (screen->format->BytesPerPixel) { + + case 1: { + Uint8 *dst; + int dstskip; + + if ( palette_changed ) { + pixels8[0] = SDL_MapRGB(screen->format, 255, 255, 255); + pixels8[1] = SDL_MapRGB(screen->format, 0, 0, 0); + palette_changed = 0; + } + dst = (Uint8 *)screen->pixels + + (SDL_cursor->area.y+area->y)*screen->pitch + + SDL_cursor->area.x; + dstskip = screen->pitch-area->w; + + for ( h=area->h; h; h-- ) { + for ( w=area->w/8; w; w-- ) { + maskb = *mask++; + datab = *data++; + for ( i=0; i<8; ++i ) { + if ( maskb & 0x80 ) { + *dst = pixels8[datab>>7]; + } + maskb <<= 1; + datab <<= 1; + dst++; + } + } + dst += dstskip; + } + } + break; + + case 2: { + Uint16 *dst; + int dstskip; + + dst = (Uint16 *)screen->pixels + + (SDL_cursor->area.y+area->y)*screen->pitch/2 + + SDL_cursor->area.x; + dstskip = (screen->pitch/2)-area->w; + + for ( h=area->h; h; h-- ) { + for ( w=area->w/8; w; w-- ) { + maskb = *mask++; + datab = *data++; + for ( i=0; i<8; ++i ) { + if ( maskb & 0x80 ) { + *dst = pixels[datab>>7]; + } + maskb <<= 1; + datab <<= 1; + dst++; + } + } + dst += dstskip; + } + } + break; + + case 3: { + Uint8 *dst; + int dstskip; + + dst = (Uint8 *)screen->pixels + + (SDL_cursor->area.y+area->y)*screen->pitch + + SDL_cursor->area.x*3; + dstskip = screen->pitch-area->w*3; + + for ( h=area->h; h; h-- ) { + for ( w=area->w/8; w; w-- ) { + maskb = *mask++; + datab = *data++; + for ( i=0; i<8; ++i ) { + if ( maskb & 0x80 ) { + memset(dst,pixels[datab>>7],3); + } + maskb <<= 1; + datab <<= 1; + dst += 3; + } + } + dst += dstskip; + } + } + break; + + case 4: { + Uint32 *dst; + int dstskip; + + dst = (Uint32 *)screen->pixels + + (SDL_cursor->area.y+area->y)*screen->pitch/4 + + SDL_cursor->area.x; + dstskip = (screen->pitch/4)-area->w; + + for ( h=area->h; h; h-- ) { + for ( w=area->w/8; w; w-- ) { + maskb = *mask++; + datab = *data++; + for ( i=0; i<8; ++i ) { + if ( maskb & 0x80 ) { + *dst = pixels[datab>>7]; + } + maskb <<= 1; + datab <<= 1; + dst++; + } + } + dst += dstskip; + } + } + break; + } +} + +static void SDL_DrawCursorSlow(SDL_Surface *screen, SDL_Rect *area) +{ + const Uint32 pixels[2] = { 0xFFFFFF, 0x000000 }; + int h; + int x, minx, maxx; + Uint8 *data, datab = 0; + Uint8 *mask, maskb = 0; + Uint8 *dst; + int dstbpp, dstskip; + + data = SDL_cursor->data + area->y * SDL_cursor->area.w/8; + mask = SDL_cursor->mask + area->y * SDL_cursor->area.w/8; + dstbpp = screen->format->BytesPerPixel; + dst = (Uint8 *)screen->pixels + + (SDL_cursor->area.y+area->y)*screen->pitch + + SDL_cursor->area.x*dstbpp; + dstskip = screen->pitch-SDL_cursor->area.w*dstbpp; + + minx = area->x; + maxx = area->x+area->w; + if ( screen->format->BytesPerPixel == 1 ) { + if ( palette_changed ) { + pixels8[0] = SDL_MapRGB(screen->format, 255, 255, 255); + pixels8[1] = SDL_MapRGB(screen->format, 0, 0, 0); + palette_changed = 0; + } + for ( h=area->h; h; h-- ) { + for ( x=0; x<SDL_cursor->area.w; ++x ) { + if ( (x%8) == 0 ) { + maskb = *mask++; + datab = *data++; + } + if ( (x >= minx) && (x < maxx) ) { + if ( maskb & 0x80 ) { + memset(dst, pixels8[datab>>7], dstbpp); + } + } + maskb <<= 1; + datab <<= 1; + dst += dstbpp; + } + dst += dstskip; + } + } else { + for ( h=area->h; h; h-- ) { + for ( x=0; x<SDL_cursor->area.w; ++x ) { + if ( (x%8) == 0 ) { + maskb = *mask++; + datab = *data++; + } + if ( (x >= minx) && (x < maxx) ) { + if ( maskb & 0x80 ) { + memset(dst, pixels[datab>>7], dstbpp); + } + } + maskb <<= 1; + datab <<= 1; + dst += dstbpp; + } + dst += dstskip; + } + } +} + +/* This handles the ugly work of converting the saved cursor background from + the pixel format of the shadow surface to that of the video surface. + This is only necessary when blitting from a shadow surface of a different + pixel format than the video surface, and using a software rendered cursor. +*/ +static void SDL_ConvertCursorSave(SDL_Surface *screen, int w, int h) +{ + SDL_BlitInfo info; + SDL_loblit RunBlit; + + /* Make sure we can steal the blit mapping */ + if ( screen->map->dst != SDL_VideoSurface ) { + return; + } + + /* Set up the blit information */ + info.s_pixels = SDL_cursor->save[1]; + info.s_width = w; + info.s_height = h; + info.s_skip = 0; + info.d_pixels = SDL_cursor->save[0]; + info.d_width = w; + info.d_height = h; + info.d_skip = 0; + info.aux_data = screen->map->sw_data->aux_data; + info.src = screen->format; + info.table = screen->map->table; + info.dst = SDL_VideoSurface->format; + RunBlit = screen->map->sw_data->blit; + + /* Run the actual software blit */ + RunBlit(&info); +} + +void SDL_DrawCursorNoLock(SDL_Surface *screen) +{ + SDL_Rect area; + + /* Get the mouse rectangle, clipped to the screen */ + SDL_MouseRect(&area); + if ( (area.w == 0) || (area.h == 0) ) { + return; + } + + /* Copy mouse background */ + { int w, h, screenbpp; + Uint8 *src, *dst; + + /* Set up the copy pointers */ + screenbpp = screen->format->BytesPerPixel; + if ( (screen == SDL_VideoSurface) || + FORMAT_EQUAL(screen->format, SDL_VideoSurface->format) ) { + dst = SDL_cursor->save[0]; + } else { + dst = SDL_cursor->save[1]; + } + src = (Uint8 *)screen->pixels + area.y * screen->pitch + + area.x * screenbpp; + + /* Perform the copy */ + w = area.w*screenbpp; + h = area.h; + while ( h-- ) { + memcpy(dst, src, w); + dst += w; + src += screen->pitch; + } + } + + /* Draw the mouse cursor */ + area.x -= SDL_cursor->area.x; + area.y -= SDL_cursor->area.y; + if ( (area.x == 0) && (area.w == SDL_cursor->area.w) ) { + SDL_DrawCursorFast(screen, &area); + } else { + SDL_DrawCursorSlow(screen, &area); + } +} + +void SDL_DrawCursor(SDL_Surface *screen) +{ + /* Lock the screen if necessary */ + if ( screen == NULL ) { + return; + } + if ( SDL_MUSTLOCK(screen) ) { + if ( SDL_LockSurface(screen) < 0 ) { + return; + } + } + + SDL_DrawCursorNoLock(screen); + + /* Unlock the screen and update if necessary */ + if ( SDL_MUSTLOCK(screen) ) { + SDL_UnlockSurface(screen); + } + if ( (screen == SDL_VideoSurface) && + ((screen->flags & SDL_HWSURFACE) != SDL_HWSURFACE) ) { + SDL_VideoDevice *video = current_video; + SDL_VideoDevice *this = current_video; + SDL_Rect area; + + SDL_MouseRect(&area); + + /* This can be called before a video mode is set */ + if ( video->UpdateRects ) { + video->UpdateRects(this, 1, &area); + } + } +} + +void SDL_EraseCursorNoLock(SDL_Surface *screen) +{ + SDL_Rect area; + + /* Get the mouse rectangle, clipped to the screen */ + SDL_MouseRect(&area); + if ( (area.w == 0) || (area.h == 0) ) { + return; + } + + /* Copy mouse background */ + { int w, h, screenbpp; + Uint8 *src, *dst; + + /* Set up the copy pointers */ + screenbpp = screen->format->BytesPerPixel; + if ( (screen == SDL_VideoSurface) || + FORMAT_EQUAL(screen->format, SDL_VideoSurface->format) ) { + src = SDL_cursor->save[0]; + } else { + src = SDL_cursor->save[1]; + } + dst = (Uint8 *)screen->pixels + area.y * screen->pitch + + area.x * screenbpp; + + /* Perform the copy */ + w = area.w*screenbpp; + h = area.h; + while ( h-- ) { + memcpy(dst, src, w); + src += w; + dst += screen->pitch; + } + + /* Perform pixel conversion on cursor background */ + if ( src > SDL_cursor->save[1] ) { + SDL_ConvertCursorSave(screen, area.w, area.h); + } + } +} + +void SDL_EraseCursor(SDL_Surface *screen) +{ + /* Lock the screen if necessary */ + if ( screen == NULL ) { + return; + } + if ( SDL_MUSTLOCK(screen) ) { + if ( SDL_LockSurface(screen) < 0 ) { + return; + } + } + + SDL_EraseCursorNoLock(screen); + + /* Unlock the screen and update if necessary */ + if ( SDL_MUSTLOCK(screen) ) { + SDL_UnlockSurface(screen); + } + if ( (screen == SDL_VideoSurface) && + ((screen->flags & SDL_HWSURFACE) != SDL_HWSURFACE) ) { + SDL_VideoDevice *video = current_video; + SDL_VideoDevice *this = current_video; + SDL_Rect area; + + SDL_MouseRect(&area); + video->UpdateRects(this, 1, &area); + } +} + +/* Reset the cursor on video mode change + FIXME: Keep track of all cursors, and reset them all. + */ +void SDL_ResetCursor(void) +{ + int savelen; + + if ( SDL_cursor ) { + savelen = SDL_cursor->area.w*4*SDL_cursor->area.h; + SDL_cursor->area.x = 0; + SDL_cursor->area.y = 0; + memset(SDL_cursor->save[0], 0, savelen); + } +}