From a855d6202536ff28e5aae4f22a0f31d8f5b325d0 Mon Sep 17 00:00:00 2001 From: Franklin Wei Date: Sat, 21 Jan 2017 15:18:31 -0500 Subject: Port of Duke Nukem 3D This ports Fabien Sanglard's Chocolate Duke to run on a version of SDL for Rockbox. Change-Id: I8f2c4c78af19de10c1633ed7bb7a997b43256dd9 --- apps/plugins/sdl/src/video/SDL_cursor.c | 758 ++++++++++++++++++++++++++++++++ 1 file changed, 758 insertions(+) create mode 100644 apps/plugins/sdl/src/video/SDL_cursor.c (limited to 'apps/plugins/sdl/src/video/SDL_cursor.c') diff --git a/apps/plugins/sdl/src/video/SDL_cursor.c b/apps/plugins/sdl/src/video/SDL_cursor.c new file mode 100644 index 0000000000..87a9090e4a --- /dev/null +++ b/apps/plugins/sdl/src/video/SDL_cursor.c @@ -0,0 +1,758 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* General cursor handling code for SDL */ + +#include "SDL_mutex.h" +#include "SDL_video.h" +#include "SDL_mouse.h" +#include "SDL_blit.h" +#include "SDL_sysvideo.h" +#include "SDL_cursor_c.h" +#include "SDL_pixels_c.h" +#include "default_cursor.h" +#include "../events/SDL_sysevents.h" +#include "../events/SDL_events_c.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 */ +#ifndef IPOD + SDL_cursorstate = CURSOR_VISIBLE; +#endif + + /* 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 *)SDL_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 *)SDL_malloc((w/8)*h*2); + cursor->mask = cursor->data+((w/8)*h); + cursor->save[0] = (Uint8 *)SDL_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]; + } + SDL_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 ) { + 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 */ + int show_wm_cursor = 0; + if ( SDL_cursor->wm_cursor && video->ShowWMCursor ) { + show_wm_cursor = video->ShowWMCursor(this, SDL_cursor->wm_cursor); + } + if ( show_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 ) { + if ( video->ShowWMCursor ) { + 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 ) { + SDL_VideoDevice *video = current_video; + SDL_VideoDevice *this = current_video; + + if ( cursor->data ) { + SDL_free(cursor->data); + } + if ( cursor->save[0] ) { + SDL_free(cursor->save[0]); + } + if ( video && cursor->wm_cursor ) { + if ( video->FreeWMCursor ) { + video->FreeWMCursor(this, cursor->wm_cursor); + } + } + SDL_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; + + if ( !video || !SDL_PublicSurface ) { + SDL_SetError("A video mode must be set before warping mouse"); + return; + } + + /* If we have an offset video mode, offset the mouse coordinates */ + if (this->screen->pitch == 0) { + x += this->screen->offset / this->screen->format->BytesPerPixel; + y += this->screen->offset; + } else { + x += (this->screen->offset % this->screen->pitch) / + this->screen->format->BytesPerPixel; + y += (this->screen->offset / this->screen->pitch); + } + + /* This generates a mouse motion event */ + if ( video->WarpWMCursor ) { + video->WarpWMCursor(this, x, y); + } else { + 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 Uint8 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 = area->w < clip_diff ? 0 : area->w-clip_diff; + } + clip_diff = (area->y+area->h)-SDL_VideoSurface->h; + if ( clip_diff > 0 ) { + area->h = area->h < 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] = (Uint8)SDL_MapRGB(screen->format, 255, 255, 255); + pixels8[1] = (Uint8)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 = (Uint16)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 ) { + SDL_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] = (Uint8)SDL_MapRGB(screen->format, 255, 255, 255); + pixels8[1] = (Uint8)SDL_MapRGB(screen->format, 0, 0, 0); + palette_changed = 0; + } + for ( h=area->h; h; h-- ) { + for ( x=0; xarea.w; ++x ) { + if ( (x%8) == 0 ) { + maskb = *mask++; + datab = *data++; + } + if ( (x >= minx) && (x < maxx) ) { + if ( maskb & 0x80 ) { + SDL_memset(dst, pixels8[datab>>7], dstbpp); + } + } + maskb <<= 1; + datab <<= 1; + dst += dstbpp; + } + dst += dstskip; + } + } else { + for ( h=area->h; h; h-- ) { + for ( x=0; xarea.w; ++x ) { + if ( (x%8) == 0 ) { + maskb = *mask++; + datab = *data++; + } + if ( (x >= minx) && (x < maxx) ) { + if ( maskb & 0x80 ) { + SDL_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-- ) { + SDL_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-- ) { + SDL_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); + if ( video->UpdateRects ) { + 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; + SDL_memset(SDL_cursor->save[0], 0, savelen); + } +} -- cgit v1.2.3