scummvm/backends/sdl/sdl-common.cpp
Paweł Kołodziejski 848c566d50 fix for wrong(or rather unset) mouse 0,0 (x,y) position when joystick is plugged
when code handle joystick motion and mapping to mouse motion, it should also
set mouse x,y position

svn-id: r6880
2003-03-29 09:56:51 +00:00

1251 lines
30 KiB
C++

/* ScummVM - Scumm Interpreter
* Copyright (C) 2001-2003 The ScummVM project
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* $Header$
*
*/
#include "sdl-common.h"
#include "sound/mididrv.h"
#include "common/scaler.h"
#include "common/engine.h" // Only #included for error() and warning()
#include "scummvm.xpm"
// FIXME move joystick defines out and replace with confile file options
// we should really allow users to map any key to a joystick button
#define JOY_DEADZONE 3200
#define JOY_ANALOG
// #define JOY_INVERT_Y
#define JOY_XAXIS 0
#define JOY_YAXIS 1
// buttons
#define JOY_BUT_LMOUSE 0
#define JOY_BUT_RMOUSE 2
#define JOY_BUT_ESCAPE 3
#define JOY_BUT_PERIOD 1
#define JOY_BUT_SPACE 4
#define JOY_BUT_F5 5
bool atexit_proc_installed = false;
void atexit_proc() {
SDL_ShowCursor(SDL_ENABLE);
SDL_Quit();
}
OSystem *OSystem_SDL_create(int gfx_mode, bool full_screen) {
return OSystem_SDL_Common::create(gfx_mode, full_screen);
}
OSystem *OSystem_SDL_Common::create(int gfx_mode, bool full_screen) {
OSystem_SDL_Common *syst = OSystem_SDL_Common::create();
syst->_mode = gfx_mode;
syst->_full_screen = full_screen;
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER | SDL_INIT_JOYSTICK) ==-1) {
error("Could not initialize SDL: %s.\n", SDL_GetError());
}
SDL_ShowCursor(SDL_DISABLE);
// Enable unicode support if possible
SDL_EnableUNICODE(1);
#ifndef MACOSX // Don't set icon on OS X, as we use a nicer external icon there
// Setup the icon
syst->setup_icon();
#endif
#ifndef MACOSX // Work around a bug in OS X
// Clean up on exit
atexit_proc_installed = true;
atexit(atexit_proc);
#endif
// enable joystick
if (SDL_NumJoysticks() > 0) {
printf("Using joystick: %s\n", SDL_JoystickName(0));
syst->init_joystick();
}
return syst;
}
void OSystem_SDL_Common::set_timer(int timer, int (*callback)(int)) {
SDL_SetTimer(timer, (SDL_TimerCallback) callback);
}
OSystem_SDL_Common::OSystem_SDL_Common()
: _screen(0), _screenWidth(0), _screenHeight(0),
_tmpscreen(0), _tmpScreenWidth(0), _overlayVisible(false),
_cdrom(0), _dirty_checksums(0),
_mouseVisible(false), _mouseDrawn(false), _mouseData(0),
_mouseHotspotX(0), _mouseHotspotY(0),
_currentShakePos(0), _newShakePos(0) {
// allocate palette storage
_currentPalette = (SDL_Color *)calloc(sizeof(SDL_Color), 256);
// allocate the dirty rect storage
_mouseBackup = (byte *)malloc(MAX_MOUSE_W * MAX_MOUSE_H * MAX_SCALING * 2);
// reset mouse state
memset(&km, 0, sizeof(km));
}
OSystem_SDL_Common::~OSystem_SDL_Common() {
if (_dirty_checksums)
free(_dirty_checksums);
free(_currentPalette);
free(_mouseBackup);
}
void OSystem_SDL_Common::init_size(uint w, uint h) {
// Avoid redundant res changes
if ((int)w == _screenWidth && (int)h == _screenHeight)
return;
_screenWidth = w;
_screenHeight = h;
CKSUM_NUM = (_screenWidth * _screenHeight / (8 * 8));
if (_dirty_checksums)
free(_dirty_checksums);
_dirty_checksums = (uint32 *)calloc(CKSUM_NUM * 2, sizeof(uint32));
unload_gfx_mode();
load_gfx_mode();
#ifdef MACOSX // Work around a bug in OS X 10.1 related to OpenGL in windowed mode
if (!atexit_proc_installed) {
atexit_proc_installed = true;
atexit(atexit_proc);
}
#endif
}
void OSystem_SDL_Common::copy_rect(const byte *buf, int pitch, int x, int y, int w, int h) {
if (_screen == NULL)
return;
if (((uint32)buf & 3) == 0 && pitch == _screenWidth && x==0 && y==0 &&
w==_screenWidth && h==_screenHeight && _mode_flags&DF_WANT_RECT_OPTIM) {
/* Special, optimized case for full screen updates.
* It tries to determine what areas were actually changed,
* and just updates those, on the actual display. */
add_dirty_rgn_auto(buf);
} else {
/* Clip the coordinates */
if (x < 0) {
w += x;
buf -= x;
x = 0;
}
if (y < 0) {
h += y;
buf -= y * pitch;
y = 0;
}
if (w > _screenWidth - x) {
w = _screenWidth - x;
}
if (h > _screenHeight - y) {
h = _screenHeight - y;
}
if (w <= 0 || h <= 0)
return;
cksum_valid = false;
add_dirty_rect(x, y, w, h);
}
/* FIXME: undraw mouse only if the draw rect intersects with the mouse rect */
if (_mouseDrawn)
undraw_mouse();
if (SDL_LockSurface(_screen) == -1)
error("SDL_LockSurface failed: %s.\n", SDL_GetError());
byte *dst = (byte *)_screen->pixels + y * _screenWidth + x;
do {
memcpy(dst, buf, w);
dst += _screenWidth;
buf += pitch;
} while (--h);
SDL_UnlockSurface(_screen);
}
void OSystem_SDL_Common::move_screen(int dx, int dy, int height) {
if ((dx == 0) && (dy == 0))
return;
if (dx == 0) {
// vertical movement
if (dy > 0) {
// move down
// copy from bottom to top
for (int y = height - 1; y >= dy; y--)
copy_rect((byte *)_screen->pixels + _screenWidth * (y - dy), _screenWidth, 0, y, _screenWidth, 1);
} else {
// move up
// copy from top to bottom
for (int y = 0; y < height + dx; y++)
copy_rect((byte *)_screen->pixels + _screenWidth * (y - dy), _screenWidth, 0, y, _screenWidth, 1);
}
} else if (dy == 0) {
// horizontal movement
if (dx > 0) {
// move right
// copy from right to left
for (int x = _screenWidth - 1; x >= dx; x--)
copy_rect((byte *)_screen->pixels + x - dx, _screenWidth, x, 0, 1, height);
} else {
// move left
// copy from left to right
for (int x = 0; x < _screenWidth; x++)
copy_rect((byte *)_screen->pixels + x - dx, _screenWidth, x, 0, 1, height);
}
} else {
// free movement
// not neccessary for now
}
}
void OSystem_SDL_Common::add_dirty_rect(int x, int y, int w, int h) {
if (_forceFull)
return;
if (_num_dirty_rects == NUM_DIRTY_RECT)
_forceFull = true;
else {
SDL_Rect *r = &_dirty_rect_list[_num_dirty_rects++];
// Extend the dirty region by 1 pixel for scalers
// that "smear" the screen, e.g. 2xSAI
if (_mode_flags & DF_UPDATE_EXPAND_1_PIXEL) {
x--;
y--;
w+=2;
h+=2;
}
// clip
if (x < 0) {
w += x; x = 0;
}
if (y < 0) {
h += y;
y=0;
}
if (w > _screenWidth - x) {
w = _screenWidth - x;
}
if (h > _screenHeight - y) {
h = _screenHeight - y;
}
r->x = x;
r->y = y;
r->w = w;
r->h = h;
}
}
#define ROL(a,n) a = (a << (n)) | (a >> (32 - (n)))
#define DOLINE(x) a ^= ((uint32*)buf)[0 + (x) * (_screenWidth / 4)]; b ^= ((uint32 *)buf)[1 + (x) * (_screenWidth / 4)]
void OSystem_SDL_Common::mk_checksums(const byte *buf) {
uint32 *sums = _dirty_checksums;
uint x,y;
const uint last_x = (uint)_screenWidth / 8;
const uint last_y = (uint)_screenHeight / 8;
/* the 8x8 blocks in buf are enumerated starting in the top left corner and
* reading each line at a time from left to right */
for(y = 0; y != last_y; y++, buf += _screenWidth * (8 - 1))
for(x=0; x != last_x; x++, buf += 8) {
uint32 a = x;
uint32 b = y;
DOLINE(0); ROL(a,13); ROL(b,11);
DOLINE(2); ROL(a,13); ROL(b,11);
DOLINE(4); ROL(a,13); ROL(b,11);
DOLINE(6); ROL(a,13); ROL(b,11);
a *= 0xDEADBEEF;
b *= 0xBAADF00D;
DOLINE(1); ROL(a,13); ROL(b,11);
DOLINE(3); ROL(a,13); ROL(b,11);
DOLINE(5); ROL(a,13); ROL(b,11);
DOLINE(7); ROL(a,13); ROL(b,11);
/* output the checksum for this block */
*sums++ = a + b;
}
}
#undef DOLINE
#undef ROL
void OSystem_SDL_Common::add_dirty_rgn_auto(const byte *buf) {
assert(((uint32)buf & 3) == 0);
/* generate a table of the checksums */
mk_checksums(buf);
if (!cksum_valid) {
_forceFull = true;
cksum_valid = true;
}
/* go through the checksum list, compare it with the previous checksums,
and add all dirty rectangles to a list. try to combine small rectangles
into bigger ones in a simple way */
if (!_forceFull) {
int x,y,w;
uint32 *ck = _dirty_checksums;
for(y = 0; y!=_screenHeight / 8; y++) {
for(x = 0; x!=_screenWidth / 8; x++,ck++) {
if (ck[0] != ck[CKSUM_NUM]) {
/* found a dirty 8x8 block, now go as far to the right as possible,
and at the same time, unmark the dirty status by setting old to new. */
w=0;
do {
ck[w + CKSUM_NUM] = ck[w];
w++;
} while (x + w != _screenWidth / 8 && ck[w] != ck[w + CKSUM_NUM]);
add_dirty_rect(x * 8, y * 8, w * 8, 8);
if (_forceFull)
goto get_out;
}
}
}
} else {
get_out:;
/* Copy old checksums to new */
memcpy(_dirty_checksums + CKSUM_NUM, _dirty_checksums, CKSUM_NUM * sizeof(uint32));
}
}
void OSystem_SDL_Common::kbd_mouse() {
uint32 time = get_msecs();
if (time >= km.last_time + km.delay_time) {
km.last_time = time;
if (km.x_down_count == 1) {
km.x_down_time = time;
km.x_down_count = 2;
}
if (km.y_down_count == 1) {
km.y_down_time = time;
km.y_down_count = 2;
}
if (km.x_vel || km.y_vel) {
if (km.x_down_count) {
if (time > km.x_down_time + km.delay_time * 12) {
if (km.x_vel > 0)
km.x_vel++;
else
km.x_vel--;
} else if (time > km.x_down_time + km.delay_time * 8) {
if (km.x_vel > 0)
km.x_vel = 5;
else
km.x_vel = -5;
}
}
if (km.y_down_count) {
if (time > km.y_down_time + km.delay_time * 12) {
if (km.y_vel > 0)
km.y_vel++;
else
km.y_vel--;
} else if (time > km.y_down_time + km.delay_time * 8) {
if (km.y_vel > 0)
km.y_vel = 5;
else
km.y_vel = -5;
}
}
km.x += km.x_vel;
km.y += km.y_vel;
if (km.x < 0) {
km.x = 0;
km.x_vel = -1;
km.x_down_count = 1;
} else if (km.x > km.x_max) {
km.x = km.x_max;
km.x_vel = 1;
km.x_down_count = 1;
}
if (km.y < 0) {
km.y = 0;
km.y_vel = -1;
km.y_down_count = 1;
} else if (km.y > km.y_max) {
km.y = km.y_max;
km.y_vel = 1;
km.y_down_count = 1;
}
SDL_WarpMouse(km.x, km.y);
}
}
}
bool OSystem_SDL_Common::show_mouse(bool visible) {
if (_mouseVisible == visible)
return visible;
bool last = _mouseVisible;
_mouseVisible = visible;
if (visible)
draw_mouse();
else
undraw_mouse();
return last;
}
void OSystem_SDL_Common::set_mouse_pos(int x, int y) {
if (x != _mouseCurState.x || y != _mouseCurState.y) {
_mouseCurState.x = x;
_mouseCurState.y = y;
undraw_mouse();
}
}
void OSystem_SDL_Common::warp_mouse(int x, int y) {
if (_mouseCurState.x != x || _mouseCurState.y != y)
SDL_WarpMouse(x * _scaleFactor, y * _scaleFactor);
}
void OSystem_SDL_Common::set_mouse_cursor(const byte *buf, uint w, uint h, int hotspot_x, int hotspot_y) {
assert(0 <= w && w <= MAX_MOUSE_W);
assert(0 <= h && h <= MAX_MOUSE_H);
_mouseCurState.w = w;
_mouseCurState.h = h;
_mouseHotspotX = hotspot_x;
_mouseHotspotY = hotspot_y;
_mouseData = (byte *)buf;
undraw_mouse();
}
void OSystem_SDL_Common::set_shake_pos(int shake_pos) {
_newShakePos = shake_pos;
}
uint32 OSystem_SDL_Common::get_msecs() {
return SDL_GetTicks();
}
void OSystem_SDL_Common::delay_msecs(uint msecs) {
SDL_Delay(msecs);
}
void *OSystem_SDL_Common::create_thread(ThreadProc *proc, void *param) {
return SDL_CreateThread(proc, param);
}
static int mapKey(SDLKey key, SDLMod mod, Uint16 unicode)
{
if (key >= SDLK_F1 && key <= SDLK_F9) {
return key - SDLK_F1 + 315;
} else if (key >= SDLK_KP0 && key <= SDLK_KP9) {
return key - SDLK_KP0 + '0';
} else if (key >= SDLK_UP && key <= SDLK_PAGEDOWN) {
return key;
} else if (unicode) {
return unicode;
} else if (key >= 'a' && key <= 'z' && mod & KMOD_SHIFT) {
return key & ~0x20;
} else if (key >= SDLK_NUMLOCK && key <= SDLK_EURO) {
return 0;
}
return key;
}
bool OSystem_SDL_Common::poll_event(Event *event) {
SDL_Event ev;
int axis;
byte b = 0;
kbd_mouse();
while(SDL_PollEvent(&ev)) {
switch(ev.type) {
case SDL_KEYDOWN:
if (ev.key.keysym.mod & KMOD_SHIFT)
b |= KBD_SHIFT;
if (ev.key.keysym.mod & KMOD_CTRL)
b |= KBD_CTRL;
if (ev.key.keysym.mod & KMOD_ALT)
b |= KBD_ALT;
event->kbd.flags = b;
// Alt-Return toggles full screen mode
if (b == KBD_ALT && ev.key.keysym.sym == SDLK_RETURN) {
property(PROP_TOGGLE_FULLSCREEN, NULL);
break;
}
// Ctrl-z and Alt-X quit
if ((b == KBD_CTRL && ev.key.keysym.sym=='z') || (b == KBD_ALT && ev.key.keysym.sym=='x')) {
quit();
break;
}
#ifdef MACOSX
// On Macintosh', Cmd-Q quits
if ((ev.key.keysym.mod & KMOD_META) && ev.key.keysym.sym=='q') {
quit();
break;
}
#endif
// Ctr-Alt-1 till Ctrl-Alt-9 will change the GFX mode
if (b == (KBD_CTRL|KBD_ALT) &&
(ev.key.keysym.sym>='1') && (ev.key.keysym.sym<='9')) {
Property prop;
prop.gfx_mode = ev.key.keysym.sym - '1';
property(PROP_SET_GFX_MODE, &prop);
break;
}
#ifdef QTOPIA
// quit on fn+backspace on zaurus
if (ev.key.keysym.sym == 127) {
quit();
break;
}
// map menu key (f11) to f5 (scumm menu)
if (ev.key.keysym.sym == SDLK_F11) {
event->event_code = EVENT_KEYDOWN;
event->kbd.keycode = SDLK_F5;
event->kbd.ascii = mapKey(SDLK_F5, ev.key.keysym.mod, 0);
}
// map center (space) to tab (default action )
// I wanted to map the calendar button but the calendar comes up
//
else if (ev.key.keysym.sym == SDLK_SPACE) {
event->event_code = EVENT_KEYDOWN;
event->kbd.keycode = SDLK_TAB;
event->kbd.ascii = mapKey(SDLK_TAB, ev.key.keysym.mod, 0);
}
// since we stole space (pause) above we'll rebind it to the tab key on the keyboard
else if (ev.key.keysym.sym == SDLK_TAB) {
event->event_code = EVENT_KEYDOWN;
event->kbd.keycode = SDLK_SPACE;
event->kbd.ascii = mapKey(SDLK_SPACE, ev.key.keysym.mod, 0);
} else {
// let the events fall through if we didn't change them, this may not be the best way to
// set it up, but i'm not sure how sdl would like it if we let if fall through then redid it though.
// and yes i have an huge terminal size so i dont wrap soon enough.
event->event_code = EVENT_KEYDOWN;
event->kbd.keycode = ev.key.keysym.sym;
event->kbd.ascii = mapKey(ev.key.keysym.sym, ev.key.keysym.mod, ev.key.keysym.unicode);
}
#else
event->event_code = EVENT_KEYDOWN;
event->kbd.keycode = ev.key.keysym.sym;
event->kbd.ascii = mapKey(ev.key.keysym.sym, ev.key.keysym.mod, ev.key.keysym.unicode);
#endif
switch(ev.key.keysym.sym) {
case SDLK_LEFT:
km.x_vel = -1;
km.x_down_count = 1;
break;
case SDLK_RIGHT:
km.x_vel = 1;
km.x_down_count = 1;
break;
case SDLK_UP:
km.y_vel = -1;
km.y_down_count = 1;
break;
case SDLK_DOWN:
km.y_vel = 1;
km.y_down_count = 1;
break;
default:
break;
}
return true;
case SDL_KEYUP:
event->event_code = EVENT_KEYUP;
event->kbd.keycode = ev.key.keysym.sym;
event->kbd.ascii = mapKey(ev.key.keysym.sym, ev.key.keysym.mod, ev.key.keysym.unicode);
switch(ev.key.keysym.sym) {
case SDLK_LEFT:
if (km.x_vel < 0) {
km.x_vel = 0;
km.x_down_count = 0;
}
break;
case SDLK_RIGHT:
if (km.x_vel > 0) {
km.x_vel = 0;
km.x_down_count = 0;
}
break;
case SDLK_UP:
if (km.y_vel < 0) {
km.y_vel = 0;
km.y_down_count = 0;
}
break;
case SDLK_DOWN:
if (km.y_vel > 0) {
km.y_vel = 0;
km.y_down_count = 0;
}
break;
default:
break;
}
return true;
case SDL_MOUSEMOTION:
event->event_code = EVENT_MOUSEMOVE;
km.x = event->mouse.x = ev.motion.x;
km.y = event->mouse.y = ev.motion.y;
event->mouse.x /= _scaleFactor;
event->mouse.y /= _scaleFactor;
return true;
case SDL_MOUSEBUTTONDOWN:
if (ev.button.button == SDL_BUTTON_LEFT)
event->event_code = EVENT_LBUTTONDOWN;
else if (ev.button.button == SDL_BUTTON_RIGHT)
event->event_code = EVENT_RBUTTONDOWN;
#if defined(SDL_BUTTON_WHEELUP) && defined(SDL_BUTTON_WHEELDOWN)
else if (ev.button.button == SDL_BUTTON_WHEELUP)
event->event_code = EVENT_WHEELUP;
else if (ev.button.button == SDL_BUTTON_WHEELDOWN)
event->event_code = EVENT_WHEELDOWN;
#endif
else
break;
km.x = event->mouse.x = ev.button.x;
km.y = event->mouse.y = ev.button.y;
event->mouse.x /= _scaleFactor;
event->mouse.y /= _scaleFactor;
return true;
case SDL_MOUSEBUTTONUP:
if (ev.button.button == SDL_BUTTON_LEFT)
event->event_code = EVENT_LBUTTONUP;
else if (ev.button.button == SDL_BUTTON_RIGHT)
event->event_code = EVENT_RBUTTONUP;
else
break;
event->mouse.x = ev.button.x;
event->mouse.y = ev.button.y;
event->mouse.x /= _scaleFactor;
event->mouse.y /= _scaleFactor;
return true;
case SDL_JOYBUTTONDOWN:
if (ev.jbutton.button == JOY_BUT_LMOUSE) {
event->event_code = EVENT_LBUTTONDOWN;
} else if (ev.jbutton.button == JOY_BUT_RMOUSE) {
event->event_code = EVENT_RBUTTONDOWN;
} else {
event->event_code = EVENT_KEYDOWN;
switch (ev.jbutton.button) {
case JOY_BUT_ESCAPE:
event->kbd.keycode = SDLK_ESCAPE;
event->kbd.ascii = mapKey(SDLK_ESCAPE, ev.key.keysym.mod, 0);
break;
case JOY_BUT_PERIOD:
event->kbd.keycode = SDLK_PERIOD;
event->kbd.ascii = mapKey(SDLK_PERIOD, ev.key.keysym.mod, 0);
break;
case JOY_BUT_SPACE:
event->kbd.keycode = SDLK_SPACE;
event->kbd.ascii = mapKey(SDLK_SPACE, ev.key.keysym.mod, 0);
break;
case JOY_BUT_F5:
event->kbd.keycode = SDLK_F5;
event->kbd.ascii = mapKey(SDLK_F5, ev.key.keysym.mod, 0);
break;
}
}
return true;
case SDL_JOYBUTTONUP:
if (ev.jbutton.button == JOY_BUT_LMOUSE) {
event->event_code = EVENT_LBUTTONUP;
} else if (ev.jbutton.button == JOY_BUT_RMOUSE) {
event->event_code = EVENT_RBUTTONUP;
} else {
event->event_code = EVENT_KEYUP;
switch (ev.jbutton.button) {
case JOY_BUT_ESCAPE:
event->kbd.keycode = SDLK_ESCAPE;
event->kbd.ascii = mapKey(SDLK_ESCAPE, ev.key.keysym.mod, 0);
break;
case JOY_BUT_PERIOD:
event->kbd.keycode = SDLK_PERIOD;
event->kbd.ascii = mapKey(SDLK_PERIOD, ev.key.keysym.mod, 0);
break;
case JOY_BUT_SPACE:
event->kbd.keycode = SDLK_SPACE;
event->kbd.ascii = mapKey(SDLK_SPACE, ev.key.keysym.mod, 0);
break;
case JOY_BUT_F5:
event->kbd.keycode = SDLK_F5;
event->kbd.ascii = mapKey(SDLK_F5, ev.key.keysym.mod, 0);
break;
}
}
return true;
case SDL_JOYAXISMOTION:
axis = ev.jaxis.value;
if ( axis > JOY_DEADZONE) {
axis -= JOY_DEADZONE;
event->event_code = EVENT_MOUSEMOVE;
} else if ( axis < -JOY_DEADZONE ) {
axis += JOY_DEADZONE;
event->event_code = EVENT_MOUSEMOVE;
} else
axis = 0;
if ( ev.jaxis.axis == JOY_XAXIS) {
#ifdef JOY_ANALOG
km.x_vel = axis/2000;
km.x_down_count = 0;
#else
if (axis != 0) {
km.x_vel = (axis > 0) ? 1:-1;
km.x_down_count = 1;
} else {
km.x_vel = 0;
km.x_down_count = 0;
}
#endif
} else if (ev.jaxis.axis == JOY_YAXIS) {
#ifndef JOY_INVERT_Y
axis = -axis;
#endif
#ifdef JOY_ANALOG
km.y_vel = -axis / 2000;
km.y_down_count = 0;
#else
if (axis != 0) {
km.y_vel = (-axis > 0) ? 1: -1;
km.y_down_count = 1;
} else {
km.y_vel = 0;
km.y_down_count = 0;
}
#endif
}
event->mouse.x = km.x;
event->mouse.y = km.y;
event->mouse.x /= _scaleFactor;
event->mouse.y /= _scaleFactor;
return true;
case SDL_VIDEOEXPOSE:
_forceFull = true;
break;
case SDL_QUIT:
quit();
break;
}
}
return false;
}
bool OSystem_SDL_Common::set_sound_proc(void *param, SoundProc *proc, byte format) {
SDL_AudioSpec desired;
memset(&desired, 0, sizeof(desired));
/* only one format supported at the moment */
desired.freq = SAMPLES_PER_SEC;
desired.format = AUDIO_S16SYS;
desired.channels = 2;
desired.samples = 2048;
desired.callback = proc;
desired.userdata = param;
if (SDL_OpenAudio(&desired, NULL) != 0) {
return false;
}
SDL_PauseAudio(0);
return true;
}
uint32 OSystem_SDL_Common::property(int param, Property *value) {
switch(param) {
case PROP_GET_FULLSCREEN:
return _full_screen;
case PROP_SET_WINDOW_CAPTION:
SDL_WM_SetCaption(value->caption, value->caption);
return 1;
case PROP_OPEN_CD:
if (SDL_InitSubSystem(SDL_INIT_CDROM) == -1)
_cdrom = NULL;
else {
_cdrom = SDL_CDOpen(value->cd_num);
/* Did if open? Check if _cdrom is NULL */
if (!_cdrom) {
warning("Couldn't open drive: %s\n", SDL_GetError());
}
}
break;
case PROP_SHOW_DEFAULT_CURSOR:
SDL_ShowCursor(value->show_cursor ? SDL_ENABLE : SDL_DISABLE);
break;
case PROP_GET_SAMPLE_RATE:
return SAMPLES_PER_SEC;
}
return 0;
}
void OSystem_SDL_Common::quit() {
if(_cdrom) {
SDL_CDStop(_cdrom);
SDL_CDClose(_cdrom);
}
unload_gfx_mode();
exit(0);
}
void OSystem_SDL_Common::draw_mouse() {
if (_mouseDrawn || !_mouseVisible)
return;
int x = _mouseCurState.x - _mouseHotspotX;
int y = _mouseCurState.y - _mouseHotspotY;
int w = _mouseCurState.w;
int h = _mouseCurState.h;
byte color;
byte *src = _mouseData; // Image representing the mouse
// clip the mouse rect, and addjust the src pointer accordingly
if (x < 0) {
w += x;
src -= x;
x = 0;
}
if (y < 0) {
h += y;
src -= y * _mouseCurState.w;
y = 0;
}
if (w > _screenWidth - x)
w = _screenWidth - x;
if (h > _screenHeight - y)
h = _screenHeight - y;
// Quick check to see if anything has to be drawn at all
if (w <= 0 || h <= 0)
return;
// Store the bounding box so that undraw mouse can restore the area the
// mouse currently covers to its original content.
_mouseOldState.x = x;
_mouseOldState.y = y;
_mouseOldState.w = w;
_mouseOldState.h = h;
// Draw the mouse cursor; backup the covered area in "bak"
if (SDL_LockSurface(_overlayVisible ? _tmpscreen : _screen) == -1)
error("SDL_LockSurface failed: %s.\n", SDL_GetError());
// Mark as dirty
add_dirty_rect(x, y, w, h);
if (!_overlayVisible) {
byte *bak = _mouseBackup; // Surface used to backup the area obscured by the mouse
byte *dst; // Surface we are drawing into
dst = (byte *)_screen->pixels + y * _screenWidth + x;
while (h > 0) {
int width = w;
while (width > 0) {
*bak++ = *dst;
color = *src++;
if (color != 0xFF) // 0xFF = transparent, don't draw
*dst = color;
dst++;
width--;
}
src += _mouseCurState.w - w;
bak += MAX_MOUSE_W - w;
dst += _screenWidth - w;
h--;
}
} else {
uint16 *bak = (uint16 *)_mouseBackup; // Surface used to backup the area obscured by the mouse
uint16 *dst; // Surface we are drawing into
dst = (uint16 *)_tmpscreen->pixels + (y + 1) * _tmpScreenWidth + (x + 1);
while (h > 0) {
int width = w;
while (width > 0) {
*bak++ = *dst;
color = *src++;
if (color != 0xFF) // 0xFF = transparent, don't draw
*dst = RGBToColor(_currentPalette[color].r, _currentPalette[color].g, _currentPalette[color].b);
dst++;
width--;
}
src += _mouseCurState.w - w;
bak += MAX_MOUSE_W - w;
dst += _tmpScreenWidth - w;
h--;
}
}
SDL_UnlockSurface(_overlayVisible ? _tmpscreen : _screen);
// Finally, set the flag to indicate the mouse has been drawn
_mouseDrawn = true;
}
void OSystem_SDL_Common::undraw_mouse() {
if (!_mouseDrawn)
return;
_mouseDrawn = false;
if (SDL_LockSurface(_overlayVisible ? _tmpscreen : _screen) == -1)
error("SDL_LockSurface failed: %s.\n", SDL_GetError());
const int old_mouse_x = _mouseOldState.x;
const int old_mouse_y = _mouseOldState.y;
const int old_mouse_w = _mouseOldState.w;
const int old_mouse_h = _mouseOldState.h;
int x, y;
if (!_overlayVisible) {
byte *dst, *bak = _mouseBackup;
// No need to do clipping here, since draw_mouse() did that already
dst = (byte *)_screen->pixels + old_mouse_y * _screenWidth + old_mouse_x;
for (y = 0; y < old_mouse_h; ++y, bak += MAX_MOUSE_W, dst += _screenWidth) {
for (x = 0; x < old_mouse_w; ++x) {
dst[x] = bak[x];
}
}
} else {
uint16 *dst, *bak = (uint16 *)_mouseBackup;
// No need to do clipping here, since draw_mouse() did that already
dst = (uint16 *)_tmpscreen->pixels + (old_mouse_y + 1) * _tmpScreenWidth + (old_mouse_x + 1);
for (y = 0; y < old_mouse_h; ++y, bak += MAX_MOUSE_W, dst += _tmpScreenWidth) {
for (x = 0; x < old_mouse_w; ++x) {
dst[x] = bak[x];
}
}
}
add_dirty_rect(old_mouse_x, old_mouse_y, old_mouse_w, old_mouse_h);
SDL_UnlockSurface(_overlayVisible ? _tmpscreen : _screen);
}
void OSystem_SDL_Common::stop_cdrom() { /* Stop CD Audio in 1/10th of a second */
cd_stop_time = SDL_GetTicks() + 100;
cd_num_loops = 0;
}
void OSystem_SDL_Common::play_cdrom(int track, int num_loops, int start_frame, int end_frame) {
if (!num_loops && !start_frame)
return;
if (!_cdrom)
return;
if (end_frame > 0)
end_frame +=5;
cd_track = track;
cd_num_loops = num_loops;
cd_start_frame = start_frame;
SDL_CDStatus(_cdrom);
if (start_frame == 0 && end_frame == 0)
SDL_CDPlayTracks(_cdrom, track, 0, 1, 0);
else
SDL_CDPlayTracks(_cdrom, track, start_frame, 0, end_frame);
cd_end_frame = end_frame;
cd_stop_time = 0;
cd_end_time = SDL_GetTicks() + _cdrom->track[track].length * 1000 / CD_FPS;
}
bool OSystem_SDL_Common::poll_cdrom() {
if (!_cdrom)
return false;
return (cd_num_loops != 0 && (SDL_GetTicks() < cd_end_time || SDL_CDStatus(_cdrom) != CD_STOPPED));
}
void OSystem_SDL_Common::update_cdrom() {
if (!_cdrom)
return;
if (cd_stop_time != 0 && SDL_GetTicks() >= cd_stop_time) {
SDL_CDStop(_cdrom);
cd_num_loops = 0;
cd_stop_time = 0;
return;
}
if (cd_num_loops == 0 || SDL_GetTicks() < cd_end_time)
return;
if (cd_num_loops != 1 && SDL_CDStatus(_cdrom) != CD_STOPPED) {
// Wait another second for it to be done
cd_end_time += 1000;
return;
}
if (cd_num_loops > 0)
cd_num_loops--;
if (cd_num_loops != 0) {
if (cd_start_frame == 0 && cd_end_frame == 0)
SDL_CDPlayTracks(_cdrom, cd_track, 0, 1, 0);
else
SDL_CDPlayTracks(_cdrom, cd_track, cd_start_frame, 0, cd_end_frame);
cd_end_time = SDL_GetTicks() + _cdrom->track[cd_track].length * 1000 / CD_FPS;
}
}
void OSystem_SDL_Common::setup_icon() {
int w, h, ncols, nbytes, i;
unsigned int rgba[256], icon[32 * 32];
unsigned char mask[32][4];
sscanf(scummvm_icon[0], "%d %d %d %d", &w, &h, &ncols, &nbytes);
if ((w != 32) || (h != 32) || (ncols > 255) || (nbytes > 1)) {
warning("Could not load the icon (%d %d %d %d)", w, h, ncols, nbytes);
return;
}
for (i = 0; i < ncols; i++) {
unsigned char code;
char color[32];
unsigned int col;
sscanf(scummvm_icon[1 + i], "%c c %s", &code, color);
if (!strcmp(color, "None"))
col = 0x00000000;
else if (!strcmp(color, "black"))
col = 0xFF000000;
else if (color[0] == '#') {
sscanf(color + 1, "%06x", &col);
col |= 0xFF000000;
} else {
warning("Could not load the icon (%d %s - %s) ", code, color, scummvm_icon[1 + i]);
return;
}
rgba[code] = col;
}
memset(mask, 0, sizeof(mask));
for (h = 0; h < 32; h++) {
char *line = scummvm_icon[1 + ncols + h];
for (w = 0; w < 32; w++) {
icon[w + 32 * h] = rgba[line[w]];
if (rgba[line[w]] & 0xFF000000) {
mask[h][w >> 3] |= 1 << (7 - (w & 0x07));
}
}
}
SDL_Surface *sdl_surf = SDL_CreateRGBSurfaceFrom(icon, 32, 32, 32, 32 * 4, 0xFF0000, 0x00FF00, 0x0000FF, 0xFF000000);
SDL_WM_SetIcon(sdl_surf, (unsigned char *) mask);
}
void *OSystem_SDL_Common::create_mutex(void) {
return (void *) SDL_CreateMutex();
}
void OSystem_SDL_Common::lock_mutex(void *mutex) {
SDL_mutexP((SDL_mutex *) mutex);
}
void OSystem_SDL_Common::unlock_mutex(void *mutex) {
SDL_mutexV((SDL_mutex *) mutex);
}
void OSystem_SDL_Common::delete_mutex(void *mutex) {
SDL_DestroyMutex((SDL_mutex *) mutex);
}
void OSystem_SDL_Common::show_overlay() {
// hide the mouse
undraw_mouse();
_overlayVisible = true;
clear_overlay();
}
void OSystem_SDL_Common::hide_overlay() {
// hide the mouse
undraw_mouse();
_overlayVisible = false;
_forceFull = true;
}
void OSystem_SDL_Common::clear_overlay() {
if (!_overlayVisible)
return;
// hide the mouse
undraw_mouse();
// Clear the overlay by making the game screen "look through" everywhere.
SDL_Rect src, dst;
src.x = src.y = 0;
dst.x = dst.y = 1;
src.w = dst.w = _screenWidth;
src.h = dst.h = _screenHeight;
if (SDL_BlitSurface(_screen, &src, _tmpscreen, &dst) != 0)
error("SDL_BlitSurface failed: %s", SDL_GetError());
_forceFull = true;
}
int16 OSystem_SDL_Common::get_height() {
return _screenHeight;
}
int16 OSystem_SDL_Common::get_width() {
return _screenWidth;
}
void OSystem_SDL_Common::grab_overlay(int16 *buf, int pitch) {
if (!_overlayVisible)
return;
if (_tmpscreen == NULL)
return;
// hide the mouse
undraw_mouse();
if (SDL_LockSurface(_tmpscreen) == -1)
error("SDL_LockSurface failed: %s.\n", SDL_GetError());
int16 *src = (int16 *)_tmpscreen->pixels + _tmpScreenWidth + 1;
int h = _screenHeight;
do {
memcpy(buf, src, _screenWidth*2);
src += _tmpScreenWidth;
buf += pitch;
} while (--h);
SDL_UnlockSurface(_tmpscreen);
}
void OSystem_SDL_Common::copy_rect_overlay(const int16 *buf, int pitch, int x, int y, int w, int h) {
if (!_overlayVisible)
return;
if (_tmpscreen == NULL)
return;
// Clip the coordinates
if (x < 0) {
w += x;
buf -= x;
x = 0;
}
if (y < 0) {
h += y; buf -= y * pitch;
y = 0;
}
if (w > _screenWidth - x) {
w = _screenWidth - x;
}
if (h > _screenHeight-y) {
h = _screenHeight - y;
}
if (w <= 0 || h <= 0)
return;
// Mark the modified region as dirty
cksum_valid = false;
add_dirty_rect(x, y, w, h);
/* FIXME: undraw mouse only if the draw rect intersects with the mouse rect */
undraw_mouse();
if (SDL_LockSurface(_tmpscreen) == -1)
error("SDL_LockSurface failed: %s.\n", SDL_GetError());
int16 *dst = (int16 *)_tmpscreen->pixels + (y + 1) * _tmpScreenWidth + (x + 1);
do {
memcpy(dst, buf, w * 2);
dst += _tmpScreenWidth;
buf += pitch;
} while (--h);
SDL_UnlockSurface(_tmpscreen);
}
int16 OSystem_SDL_Common::RGBToColor(uint8 r, uint8 g, uint8 b) {
return SDL_MapRGB(_tmpscreen->format, r, g, b);
}
void OSystem_SDL_Common::colorToRGB(int16 color, uint8 &r, uint8 &g, uint8 &b) {
SDL_GetRGB(color, _tmpscreen->format, &r, &g, &b);
}