diff options
author | Max Horn | 2006-06-24 12:33:52 +0000 |
---|---|---|
committer | Max Horn | 2006-06-24 12:33:52 +0000 |
commit | 492c65009cb186024f52ec1970617028c3d09efe (patch) | |
tree | 8e4f89975ccd8bc094161c73c128d92706880a73 /backends/platform | |
parent | dc41d02ac73109add30a3456bc44d558f29d2f8d (diff) | |
download | scummvm-rg350-492c65009cb186024f52ec1970617028c3d09efe.tar.gz scummvm-rg350-492c65009cb186024f52ec1970617028c3d09efe.tar.bz2 scummvm-rg350-492c65009cb186024f52ec1970617028c3d09efe.zip |
Started to move 'monolithic' parts of our ports to the new 'background/platform' directory (see also the 'Modular_Backends' page in our Wiki)
svn-id: r23293
Diffstat (limited to 'backends/platform')
29 files changed, 9020 insertions, 0 deletions
diff --git a/backends/platform/maemo/Makefile b/backends/platform/maemo/Makefile new file mode 100644 index 0000000000..ca49a35f9c --- /dev/null +++ b/backends/platform/maemo/Makefile @@ -0,0 +1,69 @@ +# ScummVM Maemo MakeFile +# $URL$ +# $Id$ + +#control build +DISABLE_HQ_SCALERS = true + +#DISABLE_SCUMM = 1 +#DISABLE_HE = 1 +DISABLE_SIMON = 1 +DISABLE_SKY = 1 +DISABLE_SWORD1 = 1 +DISABLE_SWORD2 = 1 +DISABLE_QUEEN = 1 +DISABLE_KYRA = 1 +DISABLE_SAGA = 1 +DISABLE_GOB = 1 +DISABLE_LURE = 1 +DISABLE_CINE = 1 +DISABLE_AGI = 1 + +srcdir = ../.. +VPATH = $(srcdir) + +CXX := g++ +EXECUTABLE := scummvm + +INCDIR = ../../ . $(srcdir)/engines/ + +CXXFLAGS := -g -ansi -W -Wno-unused-parameter +CXXFLAGS += `pkg-config --cflags gconf-2.0 hildon-libs gtk+-2.0 libosso gdk-2.0` +CXXFLAGS += $(addprefix -I,$(INCDIR)) -I. -Wall $(CXXFLAGS) +CXXFLAGS += -O -Wuninitialized +CXXFLAGS += -Wno-long-long -Wno-multichar -Wno-unknown-pragmas +# Even more warnings... +CXXFLAGS += -pedantic -Wpointer-arith -Wcast-qual -Wconversion +CXXFLAGS += -Wshadow -Wimplicit -Wundef -Wnon-virtual-dtor +CXXFLAGS += -Wno-reorder -Wwrite-strings -fcheck-new + +LIBS += -lz -L/usr/lib -lSDL -lpthread -lXsp -losso +LIBS += `pkg-config --libs gconf-2.0 hildon-libs gtk+-2.0 libosso gdk-2.0` +INCLUDES += -I/usr/include/SDL -D_REENTRANT -I/usr/X11R6/include +OBJS += +DEFINES += -DUNIX -DNONSTANDARD_PORT -D__MAEMO__ +LDFLAGS += + +RANLIB := ranlib +INSTALL := install +AR := ar cru +MKDIR := mkdir -p +ECHO := printf +CAT := cat +RM := rm -f +RM_REC := rm -f -r +CP := cp + +OBJS := main.o maemo-sdl.o + +MODULE_DIRS += . + + +BACKEND := sdl +MODULES += backends/platform/sdl base +MODULE_DIRS += . + +HAVE_GCC3 = 1 + +include $(srcdir)/Makefile.common + diff --git a/backends/platform/maemo/hildon.cpp b/backends/platform/maemo/hildon.cpp new file mode 100644 index 0000000000..1c9bd9162a --- /dev/null +++ b/backends/platform/maemo/hildon.cpp @@ -0,0 +1,134 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include <hildon-widgets/hildon-app.h> +#include <gtk/gtk.h> +#include <libosso.h> + +#define OSSO_APP_NAME "scummvm" +#define OSSO_APP_VERSION "0.9.0CVS" +#define OSSO_APP_SERVICE "org.scummvm."OSSO_APP_NAME +#define OSSO_APP_OBJECT "/org/scummvm/"OSSO_APP_NAME +#define OSSO_APP_IFACE "org.scummvm."OSSO_APP_NAME + +// Application UI data struct +typedef struct _AppData AppData; +struct _AppData { + HildonApp *app; + HildonAppView *appview; + osso_context_t *osso_context; +}; + +// Callback for exit D-BUS event +void exit_event_handler(gboolean die_now, gpointer data) { + AppData *appdata; + appdata = (AppData *)data; + g_print("exit_event_handler called\n"); + /* Do whatever application needs to do before exiting */ + gtk_infoprint(GTK_WINDOW(appdata->app), "Exiting..."); +} + +// Callback for normal D-BUS messages +gint dbus_req_handler(const gchar *interface, const gchar *method, + GArray *arguments, gpointer data, + osso_rpc_t *retval) { + AppData *appdata; + appdata = (AppData *)data; + osso_system_note_infoprint(appdata->osso_context, method, retval); + return OSSO_OK; +} + + +// Main application +int main(int argc, char *argv[]) { + // Create needed variables + HildonApp *app; + HildonAppView *appview; + osso_context_t *osso_context; + osso_return_t result; + GtkWidget *main_vbox; + GtkWidget *label; + + // Initialize the GTK. + gtk_init(&argc, &argv); + + // Initialize maemo application + osso_context = osso_initialize(OSSO_APP_NAME, OSSO_APP_VERSION, TRUE, NULL); + + // Check that initialization was ok + if (osso_context == NULL) { + return OSSO_ERROR; + } + + // Create the hildon application and setup the title + app = HILDON_APP(hildon_app_new()); + hildon_app_set_title(app, "ScummVM"); + hildon_app_set_two_part_title(app, TRUE); + + // Create HildonAppView and set it to HildonApp + appview = HILDON_APPVIEW(hildon_appview_new("AppView Title")); + hildon_app_set_appview(app, appview); + + // Create AppData + AppData *appdata; + appdata = g_new0(AppData, 1); + appdata->app = app; + appdata->appview = appview; + appdata->osso_context = osso_context; + + // Add vbox to appview + main_vbox = gtk_vbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(appview), main_vbox); + + // Add button to vbox + label = gtk_label_new("Waiting for DBUS message..."); + gtk_box_pack_start(GTK_BOX(main_vbox), label, FALSE, TRUE, 0); + + // Add handler for hello D-BUS messages + result = osso_rpc_set_cb_f(appdata->osso_context, + OSSO_APP_SERVICE, + OSSO_APP_OBJECT, + OSSO_APP_IFACE, + dbus_req_handler, appdata); + if (result != OSSO_OK) { + g_print("Error setting D-BUS callback (%d)\n", result); + return OSSO_ERROR; + } + + // Add handler for Exit D-BUS messages + result = osso_application_set_exit_cb(appdata->osso_context, + exit_event_handler, + (gpointer) appdata); + if (result != OSSO_OK) { + g_print("Error setting exit callback (%d)\n", result); + return OSSO_ERROR; + } + + // Begin the main application + gtk_widget_show_all(GTK_WIDGET(app)); + gtk_main(); + + // Deinitialize OSSO + osso_deinitialize(osso_context); + + return 0; +} diff --git a/backends/platform/maemo/maemo-sdl.cpp b/backends/platform/maemo/maemo-sdl.cpp new file mode 100644 index 0000000000..dfb6cb7048 --- /dev/null +++ b/backends/platform/maemo/maemo-sdl.cpp @@ -0,0 +1,42 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "common/stdafx.h" +#include "backends/maemo/maemo-sdl.h" + + +void OSystem_MAEMO::loadGFXMode() { + if (_fullscreen || effectiveScreenHeight() > 400) { + _overlayWidth = 800; + _overlayHeight = 480; + + _fullscreen = true; + } else { + _overlayWidth = 720; + _overlayHeight = 400; + } + + if (_screenHeight != 200) + _adjustAspectRatio = false; + + OSystem_SDL::loadGFXMode(); +} diff --git a/backends/platform/maemo/maemo-sdl.h b/backends/platform/maemo/maemo-sdl.h new file mode 100644 index 0000000000..e342ead5f9 --- /dev/null +++ b/backends/platform/maemo/maemo-sdl.h @@ -0,0 +1,41 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef MAEMO_SDL_H +#define MAEMO_SDL_H + +#include "common/stdafx.h" +#include "backends/platform/sdl/sdl-common.h" + +//#include "CEkeys.h" + +#include <SDL.h> + + +class OSystem_MAEMO : public OSystem_SDL { +public: + OSystem_MAEMO() {}; + + void loadGFXMode(); +}; + +#endif diff --git a/backends/platform/maemo/main.cpp b/backends/platform/maemo/main.cpp new file mode 100644 index 0000000000..2631d35000 --- /dev/null +++ b/backends/platform/maemo/main.cpp @@ -0,0 +1,81 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include <common/stdafx.h> +#include <common/scummsys.h> +#include <X11/Xlib.h> +#include <X11/extensions/Xsp.h> + +#include <SDL/SDL.h> +#include <SDL/SDL_syswm.h> + +#include "backends/maemo/maemo-sdl.h" +#include "base/main.h" +#include <hildon-widgets/hildon-app.h> +#include <gtk/gtk.h> +#include <libosso.h> + +#include <sys/time.h> +#include <sys/resource.h> + +#define OSSO_APP_NAME "scummvm" +#define OSSO_APP_VERSION "0.9.0CVS" + +void set_doubling(unsigned char enable) { + return; + + SDL_SysWMinfo wminfo; + SDL_VERSION(&wminfo.version); + SDL_GetWMInfo(&wminfo); + XSPSetPixelDoubling(wminfo.info.x11.display, 0, enable); +} + +int main(int argc, char *argv[]) { + osso_context_t *osso_context; + + // Initialize maemo application + //osso_context = osso_initialize(OSSO_APP_NAME, OSSO_APP_VERSION, TRUE, NULL); + + // Check that initialization was ok + //if (osso_context == NULL) { + // return OSSO_ERROR; + //} + + // Maemo task navigator priority inheritance fix + setpriority(PRIO_PROCESS, 0, 0); + + set_doubling(0); + + g_system = new OSystem_MAEMO(); + assert(g_system); + + // Invoke the actual ScummVM main entry point: + int res = scummvm_main(argc, argv); + g_system->quit(); // TODO: Consider removing / replacing this! + + /* Deinitialize OSSO */ + //osso_deinitialize(osso_context); + + set_doubling(0); + + return res; +} diff --git a/backends/platform/maemo/portdefs.h b/backends/platform/maemo/portdefs.h new file mode 100644 index 0000000000..f825e5c4e2 --- /dev/null +++ b/backends/platform/maemo/portdefs.h @@ -0,0 +1,69 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef PORTDEFS_H +#define PORTDEFS_H + +#define SCUMM_LITTLE_ENDIAN +#define SCUMM_NEED_ALIGNMENT + +#undef HAVE_X86 + +#undef LINUPY + +/* Data types */ +typedef unsigned char byte; +typedef unsigned int uint; +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; +typedef signed char int8; +typedef signed short int16; +typedef signed int int32; + +/* Libs */ +#undef USE_VORBIS +#undef USE_TREMOR +#undef USE_FLAC +#undef USE_MAD +#define USE_ZLIB +#undef USE_MPEG2 +#undef USE_MT32EMU + +/* Whether we should use i386 assembly routines */ +#undef USE_NASM + +#include <stdio.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <assert.h> +#include <ctype.h> +#include <time.h> +#include <math.h> + +void set_doubling(unsigned char enable); + +#endif /* PORTDEFS_H */ + + diff --git a/backends/platform/morphos/MorphOS.readme b/backends/platform/morphos/MorphOS.readme new file mode 100644 index 0000000000..201c8f32b8 --- /dev/null +++ b/backends/platform/morphos/MorphOS.readme @@ -0,0 +1,13 @@ +This directory contains the source for the MorphOS port of ScummVM. To build, you +must have a proper Geek Gadgets installation. If you don't have the includes for +Etude and cdda.library, check my webpage. If they aren't uploaded yet, feel free +to e-mail me. + +You don't have to build ScummVM yourself. The latest official and CVS binaries are +available from my website at: + +http://www.muenster.de/~tomjoad/scummvm.html + +Ruediger Hanke +tomjoad@muenster.de + diff --git a/backends/platform/morphos/build.rules b/backends/platform/morphos/build.rules new file mode 100644 index 0000000000..13b0033b8c --- /dev/null +++ b/backends/platform/morphos/build.rules @@ -0,0 +1,11 @@ +CXX = g++ +CXXFLAGS = -Wno-multichar -fstrength-reduce -fsigned-char -O2 +DEFINES = -DNDEBUG +LDFLAGS = -noixemul -s +LIBS = -lamiga -lamigastubs -lcybergraphics +INCLUDES += -Ibackends/morphos +MODULES += backends/morphos +OBJS += backends/morphos/morphos.o backends/morphos/morphos_scaler.o \ + backends/morphos/morphos_sound.o backends/morphos/morphos_start.o \ + backends/morphos/morphos_timer.o + diff --git a/backends/platform/morphos/morphos.cpp b/backends/platform/morphos/morphos.cpp new file mode 100644 index 0000000000..889976afc4 --- /dev/null +++ b/backends/platform/morphos/morphos.cpp @@ -0,0 +1,1645 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2002 Rüdiger Hanke + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * MorphOS interface + * + * $URL$ + * $Id$ + * + */ + +#include "common/stdafx.h" +#include "base/engine.h" +#include "common/util.h" +#include "scumm/scumm.h" + +#include <exec/types.h> +#include <exec/memory.h> +#include <exec/libraries.h> +#include <exec/semaphores.h> +#include <devices/ahi.h> +#include <devices/rawkeycodes.h> +#include <dos/dostags.h> +#include <intuition/screens.h> +#include <cybergraphics/cybergraphics.h> +#include <devices/input.h> +#include <devices/inputevent.h> +#include <intuition/intuition.h> + +#include <clib/alib_protos.h> +#include <proto/exec.h> +#include <proto/dos.h> +#include <proto/graphics.h> +#include <proto/intuition.h> +#include <proto/keymap.h> +#include <proto/timer.h> +#include <proto/cdda.h> +#include <proto/cybergraphics.h> + +#include <time.h> + +#include "morphos.h" +#include "morphos_sound.h" +#include "morphos_scaler.h" + +static TagItem PlayTags[] = { { CDPA_StartTrack, 1 }, + { CDPA_StartFrame, 0 }, + { CDPA_EndTrack, 1 }, + { CDPA_EndFrame, 0 }, + { CDPA_Loops, 1 }, + { TAG_DONE, 0 } + }; + +static CONST_STRPTR MonkeyCDIDs[] = { "ID2500496F035CBC", "ID250040360345DB", NULL }; +static CONST_STRPTR LoomCDIDs[] = { NULL }; +static CONST_STRPTR MonkeyNames[] = { "Monkey1CD", "Madness", NULL }; +static CONST_STRPTR LoomNames[] = { "LoomCD", NULL }; + +#define BLOCKSIZE_X 32 +#define BLOCKSIZE_Y 8 + +#define BLOCKS_X (ScummBufferWidth/BLOCKSIZE_X) +#define BLOCKS_Y (ScummBufferHeight/BLOCKSIZE_Y) +#define BLOCK_ID(x, y) ((y/BLOCKSIZE_Y)*BLOCKS_X+(x/BLOCKSIZE_X)) + +OSystem_MorphOS *OSystem_MorphOS::create(SCALERTYPE gfx_scaler, bool full_screen) +{ + OSystem_MorphOS *syst = new OSystem_MorphOS(gfx_scaler, full_screen); + + if (!syst || !syst->Initialise()) + { + delete syst; + error("Failed to create system object. Exiting."); + } + + return syst; +} + +OSystem_MorphOS::OSystem_MorphOS(SCALERTYPE gfx_mode, bool full_screen) +{ + ScummScreen = NULL; + ScummWindow = NULL; + ScummBuffer = NULL; + ScummScreenBuffer[0] = NULL; + ScummScreenBuffer[1] = NULL; + ScummRenderTo = NULL; + ScummNoCursor = NULL; + ScummSoundThread = NULL; + ScummWinX = -1; + ScummWinY = -1; + ScummDefaultMouse = false; + ScummOrigMouse = false; + ScummShakePos = 0; + ScummScaler = gfx_mode; + ScummScale = (gfx_mode == ST_NONE) ? 0 : 1; + ScummDepth = 0; + Scumm16ColFmt16 = false; + ScummScrWidth = 0; + ScummScrHeight = 0; + ScreenChanged = false; + DirtyBlocks = NULL; + BlockColors = NULL; + UpdateRects = 0; + Scaler = NULL; + FullScreenMode = full_screen; + CDrive = NULL; + CDDATrackOffset = 0; + strcpy(ScummWndTitle, "ScummVM MorphOS"); + TimerMsgPort = NULL; + TimerIORequest = NULL; + InputMsgPort = NULL; + InputIORequest = NULL; + ThreadPort = NULL; + OvlCMap = NULL; + OvlBitMap = NULL; + OvlSavedBuffer = NULL; + TimerBase = NULL; + ScummNoCursor = NULL; + UpdateRegion = NULL; + NewUpdateRegion = NULL; + MouseImage = NULL; +} + +bool OSystem_MorphOS::Initialise() +{ + OpenATimer(&TimerMsgPort, (IORequest **) &TimerIORequest, UNIT_MICROHZ); + + if ((InputMsgPort = CreateMsgPort())) + { + if ((InputIORequest = (IOStdReq*) CreateIORequest(InputMsgPort, sizeof (IOStdReq)))) + { + if ((OpenDevice("input.device", NULL, (IORequest *) InputIORequest, NULL))) + { + DeleteIORequest(InputIORequest); + DeleteMsgPort(InputMsgPort); + InputIORequest = NULL; + InputMsgPort = NULL; + } + } + else + { + DeleteMsgPort(InputMsgPort); + InputMsgPort = NULL; + } + } + + if (!InputIORequest) + { + warning("input.device could not be opened"); + return false; + } + + ThreadPort = CreateMsgPort(); + if (!ThreadPort) + { + warning("Unable to create a message port"); + return false; + } + + OvlCMap = GetColorMap(256); + + InitSemaphore(&CritSec); + + TimerBase = (Library*) TimerIORequest->tr_node.io_Device; + ScummNoCursor = (UWORD *) AllocVec(16, MEMF_CLEAR); + UpdateRegion = NewRegion(); + NewUpdateRegion = NewRegion(); + if (!UpdateRegion || !NewUpdateRegion) + { + warning("Could not create region for screen update"); + return false; + } + if (!OvlCMap) + { + warning("Could not allocate overlay color map"); + return false; + } + if (!ScummNoCursor) + { + warning("Could not allocate empty cursor image"); + return false; + } + + return true; +} + +OSystem_MorphOS::~OSystem_MorphOS() +{ + if (DirtyBlocks) + { + FreeVec(DirtyBlocks); + + for (int b = 0; b < BLOCKS_X*BLOCKS_Y; b++) + FreeVec(BlockColors[b]); + FreeVec(BlockColors); + } + + if (OvlCMap) + FreeColorMap(OvlCMap); + + delete Scaler; + + if (UpdateRegion) + DisposeRegion(UpdateRegion); + + if (NewUpdateRegion) + DisposeRegion(NewUpdateRegion); + + if (ThreadPort) + DeleteMsgPort(ThreadPort); + + if (CDrive && CDDABase) + { + CDDA_Stop(CDrive); + CDDA_ReleaseDrive(CDrive); + } + + if (InputIORequest) + { + CloseDevice((IORequest *) InputIORequest); + DeleteIORequest((IORequest *) InputIORequest); + } + + if (InputMsgPort) + DeleteMsgPort(InputMsgPort); + + if (TimerIORequest) + { + CloseDevice((IORequest *) TimerIORequest); + DeleteIORequest((IORequest *) TimerIORequest); + } + + if (TimerMsgPort) + DeleteMsgPort(TimerMsgPort); + + if (ScummNoCursor) + FreeVec(ScummNoCursor); + + if (ScummBuffer) + FreeVec(ScummBuffer); + + if (OvlSavedBuffer) + FreeVec(OvlSavedBuffer); + + if (ScummRenderTo && !ScummScreen) + FreeBitMap(ScummRenderTo); + + if (OvlBitMap) + FreeVec(OvlBitMap); + + if (ScummWindow) + CloseWindow(ScummWindow); + + if (ScummScreen) + { + if (ScummScreenBuffer[0]) + FreeScreenBuffer(ScummScreen, ScummScreenBuffer[0]); + if( ScummScreenBuffer[1] ) + FreeScreenBuffer(ScummScreen, ScummScreenBuffer[1]); + CloseScreen(ScummScreen); + } +} + +bool OSystem_MorphOS::OpenATimer(MsgPort **port, IORequest **req, ULONG unit, bool required) +{ + *req = NULL; + const char *err_msg = NULL; + + *port = CreateMsgPort(); + if (*port) + { + *req = (IORequest *) CreateIORequest(*port, sizeof (timerequest)); + if (*req) + { + if (OpenDevice(TIMERNAME, unit, *req, 0)) + { + DeleteIORequest(*req); + *req = NULL; + err_msg = "Failed to open timer device"; + } + } + else + err_msg = "Failed to create IO request"; + } + else + err_msg = "Failed to create message port"; + + if (err_msg) + { + if (required) + error(err_msg); + warning(err_msg); + } + + return *req != NULL; +} + +uint32 OSystem_MorphOS::getMillis() +{ + int ticks = clock(); + ticks *= (1000/CLOCKS_PER_SEC); + return ticks; +} + +void OSystem_MorphOS::delayMillis(uint msecs) +{ +/* TimerIORequest->tr_node.io_Command = TR_ADDREQUEST; + TimerIORequest->tr_time.tv_secs = 0; + TimerIORequest->tr_time.tv_micro = msecs*1000; + DoIO((IORequest *) TimerIORequest);*/ + TimeDelay(UNIT_MICROHZ, 0, msecs*1000); +} + +void OSystem_MorphOS::setTimerCallback(TimerProc callback, int timer) +{ + warning("setTimerCallback() unexpectedly called"); +} + +OSystem::MutexRef OSystem_MorphOS::createMutex() +{ + SignalSemaphore *sem = (SignalSemaphore *) AllocVec(sizeof (SignalSemaphore), MEMF_PUBLIC); + + if (sem) + InitSemaphore(sem); + + return (MutexRef)sem; +} + +void OSystem_MorphOS::lockMutex(MutexRef mutex) +{ + ObtainSemaphore((SignalSemaphore *) mutex); +} + +void OSystem_MorphOS::unlockMutex(MutexRef mutex) +{ + ReleaseSemaphore((SignalSemaphore *)mutex); +} + +void OSystem_MorphOS::deleteMutex(MutexRef mutex) +{ + FreeVec(mutex); +} + +uint32 OSystem_MorphOS::property(int param, Property *value) +{ + AUTO_LOCK + + switch (param) + { + case PROP_GET_FULLSCREEN: + return ScummScreen != NULL; + + case PROP_TOGGLE_FULLSCREEN: + CreateScreen(CSDSPTYPE_TOGGLE); + return 1; + + case PROP_SET_WINDOW_CAPTION: + sprintf(ScummWndTitle, "ScummVM MorphOS - %s", value->caption); + if (ScummWindow) + SetWindowTitles(ScummWindow, ScummWndTitle, ScummWndTitle); + return 1; + + case PROP_OPEN_CD: + { + CONST_STRPTR *ids = NULL, *names = NULL; + + if (g_scumm) + GameID = g_scumm->_gameId; + + switch (GameID) + { + case GID_MONKEY: + case GID_MONKEY_SEGA: + ids = MonkeyCDIDs; + names = MonkeyNames; + break; + + case GID_LOOM256: + ids = LoomCDIDs; + names = LoomNames; + break; + } + + if (!CDDABase) CDDABase = OpenLibrary("cdda.library", 2); + if (CDDABase) + { + CDrive = NULL; + if (ids) + { + int i = 0; + + while (ids[i] && !CDrive) + { + TagItem FindCDTags[] = { { CDFA_CDID, (ULONG) ids[i] }, + { TAG_DONE, 0 } + }; + CDrive = CDDA_FindNextDriveA(NULL, FindCDTags); + i++; + } + } + + if (!CDrive && names) + { + int i = 0; + + while (names[i] && !CDrive) + { + TagItem FindCDTags[] = { { CDFA_VolumeName, (ULONG) names[i] }, + { TAG_DONE, 0 } + }; + CDrive = CDDA_FindNextDriveA(NULL, FindCDTags); + i++; + } + } + + if (CDrive) + { + if (!CDDA_ObtainDriveA(CDrive, CDDA_SHARED_ACCESS, NULL)) + { + CDrive = NULL; + warning("Failed to obtain CD drive - music will not play"); + } + else if (GameID == GID_LOOM256) + { + // Offset correction *may* be required + CDS_TrackInfo ti = { sizeof (CDS_TrackInfo) }; + + if (CDDA_GetTrackInfo(CDrive, 1, 0, &ti)) + CDDATrackOffset = ti.ti_TrackStart.tm_Format.tm_Frame-22650; + } + } + else + warning( "Could not find game CD inserted in CD-ROM drive - cd audio will not play" ); + } + else + warning( "Failed to open cdda.library - cd audio will not play" ); + break; + } + + case PROP_GET_SAMPLE_RATE: + return SAMPLES_PER_SEC; + } + + return 0; +} + +void OSystem_MorphOS::playCD(int track, int num_loops, int start_frame, int duration) +{ + if (CDrive && start_frame >= 0) + { + if (start_frame > 0) + start_frame -= CDDATrackOffset; + + PlayTags[0].ti_Data = track; + PlayTags[1].ti_Data = start_frame; + PlayTags[2].ti_Data = (duration == 0) ? track+1 : track; + PlayTags[3].ti_Data = duration ? start_frame+duration : 0; + PlayTags[4].ti_Data = (num_loops == 0) ? 1 : num_loops; + CDDA_PlayA(CDrive, PlayTags); + } +} + +void OSystem_MorphOS::stopCD() +{ + if (CDrive) + CDDA_Stop(CDrive); +} + +bool OSystem_MorphOS::pollCD() +{ + ULONG status; + + if (CDrive == NULL) + return false; + + CDDA_GetAttr(CDDA_Status, CDrive, &status); + return status == CDDA_Status_Busy; +} + +void OSystem_MorphOS::updateCD() +{ +} + +void OSystem_MorphOS::quit() +{ + int num_threads = 0; + + if (ScummSoundThread) + { + num_threads++; + Signal((Task *) ScummSoundThread, SIGBREAKF_CTRL_C); + ScummSoundThread = NULL; + } + + // TODO: this code could probably greatly simplified now that there is + // only one thread left... + while (num_threads > 0) + { + Message* msg; + + WaitPort(ThreadPort); + while (msg = GetMsg(ThreadPort)) + num_threads--; + } + + exit(0); +} + +#define CVT8TO32(col) ((col<<24) | (col<<16) | (col<<8) | col) + +void OSystem_MorphOS::setPalette(const byte *colors, uint start, uint num) +{ + const byte *data = colors; + UWORD changed_colors[256]; + UWORD num_changed = 0; + + for (uint i = start; i != start+num; i++) + { + ULONG color32 = (data[0] << 16) | (data[1] << 8) | data[2]; + if (color32 != ScummColors[i]) + { + if (ScummDepth == 8) + SetRGB32(&ScummScreen->ViewPort, i, CVT8TO32(data[0]), CVT8TO32(data[1]), CVT8TO32(data[2])); + ScummColors16[i] = Scumm16ColFmt16 ? (((data[0]*31)/255) << 11) | (((data[1]*63)/255) << 5) | ((data[ 2 ]*31)/255) : (((data[0]*31)/255) << 10) | (((data[1]*31)/255) << 5) | ((data[2]*31)/255); + ScummColors[i] = color32; + changed_colors[num_changed++] = i; + } + data += 4; + } + + if (ScummScale || ScummDepth != 8) + { + if (DirtyBlocks && num_changed < 200) + { + for (int b = 0; b < BLOCKS_X*BLOCKS_Y; b++) + { + UWORD *block_colors = BlockColors[b]; + UWORD *color_ptr = changed_colors; + for (int c = 0; c < num_changed; c++) + { + if (block_colors[*color_ptr++]) + { + UWORD x, y; + x = b % BLOCKS_X; + y = b / BLOCKS_X; + DirtyBlocks[b] = true; + AddUpdateRect(x*BLOCKSIZE_X, y*BLOCKSIZE_Y, BLOCKSIZE_X, BLOCKSIZE_Y); + break; + } + } + } + } + else + AddUpdateRect(0, 0, ScummBufferWidth, ScummBufferHeight); + } +} + +void OSystem_MorphOS::CreateScreen(CS_DSPTYPE dspType) +{ + LONG mode = INVALID_ID; + int depths[] = { 8, 32, 16, 15, 0 }; + int i; + Screen *wb = NULL; + + if (dspType != CSDSPTYPE_KEEP) + FullScreenMode = (dspType == CSDSPTYPE_FULLSCREEN) || (dspType == CSDSPTYPE_TOGGLE && !FullScreenMode); + + if (ScummRenderTo && !ScummScreen) + FreeBitMap(ScummRenderTo); + ScummRenderTo = NULL; + + if (ScummWindow) + { + if (ScummScreen == NULL) + { + ScummWinX = ScummWindow->LeftEdge; + ScummWinY = ScummWindow->TopEdge; + } + CloseWindow (ScummWindow); + ScummWindow = NULL; + } + + if (ScummScreen) + { + if (ScummScreenBuffer[0]) + FreeScreenBuffer(ScummScreen, ScummScreenBuffer[0]); + if (ScummScreenBuffer[1]) + FreeScreenBuffer(ScummScreen, ScummScreenBuffer[1]); + CloseScreen(ScummScreen); + ScummScreen = NULL; + } + + ScummScrWidth = ScummBufferWidth << ScummScale; + ScummScrHeight = ScummBufferHeight << ScummScale; + + if (FullScreenMode) + { + for (i = ScummScale; mode == INVALID_ID && depths[i]; i++) + mode = BestCModeIDTags(CYBRBIDTG_NominalWidth, ScummScrWidth, + CYBRBIDTG_NominalHeight, ScummScrHeight, + CYBRBIDTG_Depth, depths[i], + TAG_DONE + ); + ScummDepth = depths[i-1]; + + if (mode == INVALID_ID) + error("Could not find suitable screenmode"); + + ScummScreen = OpenScreenTags(NULL, SA_AutoScroll, TRUE, + SA_Depth, ScummDepth, + SA_Width, STDSCREENWIDTH, + SA_Height, STDSCREENHEIGHT, + SA_DisplayID, mode, + SA_ShowTitle, FALSE, + SA_Type, CUSTOMSCREEN, + SA_Title, "ScummVM MorphOS", + TAG_DONE + ); + + if (ScummScreen == NULL) + error("Failed to open screen"); + + LONG RealDepth = GetBitMapAttr(&ScummScreen->BitMap, BMA_DEPTH); + if (RealDepth != ScummDepth) + { + warning("Screen did not open in expected depth"); + ScummDepth = RealDepth; + } + ScummScreenBuffer[0] = AllocScreenBuffer(ScummScreen, NULL, SB_SCREEN_BITMAP); + ScummScreenBuffer[1] = AllocScreenBuffer(ScummScreen, NULL, 0); + ScummRenderTo = ScummScreenBuffer[1]->sb_BitMap; + ScummPaintBuffer = 1; + + if (ScummScreenBuffer[0] == NULL || ScummScreenBuffer[1] == NULL) + error("Failed to allocate screen buffer"); + + // Make both buffers black to avoid grey strip on bottom of screen + RastPort rp; + InitRastPort(&rp); + SetRGB32(&ScummScreen->ViewPort, 0, 0, 0, 0); + rp.BitMap = ScummScreenBuffer[0]->sb_BitMap; + FillPixelArray(&ScummScreen->RastPort, 0, 0, ScummScreen->Width, ScummScreen->Height, 0); + rp.BitMap = ScummRenderTo; + FillPixelArray(&rp, 0, 0, ScummScreen->Width, ScummScreen->Height, 0); + + if (ScummDepth == 8) + { + for (int color = 0; color < 256; color++) + { + ULONG r, g, b; + + r = (ScummColors[color] >> 16) & 0xff; + g = (ScummColors[color] >> 8) & 0xff; + b = (ScummColors[color] >> 0) & 0xff; + SetRGB32(&ScummScreen->ViewPort, color, CVT8TO32(r), CVT8TO32(g), CVT8TO32(b)); + } + } + } + else + { + wb = LockPubScreen(NULL); + if (wb == NULL) + error("Could not lock default public screen"); + + ScreenToFront(wb); + } + + ScummWindow = OpenWindowTags(NULL, WA_Left, (wb && ScummWinX >= 0) ? ScummWinX : 0, + WA_Top, wb ? ((ScummWinY >= 0) ? ScummWinY : wb->BarHeight+1) : 0, + WA_InnerWidth, FullScreenMode ? ScummScreen->Width : ScummScrWidth, + WA_InnerHeight, FullScreenMode ? ScummScreen->Height : ScummScrHeight, + WA_Activate, TRUE, + WA_Title, wb ? ScummWndTitle : NULL, + WA_ScreenTitle, wb ? ScummWndTitle : NULL, + WA_Borderless, FullScreenMode, + WA_CloseGadget, !FullScreenMode, + WA_DepthGadget, !FullScreenMode, + WA_DragBar, !FullScreenMode, + WA_ReportMouse, TRUE, + WA_RMBTrap, TRUE, + WA_IDCMP, IDCMP_RAWKEY | + IDCMP_MOUSEMOVE | + IDCMP_CLOSEWINDOW | + IDCMP_MOUSEBUTTONS, + WA_CustomScreen, FullScreenMode ? (ULONG)ScummScreen : (ULONG)wb, + TAG_DONE + ); + + if (wb) + UnlockPubScreen(NULL, wb); + + if (ScummWindow == NULL) + error("Failed to open window"); + + if (!ScummDefaultMouse) + { + SetPointer(ScummWindow, ScummNoCursor, 1, 1, 0, 0); + ScummOrigMouse = false; + } + + if (ScummScreen == NULL) + { + ScummDepth = GetCyberMapAttr(ScummWindow->RPort->BitMap, CYBRMATTR_DEPTH); + if (ScummDepth == 8) + error("Default public screen must be 15 bit or higher if you want to play in window mode"); + + ScummRenderTo = AllocBitMap(ScummScrWidth, ScummScrHeight, ScummDepth, BMF_MINPLANES, ScummWindow->RPort->BitMap); + if (ScummRenderTo == NULL) + error("Failed to allocate bitmap"); + } + + if ((ScummDepth == 15 && Scumm16ColFmt16) || (ScummDepth == 16 && !Scumm16ColFmt16)) + { + for (int col = 0; col < 256; col++) + { + int r = (ScummColors[col] >> 16) & 0xff; + int g = (ScummColors[col] >> 8) & 0xff; + int b = ScummColors[col] & 0xff; + ScummColors16[col] = (Scumm16ColFmt16 == false) ? (((r*31)/255) << 11) | (((g*63)/255) << 5) | ((b*31)/255) : (((r*31)/255) << 10) | (((g*31)/255) << 5) | ((b*31)/255); + } + + Scumm16ColFmt16 = (ScummDepth == 16); + } + + if (OvlBitMap) + FreeVec(OvlBitMap); + + OvlBitMap = AllocVec(ScummBufferWidth*ScummBufferHeight*3, MEMF_PUBLIC | MEMF_CLEAR); + if (OvlBitMap == NULL) + error("Failed to allocated bitmap for overlay"); + + if (Scaler) + { + delete Scaler; + Scaler = NULL; + } + + if (ScummScale) + { + Scaler = MorphOSScaler::Create(ScummScaler, ScummBuffer, ScummBufferWidth, ScummBufferHeight, ScummColors, ScummColors16, ScummRenderTo); + if (Scaler == NULL) + { + warning("Failed to create scaler - scaling will be disabled"); + SwitchScalerTo(ST_NONE); + } + } + + AddUpdateRect(0, 0, ScummBufferWidth, ScummBufferHeight); +} + +void OSystem_MorphOS::SwitchScalerTo(SCALERTYPE newScaler) +{ + if (newScaler == ST_NONE && ScummScale != 0) + { + if (Scaler) + { + delete Scaler; + Scaler = NULL; + } + ScummScale = 0; + ScummScaler = ST_NONE; + CreateScreen(ScummScreen ? CSDSPTYPE_FULLSCREEN : CSDSPTYPE_WINDOWED); + } + else + { + if (ScummScale == 0) + { + ScummScale = 1; + ScummScaler = newScaler; + CreateScreen(ScummScreen ? CSDSPTYPE_FULLSCREEN : CSDSPTYPE_WINDOWED); + } + else if (ScummScaler != newScaler) + { + ScummScaler = newScaler; + if (Scaler) + delete Scaler; + Scaler = MorphOSScaler::Create(ScummScaler, ScummBuffer, ScummBufferWidth, ScummBufferHeight, ScummColors, ScummColors16, ScummRenderTo); + if (Scaler == NULL) + { + warning("Failed to create scaler - scaling will be disabled"); + SwitchScalerTo(ST_NONE); + } + else + AddUpdateRect(0, 0, ScummBufferWidth, ScummBufferHeight); + } + } +} + +bool OSystem_MorphOS::pollEvent(Event &event) +{ + IntuiMessage *ScummMsg; + + ScummMsg = (IntuiMessage *) GetMsg(ScummWindow->UserPort); + if (ScummMsg) + { + switch (ScummMsg->Class) + { + case IDCMP_RAWKEY: + { + InputEvent FakedIEvent; + char charbuf; + int qual = 0; + + memset(&FakedIEvent, 0, sizeof (InputEvent)); + FakedIEvent.ie_Class = IECLASS_RAWKEY; + FakedIEvent.ie_Code = ScummMsg->Code; + + if (ScummMsg->Qualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT)) + qual |= KBD_ALT; + if (ScummMsg->Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)) + qual |= KBD_SHIFT; + if (ScummMsg->Qualifier & IEQUALIFIER_CONTROL) + qual |= KBD_CTRL; + event.kbd.flags = qual; + + event.type = (ScummMsg->Code & IECODE_UP_PREFIX) ? EVENT_KEYUP : EVENT_KEYDOWN; + ScummMsg->Code &= ~IECODE_UP_PREFIX; + + if (ScummMsg->Code >= RAWKEY_F1 && ScummMsg->Code <= RAWKEY_F10) + { + /* + * Function key + */ + event.kbd.ascii = (ScummMsg->Code-RAWKEY_F1)+315; + event.kbd.keycode = 0; + } + else if (ScummMsg->Code == RAWKEY_F11 || ScummMsg->Code == RAWKEY_F12) + { + /* + * Function key on PC keyboard + */ + event.kbd.ascii = (ScummMsg->Code == RAWKEY_F11) ? 325 : 326; + event.kbd.keycode = 0; + } + else if (ScummMsg->Code == NM_WHEEL_UP || ScummMsg->Code == NM_WHEEL_DOWN) + { + /* + * Wheelmouse event + */ + event.type = (ScummMsg->Code == NM_WHEEL_UP) ? EVENT_WHEELUP : EVENT_WHEELDOWN; + } + else if (MapRawKey(&FakedIEvent, &charbuf, 1, NULL) == 1) + { + if (qual == KBD_CTRL && charbuf == 'z') + { + event.type = EVENT_QUIT; + break; + } + else if (qual == KBD_ALT) + { + if (charbuf >= '0' && charbuf <= '9') + { + SCALERTYPE new_scaler = MorphOSScaler::FindByIndex(charbuf-'0'); + ReplyMsg((Message *) ScummMsg); + if (new_scaler != ST_INVALID) + SwitchScalerTo(new_scaler); + return false; + } + else if (charbuf == 'x') + { + event.type = EVENT_QUIT; + break; + } + else if (charbuf == 0x0d) + { + ReplyMsg((Message *) ScummMsg); + CreateScreen(CSDSPTYPE_TOGGLE); + return false; + } + } + + event.kbd.ascii = charbuf; + event.kbd.keycode = charbuf; + } + break; + } + + case IDCMP_MOUSEMOVE: + { + LONG newx, newy; + + newx = (ScummMsg->MouseX-ScummWindow->BorderLeft) >> ScummScale; + newy = (ScummMsg->MouseY-ScummWindow->BorderTop) >> ScummScale; + + if (!FullScreenMode && !ScummDefaultMouse) + { + if (newx < 0 || newx > (LONG) ScummBufferWidth || + newy < 0 || newy > (LONG) ScummBufferHeight + ) + { + if (!ScummOrigMouse) + { + ScummOrigMouse = true; + ClearPointer(ScummWindow); + } + } + else if (ScummOrigMouse) + { + ScummOrigMouse = false; + SetPointer(ScummWindow, ScummNoCursor, 1, 1, 0, 0); + } + } + else if (FullScreenMode) + newy = newy <? (ScummScrHeight >> ScummScale)-2; + + event.type = EVENT_MOUSEMOVE; + event.mouse.x = newx; + event.mouse.y = newy; + set_mouse_pos(event.mouse.x, event.mouse.y); + break; + } + + case IDCMP_MOUSEBUTTONS: + { + int newx, newy; + + newx = (ScummMsg->MouseX-ScummWindow->BorderLeft) >> ScummScale; + newy = (ScummMsg->MouseY-ScummWindow->BorderTop) >> ScummScale; + + switch (ScummMsg->Code) + { + case SELECTDOWN: + event.type = EVENT_LBUTTONDOWN; + break; + + case SELECTUP: + event.type = EVENT_LBUTTONUP; + break; + + case MENUDOWN: + event.type = EVENT_RBUTTONDOWN; + break; + + case MENUUP: + event.type = EVENT_RBUTTONUP; + break; + + default: + ReplyMsg((Message *)ScummMsg); + return false; + } + event.mouse.x = newx; + event.mouse.y = newy; + break; + } + + case IDCMP_CLOSEWINDOW: + event.type = EVENT_QUIT; + break; + } + + if (ScummMsg) + ReplyMsg((Message *) ScummMsg); + + return true; + } + + return false; +} + +void OSystem_MorphOS::warpMouse(int x, int y) +{ + if (InputIORequest) + { + InputEvent* FakeIE; + IEPointerPixel* NewPixel; + + /* + * Fake a mousemove input event + */ + if ((FakeIE = (InputEvent*) AllocVec(sizeof (InputEvent), MEMF_PUBLIC))) + { + if ((NewPixel = (IEPointerPixel*) AllocVec(sizeof (IEPointerPixel), MEMF_PUBLIC))) + { + NewPixel->iepp_Screen = ScummWindow->WScreen; + NewPixel->iepp_Position.X = (x << ScummScale) + ScummWindow->LeftEdge + ScummWindow->BorderLeft; + NewPixel->iepp_Position.Y = (y << ScummScale) + ScummWindow->TopEdge + ScummWindow->BorderTop; + + FakeIE->ie_EventAddress = NewPixel; + FakeIE->ie_NextEvent = NULL; + FakeIE->ie_Class = IECLASS_NEWPOINTERPOS; + FakeIE->ie_SubClass = IESUBCLASS_PIXEL; + FakeIE->ie_Code = IECODE_NOBUTTON; + FakeIE->ie_Qualifier = NULL; + + InputIORequest->io_Data = FakeIE; + InputIORequest->io_Length = sizeof (InputEvent); + InputIORequest->io_Command = IND_WRITEEVENT; + DoIO((IORequest *) InputIORequest); + + FreeVec(NewPixel); + } + FreeVec(FakeIE); + } + } +} + +void OSystem_MorphOS::setShakePos(int shake_pos) +{ + ScummShakePos = shake_pos; + AddUpdateRect(0, 0, ScummBufferWidth, ScummBufferHeight); +} + +#define MOUSE_INTERSECTS(x, y, w, h) \ + (!((MouseOldX+MouseOldWidth <= x ) || (MouseOldX >= x+w) || \ + (MouseOldY+MouseOldHeight <= y) || (MouseOldY >= y+h))) + +/* Copy part of bitmap */ +void OSystem_MorphOS::copyRectToScreen(const byte *src, int pitch, int x, int y, int w, int h) +{ + byte *dst; + + if (x < 0) { w+=x; src-=x; x = 0; } + if (y < 0) { h+=y; src-=y*pitch; y = 0; } + if (w >= ScummBufferWidth-x) { w = ScummBufferWidth - x; } + if (h >= ScummBufferHeight-y) { h = ScummBufferHeight - y; } + + if (w <= 0 || h <= 0) + return; + + AUTO_LOCK + + if (MouseDrawn) + { + if (MOUSE_INTERSECTS(x, y, w, h)) + UndrawMouse(); + } + + AddUpdateRect(x, y, w, h); + + dst = (byte *)ScummBuffer+y*ScummBufferWidth + x; + if (DirtyBlocks) + { + int cx, cy; + int block = BLOCK_ID(x, y); + int line_block = block; + int start_block = BLOCKSIZE_X-(x % BLOCKSIZE_X); + int start_y_block = BLOCKSIZE_Y-(y % BLOCKSIZE_Y); + int next_block; + int next_y_block; + UWORD *block_cols = BlockColors[block]; + + if (start_block == 0) + start_block = BLOCKSIZE_X; + if (start_y_block == 0) + start_y_block = BLOCKSIZE_Y; + + next_block = start_block; + next_y_block = start_y_block; + for (cy = 0; cy < h; cy++) + { + for (cx = 0; cx < w; cx++) + { + UWORD old_pixel = *dst; + UWORD src_pixel = *src++; + if (old_pixel != src_pixel) + { + *dst++ = src_pixel; + block_cols[old_pixel]--; + block_cols[src_pixel]++; + } + else + dst++; + if (--next_block == 0) + { + block++; + block_cols = BlockColors[block]; + next_block = BLOCKSIZE_X; + } + } + if (--next_y_block == 0) + { + line_block += BLOCKS_X; + next_y_block = BLOCKSIZE_Y; + } + block = line_block; + block_cols = BlockColors[block]; + next_block = start_block; + dst += ScummBufferWidth-w; + src += pitch-w; + } + } + else + { + do + { + memcpy(dst, src, w); + dst += ScummBufferWidth; + src += pitch; + } while (--h); + } +} + +bool OSystem_MorphOS::AddUpdateRect(WORD x, WORD y, WORD w, WORD h) +{ + if (UpdateRects > 25) + return false; + + if (x < 0) { w+=x; x = 0; } + if (y < 0) { h+=y; y = 0; } + if (w >= ScummBufferWidth-x) { w = ScummBufferWidth - x; } + if (h >= ScummBufferHeight-y) { h = ScummBufferHeight - y; } + + if (w <= 0 || h <= 0) + return false; + + if (++UpdateRects > 25) + { + x = 0; y = 0; + w = ScummBufferWidth; h = ScummBufferHeight; + } + + Rectangle update_rect = { x, y, x+w, y+h }; + OrRectRegion(NewUpdateRegion, &update_rect); + ScreenChanged = true; + + return true; +} + +void OSystem_MorphOS::updateScreen() +{ + AUTO_LOCK + + DrawMouse(); + + if (!ScreenChanged) + return; + + OrRegionRegion(NewUpdateRegion, UpdateRegion); + + if (ScummShakePos) + { + RastPort rp; + + InitRastPort(&rp); + rp.BitMap = ScummRenderTo; + + uint32 src_y = 0; + uint32 dest_y = 0; + if (ScummShakePos < 0) + src_y = -ScummShakePos; + else + dest_y = ScummShakePos; + + if (!ScummScale) + { + if (ScummDepth == 8) + WritePixelArray(ScummBuffer, 0, src_y, ScummBufferWidth, &rp, 0, dest_y, ScummBufferWidth, ScummBufferHeight-src_y-dest_y, RECTFMT_LUT8); + else + WriteLUTPixelArray(ScummBuffer, 0, src_y, ScummBufferWidth, &rp, ScummColors, 0, dest_y, ScummBufferWidth, ScummBufferHeight-src_y-dest_y, CTABFMT_XRGB8); + } + else if (Scaler->Prepare(ScummRenderTo)) + { + Scaler->Scale(0, src_y, 0, dest_y, ScummBufferWidth, ScummBufferHeight-src_y-dest_y); + Scaler->Finish(); + } + + if (ScummShakePos < 0) + FillPixelArray(&rp, 0, (ScummBufferHeight-1) << ScummScale, ScummScrWidth, -ScummShakePos << ScummScale, 0); + else + FillPixelArray(&rp, 0, 0, ScummScrWidth, ScummShakePos << ScummScale, 0); + } + else if (!ScummScale) + { + RastPort rp; + + InitRastPort(&rp); + rp.BitMap = ScummRenderTo; + + int32 src_x, src_y; + int32 src_w, src_h; + int32 reg_x, reg_y; + RegionRectangle *update_rect = UpdateRegion->RegionRectangle; + + reg_x = UpdateRegion->bounds.MinX; + reg_y = UpdateRegion->bounds.MinY; + while (update_rect) + { + src_x = update_rect->bounds.MinX; + src_y = update_rect->bounds.MinY; + src_w = update_rect->bounds.MaxX-src_x; + src_h = update_rect->bounds.MaxY-src_y; + src_x += reg_x; + src_y += reg_y; + + if (src_x) src_x--; + if (src_y) src_y--; + src_w += 2; + if (src_x+src_w >= ScummBufferWidth) + src_w = ScummBufferWidth-src_x; + src_h += 2; + if (src_y+src_h >= ScummBufferHeight) + src_h = ScummBufferHeight-src_y; + + if (ScummDepth == 8) + WritePixelArray(ScummBuffer, src_x, src_y, ScummBufferWidth, &rp, src_x, src_y, src_w, src_h, RECTFMT_LUT8); + else + WriteLUTPixelArray(ScummBuffer, src_x, src_y, ScummBufferWidth, &rp, ScummColors, src_x, src_y, src_w, src_h, CTABFMT_XRGB8); + + update_rect = update_rect->Next; + } + } + else + { + int32 src_x, src_y; + int32 src_w, src_h; + int32 reg_x, reg_y; + RegionRectangle *update_rect = UpdateRegion->RegionRectangle; + + reg_x = UpdateRegion->bounds.MinX; + reg_y = UpdateRegion->bounds.MinY; + + if (!Scaler->Prepare(ScummRenderTo)) + update_rect = NULL; + + while (update_rect) + { + src_x = update_rect->bounds.MinX; + src_y = update_rect->bounds.MinY; + src_w = update_rect->bounds.MaxX-src_x; + src_h = update_rect->bounds.MaxY-src_y; + src_x += reg_x; + src_y += reg_y; + + if (src_x) src_x--; + if (src_y) src_y--; + src_w += 2; + if (src_x+src_w >= ScummBufferWidth) + src_w = ScummBufferWidth-src_x; + src_h += 2; + if (src_y+src_h >= ScummBufferHeight) + src_h = ScummBufferHeight-src_y; + + Scaler->Scale(src_x, src_y, src_x, src_y, src_w, src_h); + update_rect = update_rect->Next; + } + Scaler->Finish(); + } + + if (ScummScreen) + { + while (!ChangeScreenBuffer(ScummScreen, ScummScreenBuffer[ScummPaintBuffer])); + ScummPaintBuffer = !ScummPaintBuffer; + ScummRenderTo = ScummScreenBuffer[ScummPaintBuffer]->sb_BitMap; + } + else + { + int32 x = (UpdateRegion->bounds.MinX-1) << ScummScale; + int32 y = (UpdateRegion->bounds.MinY-1) << ScummScale; + if (x < 0) x = 0; + if (y < 0) y = 0; + int32 w = (UpdateRegion->bounds.MaxX << ScummScale)-x+(1 << ScummScale); + int32 h = (UpdateRegion->bounds.MaxY << ScummScale)-y+(1 << ScummScale); + if (x+w > ScummScrWidth) w = ScummScrWidth-x; + if (y+h > ScummScrHeight) h = ScummScrHeight-y; + BltBitMapRastPort(ScummRenderTo, x, y, ScummWindow->RPort, ScummWindow->BorderLeft+x, ScummWindow->BorderTop+y, w, h, ABNC | ABC); + WaitBlit(); + } + + Region *new_region_part = NewUpdateRegion; + NewUpdateRegion = UpdateRegion; + ClearRegion(NewUpdateRegion); + UpdateRegion = new_region_part; + + ScreenChanged = false; + memset(DirtyBlocks, 0, BLOCKS_X*BLOCKS_Y*sizeof (bool)); + UpdateRects = 0; +} + +void OSystem_MorphOS::DrawMouse() +{ + int x,y; + byte *dst,*bak; + byte color; + + if (MouseDrawn || !MouseVisible) + return; + MouseDrawn = true; + + int ydraw = MouseY - MouseHotspotY; + int xdraw = MouseX - MouseHotspotX; + int w = MouseWidth; + int h = MouseHeight; + int x_mouseimg_offs = 0; + int y_mouseimg_offs = 0; + byte *buf; + + if (xdraw < 0) { x_mouseimg_offs = -xdraw; w += xdraw; xdraw = 0; } + if (ydraw < 0) { y_mouseimg_offs = -ydraw; h += ydraw; ydraw = 0; } + + MouseOldX = xdraw; + MouseOldY = ydraw; + MouseOldWidth = w; + MouseOldHeight = h; + + AddUpdateRect(xdraw, ydraw, w, h); + dst = (byte*)ScummBuffer + ydraw*ScummBufferWidth + xdraw; + bak = MouseBackup; + buf = MouseImage + y_mouseimg_offs*MAX_MOUSE_W + x_mouseimg_offs; + + for (y = 0; y < h; y++, dst += ScummBufferWidth, bak += MAX_MOUSE_W, buf += MouseWidth) + { + if (ydraw+y < ScummBufferHeight) + { + for (x = 0; x<w; x++) + { + if (xdraw+x < ScummBufferWidth) + { + bak[x] = dst[x]; + if ((color=buf[x])!=MouseKeycolor) + dst[x] = color; + } + } + } + else + break; + } +} + +void OSystem_MorphOS::UndrawMouse() +{ + int x,y; + byte *dst,*bak; + + if (!MouseDrawn) + return; + MouseDrawn = false; + + dst = (byte*)ScummBuffer + MouseOldY*ScummBufferWidth + MouseOldX; + bak = MouseBackup; + + AddUpdateRect(MouseOldX, MouseOldY, MouseOldWidth, MouseOldHeight); + + for (y = 0; y < MouseOldHeight; y++, bak += MAX_MOUSE_W, dst += ScummBufferWidth) + { + if (MouseOldY + y < ScummBufferHeight) + { + for (x = 0; x < MouseOldWidth; x++) + { + if (MouseOldX + x < ScummBufferWidth) + dst[x] = bak[x]; + } + } + else + break; + } +} + +bool OSystem_MorphOS::showMouse(bool visible) +{ + if (MouseVisible == visible) + return visible; + + bool last = MouseVisible; + MouseVisible = visible; + + if (!visible) + UndrawMouse(); + + return last; +} + +void OSystem_MorphOS::set_mouse_pos(int x, int y) +{ + if (x != MouseX || y != MouseY) + { + MouseX = x; + MouseY = y; + UndrawMouse(); + } +} + +void OSystem_MorphOS::setMouseCursor(const byte *buf, uint w, uint h, int hotspot_x, int hotspot_y, byte keycolor) +{ + MouseWidth = w; + MouseHeight = h; + + MouseHotspotX = hotspot_x; + MouseHotspotY = hotspot_y; + + MouseKeycolor = keycolor; + + if (MouseImage) + free(MouseImage); + + MouseImage = (byte *)malloc(w * h); + memcpy(mouseImage, buf, w * h); + + UndrawMouse(); +} + +bool OSystem_MorphOS::setSoundCallback(OSystem::SoundProc proc, void *param) +{ + if (ScummSoundThread) + { + if (SoundProc == proc) + return true; + clearSoundCallback(); + } + + SoundProc = proc; + SoundParam = param; + + /* + * Create Sound Thread + */ + SoundStartup.mn_Node.ln_Type = NT_MESSAGE; + SoundStartup.mn_ReplyPort = ThreadPort; + SoundStartup.mn_Length = sizeof(SoundStartup); + + ScummSoundThread = CreateNewProcTags(NP_Entry, (ULONG) &morphos_sound_thread, + NP_CodeType, CODETYPE_PPC, + NP_Name, (ULONG) "ScummVM Sound Thread", + NP_StartupMsg, &SoundStartup, + NP_PPC_Arg1, (ULONG) this, + NP_PPC_Arg2, AHIST_S16S, TAG_DONE); + if (!ScummSoundThread) + { + puts("Failed to create sound thread"); + exit(1); + } + + return true; +} + + +void OSystem_MorphOS::fill_sound(byte *stream, int len) +{ + if (SoundProc) + SoundProc(SoundParam, stream, len); + else + memset(stream, 0x0, len); +} + +void OSystem_MorphOS::clearSoundCallback() +{ + if (ScummSoundThread) + { + Signal((Task *) ScummSoundThread, SIGBREAKF_CTRL_C); + ScummSoundThread = NULL; + /* Wait for thread to finish */ + WaitPort(ThreadPort); + } +} + +void OSystem_MorphOS::initSize(uint w, uint h) +{ + if (ScummBuffer) + { + FreeVec(ScummBuffer); + ScummBuffer = NULL; + } + if (DirtyBlocks) + { + FreeVec(DirtyBlocks); + + for (int b = 0; b < BLOCKS_X*BLOCKS_Y; b++) + FreeVec(BlockColors[b]); + FreeVec(BlockColors); + DirtyBlocks = NULL; + } + + /* + * Allocate image buffer + */ + ScummBuffer = AllocVec(w*h, MEMF_CLEAR); + + if (ScummBuffer == NULL) + { + puts("Couldn't allocate image buffer"); + exit(1); + } + + OvlSavedBuffer = AllocVec(w*h, MEMF_CLEAR); + + if (OvlSavedBuffer == NULL) + { + FreeVec(ScummBuffer); + puts("Couldn't allocate overlay backup image buffer"); + exit(1); + } + + memset(ScummColors, 0, 256*sizeof (ULONG)); + + ScummBufferWidth = w; + ScummBufferHeight = h; + + DirtyBlocks = (bool *) AllocVec(BLOCKS_X*BLOCKS_Y*sizeof (bool), MEMF_CLEAR); + if (DirtyBlocks) + { + BlockColors = (UWORD **) AllocVec(BLOCKS_X*BLOCKS_Y*sizeof (UWORD *), MEMF_CLEAR); + if (BlockColors) + { + int b; + + for (b = 0; b < BLOCKS_X*BLOCKS_Y; b++) + { + BlockColors[b] = (UWORD *) AllocVec(256*sizeof (UWORD), MEMF_CLEAR); + if (BlockColors[b] == NULL) + break; + BlockColors[b][0] = BLOCKSIZE_X*BLOCKSIZE_Y; + } + + if (b < BLOCKS_X*BLOCKS_Y) + { + for (--b; b >= 0; --b) + FreeVec(BlockColors[b]); + FreeVec(BlockColors); + BlockColors = NULL; + } + } + + if (!BlockColors) + { + FreeVec(DirtyBlocks); + DirtyBlocks = NULL; + } + } + + CreateScreen(CSDSPTYPE_KEEP); +} + +int16 OSystem_MorphOS::getWidth() +{ + return ScummScrWidth; +} + +int16 OSystem_MorphOS::getHeight() +{ + return ScummScrHeight; +} + +void OSystem_MorphOS::showOverlay() +{ + UndrawMouse(); + memcpy(OvlSavedBuffer, ScummBuffer, ScummBufferWidth*ScummBufferHeight); + clearOverlay(); + for (int c = 0; c < 256; c++) + { + ULONG r, g, b; + r = ScummColors[c] >> 16; + g = (ScummColors[c] >> 8) & 0xff; + b = ScummColors[c] & 0xff; + SetRGB32CM(OvlCMap, c, CVT8TO32(r), CVT8TO32(g), CVT8TO32(b)); + } +} + +void OSystem_MorphOS::hideOverlay() +{ + copyRectToScreen((byte *) OvlSavedBuffer, ScummBufferWidth, 0, 0, ScummBufferWidth, ScummBufferHeight); +} + +void OSystem_MorphOS::clearOverlay() +{ + AUTO_LOCK + + UBYTE *src = (UBYTE *) ScummBuffer; + UBYTE *dest = (UBYTE *) OvlBitMap; + copyRectToScreen((byte *) OvlSavedBuffer, ScummBufferWidth, 0, 0, ScummBufferWidth, ScummBufferHeight); + for (int y = 0; y < ScummBufferHeight; y++) + for (int x = 0; x < ScummBufferWidth; x++) + { + *dest++ = ScummColors[*src] >> 16; + *dest++ = (ScummColors[*src] >> 8) & 0xff; + *dest++ = ScummColors[*src++] & 0xff; + } +} + +void OSystem_MorphOS::grabOverlay(int16 *buf, int pitch) +{ + int h = ScummBufferHeight; + int x; + UBYTE *src = (UBYTE *) OvlBitMap; + + do + { + for (x = 0; x < pitch; x++) + { + *buf++ = (src[0]*31/255 << 11) | (src[1]*63/255 << 5) | src[2]*31/255; + src += 3; + } + src += (ScummBufferWidth-pitch)*3; + } while (--h); +} + +void OSystem_MorphOS::copyRectToOverlay(const int16 *ovl, int pitch, int x, int y, int w, int h) +{ + int x1, y1; + UBYTE *dest; + UBYTE *bmap, *bmap_dest; + LONG last_col[2] = { -1, -1 }; + LONG last_pen[2] = { -1, -1 }; + + if (w > pitch) w = pitch; + bmap = (UBYTE*) AllocVec(w*h, MEMF_ANY); + if (bmap) + { + bmap_dest = bmap; + dest = ((UBYTE *) OvlBitMap)+y*ScummBufferWidth*3+x*3; + for (y1 = 0; y1 < h; y1++) + { + for (x1 = 0; x1 < w; x1++) + { + uint8 r, g, b; + int16 col; + + col = *ovl++; + colorToRGB(col, r, g, b); + *dest++ = r; + *dest++ = g; + *dest++ = b; + if (col == last_col[0]) + *bmap_dest++ = last_pen[0]; + else if (col == last_col[1]) + *bmap_dest++ = last_pen[1]; + else + { + last_col[1] = last_col[0]; + last_pen[1] = last_pen[0]; + last_col[0] = col; + last_pen[0] = FindColor(OvlCMap, CVT8TO32(r), CVT8TO32(g), CVT8TO32(b), -1); + *bmap_dest++ = last_pen[0]; + } + } + dest += (ScummBufferWidth-w)*3; + ovl += pitch-w; + } + copyRectToScreen(bmap, w, x, y, w, h); + FreeVec(bmap); + } +} + diff --git a/backends/platform/morphos/morphos.h b/backends/platform/morphos/morphos.h new file mode 100644 index 0000000000..67ed8a481e --- /dev/null +++ b/backends/platform/morphos/morphos.h @@ -0,0 +1,232 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2002 Rüdiger Hanke (MorphOS port) + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * MorphOS-specific header file + * + * $URL$ + * $Id$ + * + */ + +#ifndef MORPHOS_MORPHOS_H +#define MORPHOS_MORPHOS_H + +#include <dos/dosextens.h> +#include <graphics/regions.h> +#include <intuition/intuition.h> +#include <intuition/screens.h> +#include <libraries/cdda.h> +#include <proto/exec.h> + +#include "backends/intern.h" +#include "morphos_scaler.h" + +class OSystem_MorphOS : public OSystem +{ + public: + OSystem_MorphOS(SCALERTYPE gfx_mode, bool full_screen); + virtual ~OSystem_MorphOS(); + + bool Initialise(); + + // Set colors of the palette + virtual void setPalette(const byte *colors, uint start, uint num); + + // Set the size of the video bitmap. + // Typically, 320x200 + virtual void initSize(uint w, uint h); + + // Draw a bitmap to screen. + // The screen will not be updated to reflect the new bitmap + virtual void copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h); + + // Update the dirty areas of the screen + virtual void updateScreen(); + + // Either show or hide the mouse cursor + virtual bool showMouse(bool visible); + + // Set the position of the mouse cursor + virtual void set_mouse_pos(int x, int y); + + // Set the bitmap that's used when drawing the cursor. + virtual void setMouseCursor(const byte *buf, uint w, uint h, int hotspot_x, int hotspot_y, byte keycolor); + + // Shaking is used in SCUMM. Set current shake position. + virtual void setShakePos(int shake_pos); + + // Overlay + virtual void showOverlay(); + virtual void hideOverlay(); + virtual void clearOverlay(); + virtual void grabOverlay(int16 *buf, int pitch); + virtual void copyRectToOverlay(const int16 *buf, int pitch, int x, int y, int w, int h); + virtual int16 getHeight(); + virtual int16 getWidth(); + + // Get the number of milliseconds since the program was started. + virtual uint32 getMillis(); + + // Delay for a specified amount of milliseconds + virtual void delayMillis(uint msecs); + + // Add a new callback timer + virtual void setTimerCallback(TimerProc callback, int timer); + + // Mutex handling + virtual MutexRef createMutex(); + virtual void lockMutex(MutexRef mutex); + virtual void unlockMutex(MutexRef mutex); + virtual void deleteMutex(MutexRef mutex); + + // Get the next event. + // Returns true if an event was retrieved. + virtual bool pollEvent(Event &event); + + // Moves mouse pointer to specified position + virtual void warpMouse(int x, int y); + + // Set the function to be invoked whenever samples need to be generated + virtual bool setSoundCallback(SoundProc proc, void *param); + void fill_sound (byte * stream, int len); + void clearSoundCallback(); + + virtual uint32 property(int param, Property *value); + + // Poll cdrom status + // Returns true if cd audio is playing + virtual bool pollCD(); + + // Play cdrom audio track + virtual void playCD(int track, int num_loops, int start_frame, int duration); + + // Stop cdrom audio track + virtual void stopCD(); + + // Update cdrom audio status + virtual void updateCD(); + + // Quit + virtual void quit(); + + static OSystem_MorphOS *create(SCALERTYPE gfx_scaler, bool full_screen); + + static bool OpenATimer(MsgPort **port, IORequest **req, ULONG unit, bool required = true); + + private: + typedef enum { CSDSPTYPE_WINDOWED, CSDSPTYPE_FULLSCREEN, CSDSPTYPE_TOGGLE, CSDSPTYPE_KEEP } CS_DSPTYPE; + + static const int MAX_MOUSE_W = 80; + static const int MAX_MOUSE_H = 80; + + void CreateScreen(CS_DSPTYPE dspType); + void SwitchScalerTo(SCALERTYPE newScaler); + bool AddUpdateRect(WORD x, WORD y, WORD w, WORD h); + + void DrawMouse(); + void UndrawMouse(); + + /* Display-related attributes */ + Screen *ScummScreen; + Window *ScummWindow; + char ScummWndTitle[125]; + APTR ScummBuffer; + LONG ScummBufferWidth; + LONG ScummBufferHeight; + ScreenBuffer *ScummScreenBuffer[2]; + BitMap *ScummRenderTo; + ULONG ScummPaintBuffer; + int ScummScrWidth; + int ScummScrHeight; + int ScummDepth; + bool Scumm16ColFmt16; + UWORD *ScummNoCursor; + ULONG ScummColors[256]; + USHORT ScummColors16[256]; + WORD ScummWinX; + WORD ScummWinY; + bool ScummDefaultMouse; + bool ScummOrigMouse; + int ScummShakePos; + bool FullScreenMode; + bool ScreenChanged; + UWORD **BlockColors; + bool *DirtyBlocks; + Region *UpdateRegion; + Region *NewUpdateRegion; + ULONG UpdateRects; + SignalSemaphore CritSec; + + /* Overlay-related attributes */ + APTR OvlBitMap; + APTR OvlSavedBuffer; + ColorMap *OvlCMap; + + /* Sound-related attributes */ + Process *ScummSoundThread; + SoundProc SoundProc; + void *SoundParam; + MsgPort *ThreadPort; + Message MusicStartup; + Message SoundStartup; + + /* CD-ROM related attributes */ + CDRIVEPTR CDrive; + ULONG CDDATrackOffset; + + /* Scaling-related attributes */ + SCALERTYPE ScummScaler; + int ScummScale; + MorphOSScaler *Scaler; + + /* Mouse cursor-related attributes */ + bool MouseVisible, MouseDrawn; + int MouseX, MouseY; + int MouseWidth, MouseHeight; + int MouseOldX, MouseOldY; + int MouseOldWidth, MouseOldHeight; + int MouseHotspotX, MouseHotspotY; + byte *MouseImage, MouseBackup[MAX_MOUSE_W*MAX_MOUSE_H]; + byte MouseKeycolor; + MsgPort* InputMsgPort; + IOStdReq*InputIORequest; + + /* Timer-related attributes */ + MsgPort *TimerMsgPort; + timerequest *TimerIORequest; + + /* Game-related attributes */ + int GameID; +}; + +class AutoLock +{ + public: + AutoLock(SignalSemaphore* s) : sem(s) { ObtainSemaphore(sem); } + ~AutoLock() { ReleaseSemaphore(sem); } + + private: + SignalSemaphore* sem; +}; + +#define AUTO_LOCK AutoLock cs(&CritSec); + + +extern OSystem_MorphOS *TheSystem; + +#endif + diff --git a/backends/platform/morphos/morphos_scaler.cpp b/backends/platform/morphos/morphos_scaler.cpp new file mode 100644 index 0000000000..46d4452c0b --- /dev/null +++ b/backends/platform/morphos/morphos_scaler.cpp @@ -0,0 +1,848 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2002-2006 The ScummVM Team + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#include "common/stdafx.h" +#include "base/engine.h" + +#include <exec/types.h> +#include <cybergraphics/cybergraphics.h> + +#include <proto/cybergraphics.h> + +#include "morphos.h" +#include "morphos_scaler.h" + +#define GET_RESULT(A, B, C, D) ((A != C || A != D) - (B != C || B != D)) +#define INTERPOLATE(A, B) (((A & colorMask) >> 1) + ((B & colorMask) >> 1) + (A & B & lowPixelMask)) +#define Q_INTERPOLATE(A, B, C, D) ((A & qcolorMask) >> 2) + ((B & qcolorMask) >> 2) + ((C & qcolorMask) >> 2) + ((D & qcolorMask) >> 2) + ((((A & qlowpixelMask) + (B & qlowpixelMask) + (C & qlowpixelMask) + (D & qlowpixelMask)) >> 2) & qlowpixelMask) +#define SWAP_WORD(word) word = ((word & 0xff) << 8) | (word >> 8) +#define SWAP_LONG(lng) lng = ((lng & 0xff) << 24) | ((lng & 0xff00) << 8) | ((lng & 0xff0000) >> 8) | (lng >> 24) + +MorphOSScaler::GfxScaler MorphOSScaler::ScummScalers[11] + = { { "none", "normal", ST_NONE }, + { "Point", "2x", ST_POINT }, + { "AdvMame2x", "advmame2x", ST_ADVMAME2X }, + { "SuperEagle", "supereagle", ST_SUPEREAGLE }, + { "Super2xSaI", "super2xsai", ST_SUPER2XSAI }, + { NULL, NULL, ST_INVALID }, + { NULL, NULL, ST_INVALID }, + { NULL, NULL, ST_INVALID }, + { NULL, NULL, ST_INVALID }, + { NULL, NULL, ST_INVALID }, + // This is the end marker ... do not assign a scaler to it! + { NULL, NULL, ST_INVALID } + }; + +MorphOSScaler::MorphOSScaler(APTR buffer, int width, int height, ULONG *col_table, UWORD *col_table16, BitMap *bmap) +{ + dest = NULL; + handle = NULL; + + Buffer = buffer; + BufferWidth = width; + BufferHeight = height; + + ScummColors = col_table; + ScummColors16 = col_table16; + + /* Initialize scaling stuff */ + int minr, ming, minb; + ULONG depth = GetCyberMapAttr(bmap, CYBRMATTR_DEPTH); + + if (depth > 16) + { + minr = 1 << 16; + ming = 1 << 8; + minb = 1; + } + else + { + minr = 1 << ((depth == 15) ? 10 : 11); + ming = 1 << 5; + minb = 1; + } + + int pixfmt = GetCyberMapAttr(bmap, CYBRMATTR_PIXFMT); + + ScummPCMode = false; + if (pixfmt == PIXFMT_RGB15PC || pixfmt == PIXFMT_BGR15PC || + pixfmt == PIXFMT_RGB16PC || pixfmt == PIXFMT_BGR16PC || + pixfmt == PIXFMT_BGRA32) + ScummPCMode = true; + + colorMask = (MakeColor(pixfmt, 255, 0, 0) - minr) | (MakeColor(pixfmt, 0, 255, 0) - ming) | (MakeColor(pixfmt, 0, 0, 255) - minb); + lowPixelMask = minr | ming | minb; + qcolorMask = (MakeColor(pixfmt, 255, 0, 0) - 3*minr) | (MakeColor(pixfmt, 0, 255, 0) - 3*ming) | (MakeColor(pixfmt, 0, 0, 255) - 3*minb); + qlowpixelMask = (minr * 3) | (ming * 3) | (minb * 3); + redblueMask = MakeColor(pixfmt, 255, 0, 255); + greenMask = MakeColor(pixfmt, 0, 255, 0); + + PixelsPerMask = (depth <= 16) ? 2 : 1; + + if (PixelsPerMask == 2) + { + colorMask |= (colorMask << 16); + qcolorMask |= (qcolorMask << 16); + lowPixelMask |= (lowPixelMask << 16); + qlowpixelMask |= (qlowpixelMask << 16); + } +} + +MorphOSScaler::~MorphOSScaler() +{ + Finish(); +} + +MorphOSScaler *MorphOSScaler::Create(SCALERTYPE scaler_type, APTR buffer, int width, int height, ULONG *col_table, UWORD *col_table16, BitMap *bmap) +{ + MorphOSScaler *new_scaler = NULL; + + switch (scaler_type) + { + case ST_POINT: + new_scaler = new PointScaler(buffer, width, height, col_table, col_table16, bmap); + break; + + case ST_ADVMAME2X: + new_scaler = new AdvMame2xScaler(buffer, width, height, col_table, col_table16, bmap); + break; + + case ST_SUPEREAGLE: + new_scaler = new SuperEagleScaler(buffer, width, height, col_table, col_table16, bmap); + break; + + case ST_SUPER2XSAI: + new_scaler = new Super2xSaIScaler(buffer, width, height, col_table, col_table16, bmap); + break; + + default: + warning("Invalid scaler requested - falling back to Super2xSaI"); + new_scaler = new Super2xSaIScaler(buffer, width, height, col_table, col_table16, bmap); + break; + } + + return new_scaler; +} + +bool MorphOSScaler::Prepare(BitMap *render_bmap) +{ + handle = LockBitMapTags(render_bmap, LBMI_BYTESPERPIX, &dest_bpp, + LBMI_BYTESPERROW, &dest_pitch, + LBMI_BASEADDRESS, &dest, + LBMI_PIXFMT, &dest_pixfmt, + TAG_DONE); + + return handle != NULL; +} + +void MorphOSScaler::Finish() +{ + if (handle) + { + UnLockBitMap(handle); + handle = NULL; + } +} + +uint32 MorphOSScaler::MakeColor(int pixfmt, int r, int g, int b) +{ + uint32 col = 0; + + switch (pixfmt) + { + case PIXFMT_RGB15: + case PIXFMT_RGB15PC: + col = (((r*31)/255) << 10) | (((g*31)/255) << 5) | ((b*31)/255); + break; + + case PIXFMT_BGR15: + case PIXFMT_BGR15PC: + col = (((b*31)/255) << 10) | (((g*31)/255) << 5) | ((r*31)/255); + break; + + case PIXFMT_RGB16: + case PIXFMT_RGB16PC: + col = (((r*31)/255) << 11) | (((g*63)/255) << 5) | ((b*31)/255); + break; + + case PIXFMT_BGR16: + case PIXFMT_BGR16PC: + col = (((b*31)/255) << 11) | (((g*63)/255) << 5) | ((r*31)/255); + break; + + case PIXFMT_ARGB32: + case PIXFMT_BGRA32: + col = (r << 16) | (g << 8) | b; + break; + + case PIXFMT_RGBA32: + col = (r << 24) | (g << 16) | (b << 8); + break; + + case PIXFMT_RGB24: + case PIXFMT_BGR24: + error("The scaling engines do not support 24 bit modes at the moment"); + break; + + default: + error("Unsupported pixel format: %d. Please contact author at tomjoad@muenster.de", pixfmt); + } + + return col; +} + +void Super2xSaIScaler::Scale(uint32 src_x, uint32 src_y, uint32 dest_x, uint32 dest_y, uint32 width, uint32 height) +{ + unsigned int x, y; + unsigned long color[16]; + byte *src; + + if (!handle) + return; + + src = ((byte *)Buffer)+src_y*BufferWidth+src_x; + + /* Point to the first 3 lines. */ + src_line[0] = src; + src_line[1] = src; + src_line[2] = src + BufferWidth; + src_line[3] = src + BufferWidth * 2; + + dst_line[0] = dest+dest_y*2*dest_pitch+dest_x*2*dest_bpp; + dst_line[1] = dst_line[0]+dest_pitch; + + if (PixelsPerMask == 2) + { + byte *sbp; + sbp = src_line[0]; + color[0] = ScummColors16[*sbp]; color[1] = color[0]; color[2] = color[0]; color[3] = color[0]; + color[4] = color[0]; color[5] = color[0]; color[6] = ScummColors16[*(sbp+1)]; color[7] = ScummColors16[*(sbp+2)]; + sbp = src_line[2]; + color[8] = ScummColors16[*sbp]; color[9] = color[8]; color[10] = ScummColors16[*(sbp+1)]; color[11] = ScummColors16[*(sbp+2)]; + sbp = src_line[3]; + color[12] = ScummColors16[*sbp]; color[13] = color[12]; color[14] = ScummColors16[*(sbp+1)]; color[15] = ScummColors16[*(sbp+2)]; + } + else + { + byte *lbp; + lbp = src_line[0]; + color[0] = ScummColors[*lbp]; color[1] = color[0]; color[2] = color[0]; color[3] = color[0]; + color[4] = color[0]; color[5] = color[0]; color[6] = ScummColors[*(lbp+1)]; color[7] = ScummColors[*(lbp+2)]; + lbp = src_line[2]; + color[8] = ScummColors[*lbp]; color[9] = color[8]; color[10] = ScummColors[*(lbp+1)]; color[11] = ScummColors[*(lbp+2)]; + lbp = src_line[3]; + color[12] = ScummColors[*lbp]; color[13] = color[12]; color[14] = ScummColors[*(lbp+1)]; color[15] = ScummColors[*(lbp+2)]; + } + + for (y = 0; y < height; y++) + { + for (x = 0; x < width; x++) + { + unsigned long product1a, product1b, product2a, product2b; + +//--------------------------------------- B0 B1 B2 B3 0 1 2 3 +// 4 5* 6 S2 -> 4 5* 6 7 +// 1 2 3 S1 8 9 10 11 +// A0 A1 A2 A3 12 13 14 15 +//-------------------------------------- + if (color[9] == color[6] && color[5] != color[10]) + { + product2b = color[9]; + product1b = product2b; + } + else if (color[5] == color[10] && color[9] != color[6]) + { + product2b = color[5]; + product1b = product2b; + } + else if (color[5] == color[10] && color[9] == color[6]) + { + int r = 0; + + r += GET_RESULT(color[6], color[5], color[8], color[13]); + r += GET_RESULT(color[6], color[5], color[4], color[1]); + r += GET_RESULT(color[6], color[5], color[14], color[11]); + r += GET_RESULT(color[6], color[5], color[2], color[7]); + + if (r > 0) + product1b = color[6]; + else if (r < 0) + product1b = color[5]; + else + product1b = INTERPOLATE(color[5], color[6]); + + product2b = product1b; + + } + else + { + if (color[6] == color[10] && color[10] == color[13] && color[9] != color[14] && color[10] != color[12]) + product2b = Q_INTERPOLATE(color[10], color[10], color[10], color[9]); + else if (color[5] == color[9] && color[9] == color[14] && color[13] != color[10] && color[9] != color[15]) + product2b = Q_INTERPOLATE(color[9], color[9], color[9], color[10]); + else + product2b = INTERPOLATE(color[9], color[10]); + + if (color[6] == color[10] && color[6] == color[1] && color[5] != color[2] && color[6] != color[0]) + product1b = Q_INTERPOLATE(color[6], color[6], color[6], color[5]); + else if (color[5] == color[9] && color[5] == color[2] && color[1] != color[6] && color[5] != color[3]) + product1b = Q_INTERPOLATE(color[6], color[5], color[5], color[5]); + else + product1b = INTERPOLATE(color[5], color[6]); + } + + if (color[5] == color[10] && color[9] != color[6] && color[4] == color[5] && color[5] != color[14]) + product2a = INTERPOLATE(color[9], color[5]); + else if (color[5] == color[8] && color[6] == color[5] && color[4] != color[9] && color[5] != color[12]) + product2a = INTERPOLATE(color[9], color[5]); + else + product2a = color[9]; + + if (color[9] == color[6] && color[5] != color[10] && color[8] == color[9] && color[9] != color[2]) + product1a = INTERPOLATE(color[9], color[5]); + else if (color[4] == color[9] && color[10] == color[9] && color[8] != color[5] && color[9] != color[0]) + product1a = INTERPOLATE(color[9], color[5]); + else + product1a = color[5]; + + if (PixelsPerMask == 2) + { + if (ScummPCMode) + { + SWAP_WORD(product1a); + SWAP_WORD(product1b); + SWAP_WORD(product2a); + SWAP_WORD(product2b); + } + *((unsigned long *) (&dst_line[0][x * 4])) = (product1a << 16) | product1b; + *((unsigned long *) (&dst_line[1][x * 4])) = (product2a << 16) | product2b; + } + else + { + if (ScummPCMode) + { + SWAP_LONG(product1a); + SWAP_LONG(product1b); + SWAP_LONG(product2a); + SWAP_LONG(product2b); + } + *((unsigned long *) (&dst_line[0][x * 8])) = product1a; + *((unsigned long *) (&dst_line[0][x * 8 + 4])) = product1b; + *((unsigned long *) (&dst_line[1][x * 8])) = product2a; + *((unsigned long *) (&dst_line[1][x * 8 + 4])) = product2b; + } + + /* Move color matrix forward */ + color[0] = color[1]; color[4] = color[5]; color[8] = color[9]; color[12] = color[13]; + color[1] = color[2]; color[5] = color[6]; color[9] = color[10]; color[13] = color[14]; + color[2] = color[3]; color[6] = color[7]; color[10] = color[11]; color[14] = color[15]; + + if (src_x+x < BufferWidth-3) + { + x += 3; + if (PixelsPerMask == 2) + { + color[3] = ScummColors16[*(src_line[0] + x) ]; + color[7] = ScummColors16[*(src_line[1] + x) ]; + color[11] = ScummColors16[*(src_line[2] + x) ]; + color[15] = ScummColors16[*(src_line[3] + x) ]; + } + else + { + color[3] = ScummColors[*(src_line[0] + x)]; + color[7] = ScummColors[*(src_line[1] + x)]; + color[11] = ScummColors[*(src_line[2] + x)]; + color[15] = ScummColors[*(src_line[3] + x)]; + } + x -= 3; + } + } + + /* We're done with one line, so we shift the source lines up */ + src_line[0] = src_line[1]; + src_line[1] = src_line[2]; + src_line[2] = src_line[3]; + + /* Read next line */ + if (src_y + y + 3 >= BufferHeight) + src_line[3] = src_line[2]; + else + src_line[3] = src_line[2] + BufferWidth; + + /* Then shift the color matrix up */ + if (PixelsPerMask == 2) + { + byte *sbp; + sbp = src_line[0]; + color[0] = ScummColors16[*sbp]; color[1] = color[0]; color[2] = ScummColors16[ *(sbp + 1) ]; color[3] = ScummColors16[*(sbp + 2)]; + sbp = src_line[1]; + color[4] = ScummColors16[*sbp]; color[5] = color[4]; color[6] = ScummColors16[ *(sbp + 1) ]; color[7] = ScummColors16[*(sbp + 2)]; + sbp = src_line[2]; + color[8] = ScummColors16[*sbp]; color[9] = color[8]; color[10] = ScummColors16[ *(sbp + 1) ]; color[11] = ScummColors16[*(sbp + 2)]; + sbp = src_line[3]; + color[12] = ScummColors16[*sbp]; color[13] = color[12]; color[14] = ScummColors16[ *(sbp + 1) ]; color[15] = ScummColors16[*(sbp + 2)]; + + if (src_x + x > 0) + { + color[0] = ScummColors16[src_line[0][-1]]; + color[4] = ScummColors16[src_line[1][-1]]; + color[8] = ScummColors16[src_line[2][-1]]; + color[12] = ScummColors16[src_line[3][-1]]; + } + } + else + { + byte *lbp; + lbp = src_line[0]; + color[0] = ScummColors[*lbp]; color[1] = color[0]; color[2] = ScummColors[ *(lbp + 1) ]; color[3] = ScummColors[*(lbp+2)]; + lbp = src_line[1]; + color[4] = ScummColors[*lbp]; color[5] = color[4]; color[6] = ScummColors[ *(lbp + 1) ]; color[7] = ScummColors[*(lbp+2)]; + lbp = src_line[2]; + color[8] = ScummColors[*lbp]; color[9] = color[8]; color[10] = ScummColors[ *(lbp + 1) ]; color[11] = ScummColors[*(lbp+2)]; + lbp = src_line[3]; + color[12] = ScummColors[*lbp]; color[13] = color[12]; color[14] = ScummColors[ *(lbp + 1) ]; color[15] = ScummColors[*(lbp+2)]; + } + + if (src_y + y < BufferHeight - 1) + { + dst_line[0] = dst_line[1]+dest_pitch; + dst_line[1] = dst_line[0]+dest_pitch; + } + } +} + +void SuperEagleScaler::Scale(uint32 src_x, uint32 src_y, uint32 dest_x, uint32 dest_y, uint32 width, uint32 height) +{ + unsigned int x, y; + unsigned long color[12]; + byte *src; + + if (!handle) + return; + + src = (byte *)Buffer+src_y*BufferWidth+src_x; + + /* Point to the first 3 lines. */ + src_line[0] = src; + src_line[1] = src; + src_line[2] = src + BufferWidth; + src_line[3] = src + BufferWidth * 2; + + dst_line[0] = dest+dest_y*2*dest_pitch+dest_x*2*dest_bpp; + dst_line[1] = dst_line[0]+dest_pitch; + + x = 0, y = 0; + + if (PixelsPerMask == 2) + { + byte *sbp; + sbp = src_line[0]; + color[0] = ScummColors16[*sbp]; color[1] = color[0]; color[2] = color[0]; color[3] = color[0]; + color[4] = ScummColors16[*(sbp+1)]; color[5] = ScummColors16[*(sbp+2)]; + sbp = src_line[2]; + color[6] = ScummColors16[*sbp]; color[7] = color[6]; color[8] = ScummColors16[*(sbp+1)]; color[9] = ScummColors16[*(sbp+2)]; + sbp = src_line[3]; + color[10] = ScummColors16[*sbp]; color[11] = ScummColors16[*(sbp+1)]; + } + else + { + byte *lbp; + lbp = src_line[0]; + color[0] = ScummColors[*lbp]; color[1] = color[0]; color[2] = color[0]; color[3] = color[0]; + color[4] = ScummColors[*(lbp+1)]; color[5] = ScummColors[*(lbp+2)]; + lbp = src_line[2]; + color[6] = ScummColors[*lbp]; color[7] = color[6]; color[8] = ScummColors[*(lbp+1)]; color[9] = ScummColors[*(lbp+2)]; + lbp = src_line[3]; + color[10] = ScummColors[*lbp]; color[11] = ScummColors[*(lbp+1)]; + } + + for (y = 0; y < height; y++) + { + /* Todo: x = width - 2, x = width - 1 */ + + for (x = 0; x < width; x++) + { + unsigned long product1a, product1b, product2a, product2b; + +//--------------------------------------- B1 B2 0 1 +// 4 5 6 S2 -> 2 3 4 5 +// 1 2 3 S1 6 7 8 9 +// A1 A2 10 11 + + if (color[7] == color[4] && color[3] != color[8]) + { + product1b = product2a = color[7]; + + if ((color[6] == color[7]) || (color[4] == color[1])) + product1a = INTERPOLATE(color[7], INTERPOLATE(color[7], color[3])); + else + product1a = INTERPOLATE(color[3], color[4]); + + if ((color[4] == color[5]) || (color[7] == color[10])) + product2b = INTERPOLATE(color[7], INTERPOLATE(color[7], color[8])); + else + product2b = INTERPOLATE(color[7], color[8]); + } + else if (color[3] == color[8] && color[7] != color[4]) + { + product2b = product1a = color[3]; + + if ((color[0] == color[3]) || (color[5] == color[9])) + product1b = INTERPOLATE(color[3], INTERPOLATE(color[3], color[4])); + else + product1b = INTERPOLATE(color[3], color[1]); + + if ((color[8] == color[11]) || (color[2] == color[3])) + product2a = INTERPOLATE(color[3], INTERPOLATE(color[3], color[2])); + else + product2a = INTERPOLATE(color[7], color[8]); + + } + else if (color[3] == color[8] && color[7] == color[4]) + { + register int r = 0; + + r += GET_RESULT(color[4], color[3], color[6], color[10]); + r += GET_RESULT(color[4], color[3], color[2], color[0]); + r += GET_RESULT(color[4], color[3], color[11], color[9]); + r += GET_RESULT(color[4], color[3], color[1], color[5]); + + if (r > 0) + { + product1b = product2a = color[7]; + product1a = product2b = INTERPOLATE(color[3], color[4]); + } + else if (r < 0) + { + product2b = product1a = color[3]; + product1b = product2a = INTERPOLATE(color[3], color[4]); + } + else + { + product2b = product1a = color[3]; + product1b = product2a = color[7]; + } + } + else + { + product2b = product1a = INTERPOLATE(color[7], color[4]); + product2b = Q_INTERPOLATE(color[8], color[8], color[8], product2b); + product1a = Q_INTERPOLATE(color[3], color[3], color[3], product1a); + + product2a = product1b = INTERPOLATE(color[3], color[8]); + product2a = Q_INTERPOLATE(color[7], color[7], color[7], product2a); + product1b = Q_INTERPOLATE(color[4], color[4], color[4], product1b); + } + + if (PixelsPerMask == 2) + { + if (ScummPCMode) + { + SWAP_WORD(product1a); + SWAP_WORD(product1b); + SWAP_WORD(product2a); + SWAP_WORD(product2b); + } + *((unsigned long *) (&dst_line[0][x * 4])) = (product1a << 16) | product1b; + *((unsigned long *) (&dst_line[1][x * 4])) = (product2a << 16) | product2b; + } + else + { + if (ScummPCMode) + { + SWAP_LONG(product1a); + SWAP_LONG(product1b); + SWAP_LONG(product2a); + SWAP_LONG(product2b); + } + *((unsigned long *) (&dst_line[0][x * 8])) = product1a; + *((unsigned long *) (&dst_line[0][x * 8 + 4])) = product1b; + *((unsigned long *) (&dst_line[1][x * 8])) = product2a; + *((unsigned long *) (&dst_line[1][x * 8 + 4])) = product2b; + } + + /* Move color matrix forward */ + color[0] = color[1]; + color[2] = color[3]; color[3] = color[4]; color[4] = color[5]; + color[6] = color[7]; color[7] = color[8]; color[8] = color[9]; + color[10] = color[11]; + + if (src_x+x < BufferWidth - 2) + { + x += 2; + if (PixelsPerMask == 2) + { + color[1] = ScummColors16[ *(src_line[0] + x) ]; + if (src_x+x < BufferWidth-1) + { + color[5] = ScummColors16[*(src_line[1]+x+1)]; + color[9] = ScummColors16[*(src_line[2]+x+1)]; + } + color[11] = ScummColors16[*(src_line[3]+x)]; + } + else + { + color[1] = ScummColors[*(src_line[0]+x)]; + if (src_x+x < BufferWidth-1) + { + color[5] = ScummColors[*(src_line[1]+x+1)]; + color[9] = ScummColors[ *(src_line[2]+x+1)]; + } + color[11] = ScummColors[*(src_line[3]+x)]; + } + x -= 2; + } + } + + /* We're done with one line, so we shift the source lines up */ + src_line[0] = src_line[1]; + src_line[1] = src_line[2]; + src_line[2] = src_line[3]; + + /* Read next line */ + if (src_y+y+3 >= BufferHeight) + src_line[3] = src_line[2]; + else + src_line[3] = src_line[2] + BufferWidth; + + /* Then shift the color matrix up */ + if (PixelsPerMask == 2) + { + byte *sbp; + sbp = src_line[0]; + color[0] = ScummColors16[*sbp]; color[1] = ScummColors16[*(sbp+1)]; + sbp = src_line[1]; + color[2] = ScummColors16[*sbp]; color[3] = color[2]; color[4] = ScummColors16[*(sbp+1)]; color[5] = ScummColors16[*(sbp+2)]; + sbp = src_line[2]; + color[6] = ScummColors16[*sbp]; color[7] = color[6]; color[8] = ScummColors16[*(sbp+1)]; color[9] = ScummColors16[*(sbp+2)]; + sbp = src_line[3]; + color[10] = ScummColors16[*sbp]; color[11] = ScummColors16[*(sbp+1)]; + } + else + { + byte *lbp; + lbp = src_line[0]; + color[0] = ScummColors[*lbp]; color[1] = ScummColors[*(lbp+1)]; + lbp = src_line[1]; + color[2] = ScummColors[*lbp]; color[3] = color[2]; color[4] = ScummColors[*(lbp+1)]; color[5] = ScummColors[*(lbp+2)]; + lbp = src_line[2]; + color[6] = ScummColors[*lbp]; color[7] = color[6]; color[8] = ScummColors[*(lbp+1)]; color[9] = ScummColors[*(lbp+2)]; + lbp = src_line[3]; + color[10] = ScummColors[*lbp]; color[11] = ScummColors[*(lbp+1)]; + } + + + if (src_y + y < BufferHeight - 1) + { + dst_line[0] = dst_line[1]+dest_pitch; + dst_line[1] = dst_line[0]+dest_pitch; + } + } +} + +void AdvMame2xScaler::Scale(uint32 src_x, uint32 src_y, uint32 dest_x, uint32 dest_y, uint32 width, uint32 height) +{ + if (!handle) + return; + + byte *src = (byte *)Buffer+src_y*BufferWidth+src_x; + + src_line[0] = src; + src_line[1] = src; + src_line[2] = src + BufferWidth; + + dst_line[0] = dest+dest_y*2*dest_pitch+dest_x*2*dest_bpp; + dst_line[1] = dst_line[0]+dest_pitch; + + for (uint32 y = 0; y < height; y++) + { + for (uint32 x = 0; x < width; x++) + { + uint32 B, D, E, F, H; + + if (PixelsPerMask == 2) + { + // short A = *(src + i - nextlineSrc - 1); + B = ScummColors16[src_line[0][x]]; + // short C = *(src + i - nextlineSrc + 1); + D = ScummColors16[src_line[1][x-1]]; + E = ScummColors16[src_line[1][x]]; + F = ScummColors16[src_line[1][x+1]]; + // short G = *(src + i + nextlineSrc - 1); + H = ScummColors16[src_line[2][x]]; + // short I = *(src + i + nextlineSrc + 1); + } + else + { + // short A = *(src + i - nextlineSrc - 1); + B = ScummColors[src_line[0][x]]; + // short C = *(src + i - nextlineSrc + 1); + D = ScummColors[src_line[1][x-1]]; + E = ScummColors[src_line[1][x]]; + F = ScummColors[src_line[1][x+1]]; + // short G = *(src + i + nextlineSrc - 1); + H = ScummColors[src_line[2][x]]; + // short I = *(src + i + nextlineSrc + 1); + } + + + if (PixelsPerMask == 2) + { + if (ScummPCMode) + { + SWAP_WORD(B); + SWAP_WORD(D); + SWAP_WORD(E); + SWAP_WORD(F); + SWAP_WORD(H); + } + *((unsigned long *) (&dst_line[0][x * 4])) = ((D == B && B != F && D != H ? D : E) << 16) | (B == F && B != D && F != H ? F : E); + *((unsigned long *) (&dst_line[1][x * 4])) = ((D == H && D != B && H != F ? D : E) << 16) | (H == F && D != H && B != F ? F : E); + } + else + { + if (ScummPCMode) + { + SWAP_LONG(B); + SWAP_LONG(D); + SWAP_LONG(E); + SWAP_LONG(F); + SWAP_LONG(H); + } + *((unsigned long *) (&dst_line[0][x * 8])) = D == B && B != F && D != H ? D : E; + *((unsigned long *) (&dst_line[0][x * 8 + 4])) = B == F && B != D && F != H ? F : E; + *((unsigned long *) (&dst_line[1][x * 8])) = D == H && D != B && H != F ? D : E; + *((unsigned long *) (&dst_line[1][x * 8 + 4])) = H == F && D != H && B != F ? F : E; + } + } + + src_line[0] = src_line[1]; + src_line[1] = src_line[2]; + if (src_y+y+2 >= BufferHeight) + src_line[2] = src_line[1]; + else + src_line[2] = src_line[1] + BufferWidth; + + if (src_y+y < BufferHeight-1) + { + dst_line[0] = dst_line[1]+dest_pitch; + dst_line[1] = dst_line[0]+dest_pitch; + } + } +} + +void PointScaler::Scale(uint32 src_x, uint32 src_y, uint32 dest_x, uint32 dest_y, uint32 width, uint32 height) +{ + byte *src; + uint32 color; + uint32 r, g, b; + uint32 x, y; + + if (!handle) + return; + + src = (byte *)Buffer+src_y*BufferWidth+src_x; + + dst_line[0] = dest+dest_y*2*dest_pitch+dest_x*2*dest_bpp; + dst_line[1] = dst_line[0]+dest_pitch; + + for (y = 0; y < height; y++) + { + for (x = 0; x < width; x++) + { + r = (ScummColors[*(src+x)] >> 16) & 0xff; + g = (ScummColors[*(src+x)] >> 8) & 0xff; + b = ScummColors[*(src+x)] & 0xff; + + color = MakeColor(dest_pixfmt, r, g, b); + if (PixelsPerMask == 2) + { + if (ScummPCMode) + SWAP_WORD(color); + + *((unsigned long *) (&dst_line[0][x * 4])) = (color << 16) | color; + *((unsigned long *) (&dst_line[1][x * 4])) = (color << 16) | color; + } + else + { + if (ScummPCMode) + SWAP_LONG(color); + + *((unsigned long *) (&dst_line[0][x * 8])) = color; + *((unsigned long *) (&dst_line[0][x * 8 + 4])) = color; + *((unsigned long *) (&dst_line[1][x * 8])) = color; + *((unsigned long *) (&dst_line[1][x * 8 + 4])) = color; + } + } + + src += BufferWidth; + + if (src_y+y < BufferHeight-1) + { + dst_line[0] = dst_line[1]+dest_pitch; + dst_line[1] = dst_line[0]+dest_pitch; + } + } +} + +SCALERTYPE MorphOSScaler::FindByName(const char *ScalerName) +{ + int scaler = 0; + + while (ScummScalers[scaler].gs_Name) + { + if (!stricmp(ScalerName, ScummScalers[scaler].gs_Name)) + return ScummScalers[scaler].gs_Type; + scaler++; + } + + if (ScummScalers[scaler].gs_Name == NULL) + { + puts("Invalid scaler name. Please use one of the following:"); + for (scaler = 0; ScummScalers[scaler].gs_Name != NULL; scaler++) + printf(" %s\n", ScummScalers[scaler].gs_Name); + } + + return ST_INVALID; +} + +SCALERTYPE MorphOSScaler::FindByIndex(int index) +{ + if (index >= 0 && index < 10 && ScummScalers[index].gs_Name) + return ScummScalers[index].gs_Type; + + return ST_INVALID; +} + +const char *MorphOSScaler::GetParamName(SCALERTYPE type) +{ + int scaler = 0; + + while (ScummScalers[scaler].gs_Name) + { + if (ScummScalers[scaler].gs_Type == type) + return ScummScalers[scaler].gs_ParamName; + scaler++; + } + + return NULL; +} + diff --git a/backends/platform/morphos/morphos_scaler.h b/backends/platform/morphos/morphos_scaler.h new file mode 100644 index 0000000000..d794e361a6 --- /dev/null +++ b/backends/platform/morphos/morphos_scaler.h @@ -0,0 +1,94 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2002-2006 The ScummVM Team + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#ifndef MORPHOS_MORPHOSSCALER_H +#define MORPHOS_MORPHOSSCALER_H + +#include <graphics/gfx.h> + +typedef enum { ST_INVALID = 0, ST_NONE, ST_POINT, ST_ADVMAME2X, ST_SUPEREAGLE, ST_SUPER2XSAI } SCALERTYPE; + +class MorphOSScaler +{ + public: + MorphOSScaler(APTR buffer, int width, int height, ULONG *col_table, UWORD *col_table16, BitMap *bmap); + virtual ~MorphOSScaler(); + + bool Prepare(BitMap *render_bmap); + void Finish(); + + virtual void Scale(uint32 src_x, uint32 src_y, uint32 dest_x, uint32 dest_y, uint32 width, uint32 height) = 0; + + static MorphOSScaler *Create(SCALERTYPE st, APTR buffer, int width, int height, ULONG *col_table, UWORD *col_table16, BitMap *bmap); + + static SCALERTYPE FindByName(const char *ScalerName); + static SCALERTYPE FindByIndex(int index); + static const char *GetParamName(SCALERTYPE type); + + protected: + struct GfxScaler + { + STRPTR gs_Name; + STRPTR gs_ParamName; + SCALERTYPE gs_Type; + }; + + static GfxScaler ScummScalers[11]; + + static uint32 MakeColor(int pixfmt, int r, int g, int b); + + byte *dest; + uint32 dest_bpp; + uint32 dest_pitch; + uint32 dest_pixfmt; + APTR handle; + + uint32 colorMask; + uint32 lowPixelMask; + uint32 qcolorMask; + uint32 qlowpixelMask; + uint32 redblueMask; + uint32 greenMask; + int PixelsPerMask; + byte *src_line[4]; + byte *dst_line[2]; + bool ScummPCMode; + + APTR Buffer; + ULONG BufferWidth; + ULONG BufferHeight; + ULONG *ScummColors; + USHORT *ScummColors16; +}; + +#define DECLARE_SCALER(scaler_name) class scaler_name ## Scaler : public MorphOSScaler \ + { public: scaler_name ## Scaler(APTR buffer, int width, int height, ULONG *col_table, UWORD *col_table16, BitMap *bmap) : MorphOSScaler(buffer, width, height, col_table, col_table16, bmap) {} \ + void Scale(uint32 src_x, uint32 src_y, uint32 dest_x, uint32 dest_y, uint32 width, uint32 height); \ + }; + + +DECLARE_SCALER(Point) +DECLARE_SCALER(AdvMame2x) +DECLARE_SCALER(SuperEagle) +DECLARE_SCALER(Super2xSaI) + +#endif + diff --git a/backends/platform/morphos/morphos_sound.cpp b/backends/platform/morphos/morphos_sound.cpp new file mode 100644 index 0000000000..c5c2f3b7ad --- /dev/null +++ b/backends/platform/morphos/morphos_sound.cpp @@ -0,0 +1,254 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2002 Rüdiger Hanke + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * MorphOS sound support + * + * $URL$ + * $Id$ + * + */ + +#include "common/stdafx.h" +#include "base/engine.h" + +#include <dos/dos.h> +#include <exec/memory.h> +#include <devices/ahi.h> +#include <devices/etude.h> + +#include <clib/alib_protos.h> +#include <proto/exec.h> +#include <proto/dos.h> +#include <proto/ahi.h> + +#include "morphos.h" +#include "morphos_sound.h" + +#define AHI_BUF_SIZE (8*1024) + +SignalSemaphore ScummMusicThreadRunning; +SignalSemaphore ScummSoundThreadRunning; + +static MsgPort *ahiPort = NULL; +static AHIRequest *ahiReq[2] = { NULL, NULL }; +static UWORD ahiCurBuf = 0; +static bool ahiReqSent[2] = { false, false }; +static BYTE ahiDevice = -1; + UBYTE ahiUnit = AHI_DEFAULT_UNIT; +static char *ahiBuf[2] = { NULL, NULL }; + +static MsgPort *ScummMidiPort = NULL; + IOMidiRequest *ScummMidiRequest = NULL; + + Device *EtudeBase = NULL; + +bool etude_available() +{ + bool avail = init_morphos_music(ScummMidiUnit, ETUDEF_DIRECT); + if (avail) + exit_morphos_music(); + return avail; +} + +bool init_morphos_music(ULONG MidiUnit, ULONG DevFlags) +{ + ScummMidiPort = CreateMsgPort(); + if (ScummMidiPort) + { + ScummMidiRequest = (IOMidiRequest *) CreateIORequest(ScummMidiPort, sizeof (IOMidiRequest)); + if (ScummMidiRequest) + { + ScummMidiRequest->emr_Version = 1; + if (OpenDevice(ETUDENAME, MidiUnit, (IORequest *) ScummMidiRequest, DevFlags)) + { + DeleteIORequest((IORequest *) ScummMidiRequest); + DeleteMsgPort(ScummMidiPort); + ScummMidiRequest = NULL; + ScummMidiPort = NULL; + } + else + EtudeBase = ScummMidiRequest->emr_Std.io_Device; + } + else + { + DeleteMsgPort(ScummMidiPort); + ScummMidiPort = NULL; + } + } + + if (!ScummMidiRequest) + return false; + + return true; +} + + +void exit_morphos_music() +{ + if (ScummMidiRequest) + { + CloseDevice((IORequest *) ScummMidiRequest); + DeleteIORequest((IORequest *) ScummMidiRequest); + DeleteMsgPort(ScummMidiPort); + ScummMidiRequest = NULL; + ScummMidiPort = NULL; + EtudeBase = NULL; + } +} + + +static bool init_morphos_sound() +{ + if (!(ahiPort = CreateMsgPort())) + return false; + + if (!(ahiReq[0] = (AHIRequest *) CreateIORequest(ahiPort, sizeof (AHIRequest)))) + { + DeleteMsgPort(ahiPort); + ahiPort = NULL; + return false; + } + + if (!(ahiReq[1] = (AHIRequest *) AllocVec(sizeof (AHIRequest), MEMF_PUBLIC))) + { + DeleteIORequest(ahiReq[0]); + DeleteMsgPort(ahiPort); + ahiReq[0] = NULL; + ahiPort = NULL; + return false; + } + + if (!(ahiBuf[0] = (char *) AllocVec(2*AHI_BUF_SIZE, MEMF_PUBLIC))) + { + FreeVec(ahiReq[1]); + DeleteIORequest(ahiReq[0]); + DeleteMsgPort(ahiPort); + ahiReq[0] = NULL; + ahiReq[1] = NULL; + ahiPort = NULL; + return false; + } + ahiBuf[1] = &ahiBuf[0][AHI_BUF_SIZE]; + + ahiReq[0]->ahir_Version = 4; + if ((ahiDevice = OpenDevice(AHINAME, 0, (IORequest *) ahiReq[0], 0))) + { + FreeVec(ahiBuf[0]); + FreeVec(ahiReq[1]); + DeleteIORequest(ahiReq[0]); + DeleteMsgPort(ahiPort); + ahiBuf[0] = NULL; + ahiReq[0] = NULL; + ahiReq[1] = NULL; + ahiPort = NULL; + return false; + } + + CopyMem(ahiReq[0], ahiReq[1], sizeof (AHIRequest)); + + ahiCurBuf = 0; + ahiReqSent[0] = FALSE; + ahiReqSent[1] = FALSE; + + return true; +} + + +static void exit_morphos_sound() +{ + if (ahiReq[1]) + FreeVec(ahiReq[1]); + + if (ahiReq[0]) + { + CloseDevice((IORequest *) ahiReq[0]); + DeleteIORequest(ahiReq[0]); + } + + if (ahiBuf[0]) + FreeVec((APTR) ahiBuf[0]); + + if (ahiPort) + DeleteMsgPort(ahiPort); +} + +int morphos_sound_thread(OSystem_MorphOS *syst, ULONG SampleType) +{ + ULONG signals; + bool initialized; + + initialized = init_morphos_sound(); + if (!initialized) + { + warning("Sound could not be initialized. The game may hang at some point (press Ctrl-z then)."); + Wait(SIGBREAKF_CTRL_C); + } + else + { + for (;;) + { + while (!ahiReqSent[ahiCurBuf] || CheckIO((IORequest *) ahiReq[ahiCurBuf])) + { + AHIRequest *req = ahiReq[ahiCurBuf]; + UWORD ahiOtherBuf = !ahiCurBuf; + + if (ahiReqSent[ahiCurBuf]) + WaitIO((IORequest *) req); + + syst->fill_sound((byte *) ahiBuf[ahiCurBuf], AHI_BUF_SIZE); + + req->ahir_Std.io_Message.mn_Node.ln_Pri = 0; + req->ahir_Std.io_Command = CMD_WRITE; + req->ahir_Std.io_Data = ahiBuf[ahiCurBuf]; + req->ahir_Std.io_Length = AHI_BUF_SIZE; + req->ahir_Type = SampleType; + req->ahir_Frequency = SAMPLES_PER_SEC; + req->ahir_Position = 0x8000; + req->ahir_Volume = 0x10000; + req->ahir_Link = (ahiReqSent[ahiOtherBuf] && !CheckIO((IORequest *) ahiReq[ahiOtherBuf])) ? ahiReq[ahiOtherBuf] : NULL; + SendIO((IORequest *)req); + + ahiReqSent[ahiCurBuf] = true; + ahiCurBuf = ahiOtherBuf; + } + + signals = Wait(SIGBREAKF_CTRL_C | (1 << ahiPort->mp_SigBit)); + + if (signals & SIGBREAKF_CTRL_C) + break; + } + + if (ahiReqSent[ahiCurBuf]) + { + AbortIO((IORequest *) ahiReq[ahiCurBuf]); + WaitIO((IORequest *) ahiReq[ahiCurBuf]); + ahiReqSent[ahiCurBuf] = false; + } + + if (ahiReqSent[!ahiCurBuf]) + { + AbortIO((IORequest *) ahiReq[!ahiCurBuf]); + WaitIO((IORequest *) ahiReq[!ahiCurBuf]); + ahiReqSent[!ahiCurBuf] = false; + } + } + + exit_morphos_sound(); + + return 0; +} + diff --git a/backends/platform/morphos/morphos_sound.h b/backends/platform/morphos/morphos_sound.h new file mode 100644 index 0000000000..a2d211a37f --- /dev/null +++ b/backends/platform/morphos/morphos_sound.h @@ -0,0 +1,43 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2002 Rüdiger Hanke (MorphOS port) + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * MorphOS-specific header file + * + * $URL$ + * $Id$ + * + */ + +#ifndef MORPHOS_SOUND_H +#define MORPHOS_SOUND_H + +#include <exec/semaphores.h> +#include <devices/etude.h> + +class OSystem_MorphOS; + +int morphos_sound_thread(OSystem_MorphOS *syst, ULONG SampleType); +bool init_morphos_music(ULONG MidiUnit, ULONG DevFlags); +void exit_morphos_music(); +bool etude_available(); + +extern STRPTR ScummMusicDriver; +extern LONG ScummMidiUnit; +extern IOMidiRequest *ScummMidiRequest; + +#endif + diff --git a/backends/platform/morphos/morphos_start.cpp b/backends/platform/morphos/morphos_start.cpp new file mode 100644 index 0000000000..ac5c16ad85 --- /dev/null +++ b/backends/platform/morphos/morphos_start.cpp @@ -0,0 +1,444 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2002 Rüdiger Hanke (MorphOS port) + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * MorphOS startup handling + * + * $URL$ + * $Id$ + * + */ + +#include <exec/types.h> +#include <exec/devices.h> +#include <exec/memory.h> +#include <exec/libraries.h> +#include <workbench/startup.h> +#include <workbench/workbench.h> + +#include <proto/exec.h> +#include <proto/dos.h> +#include <proto/cdda.h> +#include <proto/icon.h> + +#include "common/stdafx.h" +#include "scumm/scumm.h" +#include "base/main.h" +#include "common/scaler.h" +#include "sound/mididrv.h" +#include "morphos.h" +#include "morphos_scaler.h" +#include "morphos_sound.h" + +extern "C" WBStartup *_WBenchMsg; + +// For command line parsing +static STRPTR usageTemplate = "STORY,DATAPATH/K,WINDOW/S,SCALER/K,AMIGA/S,MIDIUNIT/K/N,MUSIC/K,MASTERVOL/K/N,MUSICVOL/K/N,SFXVOL/K/N,TEMPO/K/N,TALKSPEED/K/N,LANGUAGE/K,NOSUBTITLES=NST/S, DEBUGLEVEL=DBGLVL/K/N, DUMPSCRIPTS/S"; +typedef enum { USG_STORY = 0, USG_DATAPATH, USG_WINDOW, USG_SCALER, USG_AMIGA, USG_MIDIUNIT, USG_MUSIC, USG_MASTERVOL, USG_MUSICVOL, USG_SFXVOL, USG_TEMPO, USG_TALKSPEED, USG_LANGUAGE, USG_NOSUBTITLES, USG_DEBUGLEVEL, USG_DUMPSCRIPTS, USG_MAX } usageFields; +static LONG args[USG_MAX]; +static RDArgs *ScummArgs = NULL; + +static char*ScummStory = NULL; +static char*ScummPath = NULL; +static char*ScummLang = NULL; + STRPTR ScummMusicDriver = NULL; +MidiDriver* EtudeMidiDriver = NULL; + LONG ScummMidiUnit = 0; +static LONG ScummMasterVolume = 0; +static LONG ScummMidiVolume = 0; +static LONG ScummMidiTempo = 0; +static LONG ScummSfxVolume = 0; +static LONG ScummTalkSpeed = 0; +static LONG ScummDebugLevel = 0; +static SCALERTYPE ScummGfxScaler = ST_INVALID; + +static BPTR OrigDirLock = 0; + +Library *CDDABase = NULL; +Library *TimerBase = NULL; + +OSystem_MorphOS *TheSystem = NULL; + +OSystem *OSystem_MorphOS_create() +{ + if (TheSystem) + delete TheSystem; + + TheSystem = OSystem_MorphOS::create(ST_NONE, ConfMan.getBool("fullscreen")); + + return TheSystem; +} + +void close_resources() +{ + delete TheSystem; + TheSystem = NULL; + + if (ScummPath) + { + FreeVec(ScummPath); + ScummPath = NULL; + } + + if (ScummStory) + { + FreeVec(ScummStory); + ScummStory = NULL; + } + + if (ScummArgs) + { + FreeArgs(ScummArgs); + ScummArgs = NULL; + } + + if (OrigDirLock) + { + CurrentDir(OrigDirLock); + OrigDirLock = NULL; + } + + if (CDDABase) + { + CloseLibrary(CDDABase); + CDDABase = NULL; + } +} + +static STRPTR FindMusicDriver(STRPTR argval) +{ + if (!stricmp(argval, "off")) return "-enull"; + if (!stricmp(argval, "midi")) return "-eetude"; + if (!stricmp(argval, "adlib")) return "-eadlib"; + + error("No such music driver supported. Possible values are off, Midi and Adlib."); + return NULL; +} + +static void ReadToolTypes(WBArg *OfFile) +{ + DiskObject *dobj; + char *ToolValue; + char IconPath[256]; + + NameFromLock(OfFile->wa_Lock, IconPath, 256); + AddPart(IconPath, (STRPTR) OfFile->wa_Name, 256); + + dobj = GetDiskObject(IconPath); + if (dobj == NULL) + return; + + ToolValue = (char *) FindToolType(dobj->do_ToolTypes, "STORY"); + if (ToolValue) + { + if (ScummStory) + FreeVec(ScummStory); + ScummStory = (char *) AllocVec(strlen(ToolValue)+1, MEMF_PUBLIC); + strcpy(ScummStory, ToolValue); + } + + ToolValue = (char *) FindToolType(dobj->do_ToolTypes, "DATAPATH"); + if (ToolValue) + { + if (ScummPath) + FreeVec(ScummPath); + ScummPath = (char *) AllocVec(strlen(ToolValue)+4, MEMF_PUBLIC); + strcpy(ScummPath, "-p"); + strcat(ScummPath, ToolValue); + } + + ToolValue = (char *) FindToolType(dobj->do_ToolTypes, "WINDOW"); + if (ToolValue) + { + if (MatchToolValue(ToolValue, "YES")) + args[USG_WINDOW] = TRUE; + else if (MatchToolValue(ToolValue, "NO")) + args[USG_WINDOW] = FALSE; + } + + ToolValue = (char *) FindToolType(dobj->do_ToolTypes, "SCALER"); + if (ToolValue) + { + if ((ScummGfxScaler = MorphOSScaler::FindByName(ToolValue)) == ST_INVALID) + { + FreeDiskObject(dobj); + exit(1); + } + } + + ToolValue = (char *) FindToolType(dobj->do_ToolTypes, "MUSIC"); + if (ToolValue) + { + if (!(ScummMusicDriver = FindMusicDriver(ToolValue))) + { + FreeDiskObject(dobj); + exit(1); + } + args[USG_MUSIC] = (ULONG) &ScummMusicDriver; + } + + ToolValue = (char *) FindToolType(dobj->do_ToolTypes, "MIDIUNIT"); + if (ToolValue) + ScummMidiUnit = atoi(ToolValue); + + ToolValue = (char *) FindToolType(dobj->do_ToolTypes, "MASTERVOL"); + if (ToolValue) + { + int vol = atoi(ToolValue); + if (vol >= 0 && vol <= 100) + { + ScummMasterVolume = vol; + args[USG_MASTERVOL] = (ULONG) &ScummMasterVolume; + } + } + + ToolValue = (char *) FindToolType(dobj->do_ToolTypes, "MUSICVOL"); + if (ToolValue) + { + int vol = atoi(ToolValue); + if (vol >= 0 && vol <= 100) + { + ScummMidiVolume = vol; + args[USG_MUSICVOL] = (ULONG) &ScummMidiVolume; + } + } + + ToolValue = (char *) FindToolType(dobj->do_ToolTypes, "SFXVOL"); + if (ToolValue) + { + int vol = atoi(ToolValue); + if (vol >= 0 && vol <= 255) + { + ScummSfxVolume = vol; + args[USG_SFXVOL] = (ULONG) &ScummSfxVolume; + } + } + + ToolValue = (char *) FindToolType(dobj->do_ToolTypes, "TEMPO"); + if (ToolValue) + { + ScummMidiTempo = atoi(ToolValue); + args[USG_TEMPO] = (ULONG) &ScummMidiTempo; + } + + ToolValue = (char *) FindToolType(dobj->do_ToolTypes, "TALKSPEED"); + if (ToolValue) + { + ScummTalkSpeed = atoi(ToolValue); + args[USG_TALKSPEED] = (ULONG) &ScummMidiTempo; + } + + ToolValue = (char *) FindToolType(dobj->do_ToolTypes, "LANGUAGE"); + if (ToolValue) + { + if (ScummLang) + FreeVec(ScummLang); + ScummLang = (char *) AllocVec(strlen(ToolValue)+4, MEMF_PUBLIC); + strcpy(ScummLang, "-q"); + strcat(ScummLang, ToolValue); + } + + ToolValue = (char *) FindToolType(dobj->do_ToolTypes, "SUBTITLES"); + if (ToolValue) + { + if (MatchToolValue(ToolValue, "YES")) + args[USG_NOSUBTITLES] = FALSE; + else if (MatchToolValue(ToolValue, "NO")) + args[USG_NOSUBTITLES] = TRUE; + } + + ToolValue = (char *) FindToolType(dobj->do_ToolTypes, "AMIGA"); + if (ToolValue) + { + if (MatchToolValue(ToolValue, "YES")) + args[USG_AMIGA] = FALSE; + else if (MatchToolValue(ToolValue, "NO")) + args[USG_AMIGA] = TRUE; + } + + ToolValue = (char *) FindToolType(dobj->do_ToolTypes, "DEBUGLEVEL"); + if (ToolValue) + ScummDebugLevel = atoi(ToolValue); + + ToolValue = (char *) FindToolType(dobj->do_ToolTypes, "DUMPSCRIPTS"); + if (ToolValue) + { + if (MatchToolValue(ToolValue, "YES")) + args[USG_DUMPSCRIPTS] = TRUE; + else if (MatchToolValue(ToolValue, "NO")) + args[USG_DUMPSCRIPTS] = FALSE; + } + + FreeDiskObject(dobj); +} + +#undef main + +int main() +{ + char *argv[30]; + char mastervol[6], musicvol[6], sfxvol[6], talkspeed[12], tempo[12], scaler[14]; + char dbglvl[6]; + int argc = 0; + + atexit(&close_resources); + + memset(args, '\0', sizeof (args)); + if (_WBenchMsg == NULL) + { + /* Parse the command line here */ + ScummArgs = ReadArgs(usageTemplate, args, NULL); + if (ScummArgs == NULL) + { + puts("Error in command line - type \"ScummVM ?\" for usage."); + exit(1); + } + + if (args[USG_STORY]) + { + ScummStory = (char *) AllocVec(strlen((char *) args[USG_STORY])+1, MEMF_PUBLIC); + strcpy(ScummStory, (char *) args[USG_STORY]); + } + + if (args[USG_DATAPATH]) + { + ScummPath = (char *) AllocVec(strlen((char *) args[USG_DATAPATH])+4, MEMF_PUBLIC); + strcpy(ScummPath, "-p"); + strcat(ScummPath, (char *) args[USG_DATAPATH]); + } + + if (args[USG_SCALER]) + { + if ((ScummGfxScaler = MorphOSScaler::FindByName((char *) args[USG_SCALER])) == ST_INVALID) + exit(1); + } + + if (args[USG_MUSIC]) + { + if (!(ScummMusicDriver = FindMusicDriver((char *) args[USG_MUSIC]))) + exit(1); + } + + if (args[USG_MIDIUNIT]) + ScummMidiUnit = *((LONG *) args[USG_MIDIUNIT]); + + if (args[USG_TEMPO]) + ScummMidiTempo = *((LONG *) args[USG_TEMPO]); + + if (args[USG_MASTERVOL]) + ScummMasterVolume = *((LONG *) args[USG_MASTERVOL]); + + if (args[USG_MUSICVOL]) + ScummMidiVolume = *((LONG *) args[USG_MUSICVOL]); + + if (args[USG_SFXVOL]) + ScummSfxVolume = *((LONG *) args[USG_SFXVOL]); + + if (args[USG_TALKSPEED]) + ScummTalkSpeed = *((LONG *) args[USG_TALKSPEED]); + + if (args[USG_LANGUAGE]) + { + ScummLang = (char *) AllocVec(strlen((char *) args[USG_LANGUAGE])+4, MEMF_PUBLIC); + strcpy(ScummLang, "-q"); + strcat(ScummLang, (char *) args[USG_LANGUAGE]); + } + + if (args[USG_DEBUGLEVEL]) + ScummDebugLevel = *((LONG *) args[USG_DEBUGLEVEL]); + } + else + { + /* We've been started from Workbench */ + ReadToolTypes(&_WBenchMsg->sm_ArgList[0]); + if (_WBenchMsg->sm_NumArgs > 1) + { + ReadToolTypes(&_WBenchMsg->sm_ArgList[1]); + OrigDirLock = CurrentDir(_WBenchMsg->sm_ArgList[1].wa_Lock); + } + } + + if (ScummPath) + { + char c = ScummPath[strlen(ScummPath)-1]; + if (c != '/' && c != ':') + strcat(ScummPath, "/"); + } + + argv[argc++] = "ScummVM"; + if (ScummPath) argv[argc++] = ScummPath; + if (!args[USG_WINDOW]) argv[argc++] = "-f"; + if (args[USG_NOSUBTITLES]) argv[argc++] = "-n"; + if (args[USG_AMIGA]) argv[argc++] = "-a"; + if (args[USG_MUSIC]) argv[argc++] = ScummMusicDriver; + else + { + if (etude_available()) + argv[argc++] = "-eetude"; + else + argv[argc++] = "-eadlib"; + } + if (ScummGfxScaler != ST_INVALID) + { + sprintf(scaler, "-g%s", MorphOSScaler::GetParamName(ScummGfxScaler)); + argv[argc++] = scaler; + } + else + argv[argc++] = "-gsuper2xsai"; + if (args[USG_MASTERVOL] && ScummMasterVolume >= 0 && ScummMasterVolume <= 255) + { + sprintf(mastervol, "-o%ld", ScummMasterVolume); + argv[argc++] = mastervol; + } + if (args[USG_MUSICVOL] && ScummMidiVolume >= 0 && ScummMidiVolume <= 255) + { + sprintf(musicvol, "-m%ld", ScummMidiVolume); + argv[argc++] = musicvol; + } + if (args[USG_SFXVOL] && ScummSfxVolume >= 0 && ScummSfxVolume <= 255) + { + sprintf(sfxvol, "-s%ld", ScummSfxVolume); + argv[argc++] = sfxvol; + } + if (args[USG_TEMPO] && ScummMidiTempo > 0) + { + sprintf(tempo, "-t%lx", ScummMidiTempo); + argv[argc++] = tempo; + } + if (args[USG_TALKSPEED] && ScummTalkSpeed >= 0 && ScummTalkSpeed <= 255) + { + sprintf(talkspeed, "-y%ld", ScummTalkSpeed); + argv[argc++] = talkspeed; + } + if (ScummLang) argv[argc++] = ScummLang; + if (args[USG_DUMPSCRIPTS]) argv[argc++] = "-u"; + if (args[USG_DEBUGLEVEL]) + { + sprintf(dbglvl, "-d%ld", ScummDebugLevel); + argv[argc++] = dbglvl; + } + if (ScummStory) + argv[argc++] = ScummStory; + + g_system = OSystem_MorphOS_create(); + assert(g_system); + + // Invoke the actual ScummVM main entry point: + int res = scummvm_main(argc, argv); + g_system->quit(); // TODO: Consider removing / replacing this! + return res; +} + diff --git a/backends/platform/morphos/morphos_timer.cpp b/backends/platform/morphos/morphos_timer.cpp new file mode 100644 index 0000000000..b558c7fd66 --- /dev/null +++ b/backends/platform/morphos/morphos_timer.cpp @@ -0,0 +1,234 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2002-2006 The ScummVM Team + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#include "common/stdafx.h" +#include "base/engine.h" + +#include <exec/memory.h> +#include <exec/semaphores.h> +#include <dos/dostags.h> +#include <emul/emulinterface.h> + +#include <proto/exec.h> +#include <proto/dos.h> +#include <proto/timer.h> + +#include "morphos.h" +#include "timer.h" + +Timer::Timer(Engine * engine) +{ + if ((TimerServicePort = CreateMsgPort())) + { + TimerServiceStartup.mn_Node.ln_Type = NT_MESSAGE; + TimerServiceStartup.mn_ReplyPort = TimerServicePort; + TimerServiceStartup.mn_Length = sizeof(TimerServiceStartup); + + TimerServiceThread = CreateNewProcTags(NP_Entry, (ULONG) TimerService, + NP_CodeType, CODETYPE_PPC, + NP_Name, (ULONG) "ScummVM Timer Service", + NP_Priority, 0, + NP_StartupMsg, &TimerServiceStartup, + NP_PPC_Arg1, (ULONG) this, + NP_PPC_Arg2, (ULONG) engine, + TAG_DONE + ); + } +} + +Timer::~Timer() +{ + if (TimerServiceThread) + { + Signal((Task *) TimerServiceThread, SIGBREAKF_CTRL_C); + WaitPort(TimerServicePort); + DeleteMsgPort(TimerServicePort); + TimerServiceThread = NULL; + } +} + +bool Timer::init() +{ + return TimerServiceThread != NULL; +} + +void Timer::release() +{ +} + +bool Timer::installTimerProc(TimerProc procedure, int32 interval) +{ + return SendMsg(TSM_MSGID_ADDTIMER, procedure, interval); +} + +void Timer::removeTimerProc(TimerProc procedure) +{ + SendMsg(TSM_MSGID_REMTIMER, procedure, 0); +} + +bool Timer::SendMsg(ULONG msg_id, TimerProc procedure, LONG interval) +{ + if (TimerServiceThread == NULL) + return false; + + TimerServiceMessage *tmsg = (TimerServiceMessage *) AllocVec(sizeof (TimerServiceMessage), MEMF_PUBLIC | MEMF_CLEAR); + if (tmsg == NULL) + return false; + + tmsg->tsm_Message.mn_Node.ln_Type = NT_MESSAGE; + tmsg->tsm_Message.mn_ReplyPort = NULL; + tmsg->tsm_Message.mn_Length = sizeof (TimerServiceMessage); + tmsg->tsm_MsgID = msg_id; + tmsg->tsm_Callback = procedure; + tmsg->tsm_Interval = interval; + PutMsg(&TimerServiceThread->pr_MsgPort, (Message*) tmsg); + + return true; +} + +void Timer::TimerService(Timer *this_ptr, Engine *engine) +{ + MsgPort *port = &((Process *) FindTask(NULL))->pr_MsgPort; + ULONG port_bit = 1 << port->mp_SigBit; + ULONG signal_mask = SIGBREAKF_CTRL_C | port_bit; + ULONG timer_bits = 0, signals; + ULONG interval, t; + timeval start_callback, end_callback; + + ULONG timers = 0; + TimerSlot timer_slots[MAX_TIMERS]; + + for (;;) + { + signals = Wait(signal_mask); + + GetSysTime(&start_callback); + + if (signals & port_bit) + { + TimerServiceMessage *tmsg; + + while ((tmsg = (TimerServiceMessage *) GetMsg(port))) + { + if (tmsg->tsm_Message.mn_Length == sizeof (TimerServiceMessage)) + { + switch (tmsg->tsm_MsgID) + { + case TSM_MSGID_ADDTIMER: + if (timers < MAX_TIMERS) + { + ULONG unit = UNIT_MICROHZ; + + if (tmsg->tsm_Interval >= 1000000) + unit = UNIT_VBLANK; + if (OSystem_MorphOS::OpenATimer(&timer_slots[timers].ts_Port, (IORequest **) &timer_slots[timers].ts_IORequest, unit)) + { + timer_slots[timers].ts_Callback = tmsg->tsm_Callback; + timer_slots[timers].ts_Interval = tmsg->tsm_Interval; + timer_slots[timers].ts_SignalBit = 1 << timer_slots[timers].ts_Port->mp_SigBit; + + signal_mask |= timer_slots[timers].ts_SignalBit; + timer_bits |= timer_slots[timers].ts_SignalBit; + + timerequest *req = timer_slots[timers].ts_IORequest; + interval = timer_slots[timers].ts_Interval; + req->tr_node.io_Command = TR_ADDREQUEST; + req->tr_time.tv_secs = interval/1000000; + req->tr_time.tv_micro = interval%1000000; + SendIO((IORequest*) req); + + timers++; + } + } + break; + + case TSM_MSGID_REMTIMER: + { + for (t = 0; t < timers; t++) + { + if (timer_slots[t].ts_Callback == tmsg->tsm_Callback) + { + AbortIO((IORequest *) timer_slots[t].ts_IORequest); + WaitIO((IORequest *) timer_slots[t].ts_IORequest); + signal_mask &= ~timer_slots[t].ts_SignalBit; + timer_bits &= ~timer_slots[t].ts_SignalBit; + CloseDevice((IORequest *) timer_slots[t].ts_IORequest); + DeleteIORequest((IORequest *) timer_slots[t].ts_IORequest); + DeleteMsgPort(timer_slots[t].ts_Port); + if (t < timers-1) + memmove(&timer_slots[t], &timer_slots[t+1], sizeof (TimerSlot)*(timers-t-1)); + timers--; + continue; + } + } + break; + } + + default: + warning("MorphOS TimerService received message of unknown type."); + } + } + + if (tmsg->tsm_Message.mn_ReplyPort) + ReplyMsg((Message *) tmsg); + else + FreeVec((Message *) tmsg); + } + } + + if (signals & SIGBREAKF_CTRL_C) + break; + + if (signals & timer_bits) + { + for (t = 0; t < timers; t++) + { + if (signals & timer_slots[t].ts_SignalBit) + { + timerequest *req = timer_slots[t].ts_IORequest; + WaitIO((IORequest *) req); + interval = timer_slots[t].ts_Interval; + (*timer_slots[t].ts_Callback)(engine); + GetSysTime(&end_callback); + SubTime(&end_callback, &start_callback); + interval -= end_callback.tv_sec*1000000+end_callback.tv_micro/1000000+40000; + if (interval < 0) + interval = 0; + + req->tr_node.io_Command = TR_ADDREQUEST; + req->tr_time.tv_secs = interval/1000000; + req->tr_time.tv_micro = interval%1000000; + SendIO((IORequest*) req); + } + } + } + } + + for (t = 0; t < timers; t++) + { + AbortIO((IORequest *) timer_slots[t].ts_IORequest); + WaitIO((IORequest *) timer_slots[t].ts_IORequest); + CloseDevice((IORequest *) timer_slots[t].ts_IORequest); + DeleteIORequest((IORequest *) timer_slots[t].ts_IORequest); + DeleteMsgPort(timer_slots[t].ts_Port); + } +} + diff --git a/backends/platform/morphos/morphos_timer.h b/backends/platform/morphos/morphos_timer.h new file mode 100644 index 0000000000..1ab3259b39 --- /dev/null +++ b/backends/platform/morphos/morphos_timer.h @@ -0,0 +1,88 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2002-2006 The ScummVM Team + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#ifndef MORPHOS_TIMER_H +#define MORPHOS_TIMER_H + +#ifndef TIMER_H +#include "timer.h" // for MAX_TIMER +#endif + +#ifndef EXEC_PORTS_H +#include <exec/ports.h> +#endif + +#ifndef EXEC_IO_H +#include <exec/io.h> +#endif + +#ifndef EXEC_SEMAPHORES_H +#include <exec/semaphores.h> +#endif + +#ifndef DOS_DOSEXTENS_H +#include <dos/dosextens.h> +#endif + +class OSystem; + +#define TSM_MSGID_ADDTIMER 0 +#define TSM_MSGID_REMTIMER 1 + +struct TimerServiceMessage +{ + Message tsm_Message; + ULONG tsm_MsgID; + TimerProc tsm_Callback; + LONG tsm_Interval; +}; + +class Timer +{ + public: + Timer(Engine * engine); + ~Timer(); + + bool init(); + void release(); + bool installTimerProc(TimerProc procedure, int32 interval); + void removeTimerProc(TimerProc procedure); + + protected: + bool SendMsg(ULONG MsgID, TimerProc procedure, LONG interval); + static void TimerService(Timer *, Engine *); + + Process *TimerServiceThread; + MsgPort *TimerServicePort; + Message TimerServiceStartup; + + struct TimerSlot + { + MsgPort *ts_Port; + timerequest *ts_IORequest; + ULONG ts_SignalBit; + TimerProc ts_Callback; + LONG ts_Interval; + }; +}; + +#endif + diff --git a/backends/platform/null/module.mk b/backends/platform/null/module.mk new file mode 100644 index 0000000000..12a2cbc510 --- /dev/null +++ b/backends/platform/null/module.mk @@ -0,0 +1,10 @@ +MODULE := backends/platform/null + +MODULE_OBJS := \ + null.o + +MODULE_DIRS += \ + backends/platform/null/ + +# We don't use the rules.mk here on purpose +OBJS := $(addprefix $(MODULE)/, $(MODULE_OBJS)) $(OBJS) diff --git a/backends/platform/null/null.cpp b/backends/platform/null/null.cpp new file mode 100644 index 0000000000..f9943ef448 --- /dev/null +++ b/backends/platform/null/null.cpp @@ -0,0 +1,263 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "common/stdafx.h" +#include "common/scummsys.h" +#include "common/system.h" + +#if defined(USE_NULL_DRIVER) + +#include "common/rect.h" +#include "common/savefile.h" + +class OSystem_NULL : public OSystem { +public: + static OSystem *instance(); + +public: + + OSystem_NULL(); + virtual ~OSystem_NULL(); + + virtual bool hasFeature(Feature f); + virtual void setFeatureState(Feature f, bool enable); + virtual bool getFeatureState(Feature f); + virtual const GraphicsMode *getSupportedGraphicsModes() const; + virtual int getDefaultGraphicsMode() const; + bool setGraphicsMode(const char *name); + virtual int getGraphicsMode() const; + virtual void initSize(uint width, uint height); + virtual int16 getHeight(); + virtual int16 getWidth(); + virtual void setPalette(const byte *colors, uint start, uint num); + virtual void copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h); + virtual void updateScreen(); + virtual void setShakePos(int shakeOffset); + + virtual void showOverlay(); + virtual void hideOverlay(); + virtual void clearOverlay(); + virtual void grabOverlay(OverlayColor *buf, int pitch); + virtual void copyRectToOverlay(const OverlayColor *buf, int pitch, int x, int y, int w, int h); + virtual int16 getOverlayHeight(); + virtual int16 getOverlayWidth(); + + virtual OverlayColor RGBToColor(uint8 r, uint8 g, uint8 b); + virtual void colorToRGB(OverlayColor color, uint8 &r, uint8 &g, uint8 &b); + + virtual bool showMouse(bool visible); + + virtual void warpMouse(int x, int y); + virtual void setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, byte keycolor = 255); + + virtual bool pollEvent(Event &event); + virtual uint32 getMillis(); + virtual void delayMillis(uint msecs); + + virtual void setTimerCallback(TimerProc callback, int interval); + + virtual MutexRef createMutex(void); + virtual void lockMutex(MutexRef mutex); + virtual void unlockMutex(MutexRef mutex); + virtual void deleteMutex(MutexRef mutex); + + virtual bool setSoundCallback(SoundProc proc, void *param); + virtual void clearSoundCallback(); + virtual int getOutputSampleRate() const; + + virtual bool openCD(int drive); + virtual bool pollCD(); + + virtual void playCD(int track, int num_loops, int start_frame, int duration); + virtual void stopCD(); + virtual void updateCD(); + + virtual void quit(); + + virtual void setWindowCaption(const char *caption); +}; + +static const OSystem::GraphicsMode s_supportedGraphicsModes[] = { + {0, 0, 0} +}; + +int main(int argc, char *argv[]) { + g_system = OSystem_NULL_create(); + assert(g_system); + + // Invoke the actual ScummVM main entry point: + int res = scummvm_main(argc, argv); + g_system->quit(); // TODO: Consider removing / replacing this! + return res; +} + +OSystem_NULL::OSystem_NULL() { +} + +OSystem_NULL::~OSystem_NULL() { +} + +bool OSystem_NULL::hasFeature(Feature f) { + return false; +} + +void OSystem_NULL::setFeatureState(Feature f, bool enable) { +} + +bool OSystem_NULL::getFeatureState(Feature f) { + return false; +} + +const OSystem::GraphicsMode* OSystem_NULL::getSupportedGraphicsModes() const { + return s_supportedGraphicsModes; +} + + +int OSystem_NULL::getDefaultGraphicsMode() const { + return -1; +} + +bool OSystem_NULL::setGraphicsMode(int mode) { + return true; +} + +int OSystem_NULL::getGraphicsMode() const { + return -1; +} + +void OSystem_NULL::initSize(uint width, uint height) { +} + +int16 OSystem_NULL::getHeight() { + return 320; +} + +int16 OSystem_NULL::getWidth() { + return 200; +} + +void OSystem_NULL::setPalette(const byte *colors, uint start, uint num) { +} + +void OSystem_NULL::copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h) { +} + +void OSystem_NULL::updateScreen() { +} + +void OSystem_NULL::setShakePos(int shakeOffset) { +} + +void OSystem_NULL::showOverlay () { +} + +void OSystem_NULL::hideOverlay () { +} + +void OSystem_NULL::clearOverlay () { +} + +void OSystem_NULL::grabOverlay (OverlayColor *buf, int pitch) { +} + +void OSystem_NULL::copyRectToOverlay (const OverlayColor *buf, int pitch, int x, int y, int w, int h) { +} + +int16 OSystem_NULL::getOverlayHeight() { + return getHeight(); +} + +int16 OSystem_NULL::getOverlayWidth() { + return getWidth(); +} + +OverlayColor OSystem_NULL::RGBToColor(uint8 r, uint8 g, uint8 b) { + return 0; +} + +void OSystem_NULL::colorToRGB(OverlayColor color, uint8 &r, uint8 &g, uint8 &b) { +} + +bool OSystem_NULL::showMouse(bool visible) { + return true; +} + +void OSystem_NULL::warpMouse(int x, int y) { +} + +void OSystem_NULL::setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, byte keycolor) { +} + +bool OSystem_NULL::pollEvent(Event &event) { + return false; +} + +uint32 OSystem_NULL::getMillis() { + return 0; +} + +void OSystem_NULL::delayMillis(uint msecs) { +} + +void OSystem_NULL::setTimerCallback(TimerProc callback, int interval) { +} + +OSystem::MutexRef OSystem_NULL::createMutex(void) { + return NULL; +} + +void OSystem_NULL::lockMutex(MutexRef mutex) { +} + +void OSystem_NULL::unlockMutex(MutexRef mutex) { +} + +void OSystem_NULL::deleteMutex(MutexRef mutex) { +} + +bool OSystem_NULL::setSoundCallback(SoundProc proc, void *param) { + return true; +} + +void OSystem_NULL::clearSoundCallback() { +} + +int OSystem_NULL::getOutputSampleRate() const { + return 22050; +} + +void OSystem_NULL::quit() { +} + +void OSystem_NULL::setWindowCaption(const char *caption) { +} + +OSystem *OSystem_NULL_create() { + return new OSystem_NULL(); +} +#else /* USE_NULL_DRIVER */ + +OSystem *OSystem_NULL_create() { + return NULL; +} + +#endif diff --git a/backends/platform/sdl/build.rules b/backends/platform/sdl/build.rules new file mode 100644 index 0000000000..14cfe0ebdf --- /dev/null +++ b/backends/platform/sdl/build.rules @@ -0,0 +1,9 @@ +# Build settings for the SDL backend +MODULES += backends/sdl +DEFINES += -DUNIX +INCLUDES += `sdl-config --cflags` +LIBS += `sdl-config --libs` + +# Uncomment this in addition to the above if you compile on Mac OS X +# DEFINES += -DMACOSX +# LIBS += -framework QuickTime -framework AudioUnit diff --git a/backends/platform/sdl/events.cpp b/backends/platform/sdl/events.cpp new file mode 100644 index 0000000000..4fa02907db --- /dev/null +++ b/backends/platform/sdl/events.cpp @@ -0,0 +1,510 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "backends/platform/sdl/sdl-common.h" +#include "common/util.h" + +// 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 + +#ifndef __SYMBIAN32__ // Symbian wants dialog joystick i.e cursor for movement/selection + #define JOY_ANALOG +#endif + +// #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 + + + + +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; +} + +void OSystem_SDL::fillMouseEvent(Event &event, int x, int y) { + event.mouse.x = x; + event.mouse.y = y; + + // Update the "keyboard mouse" coords + _km.x = x; + _km.y = y; + + // Adjust for the screen scaling + if (!_overlayVisible) { + event.mouse.x /= _scaleFactor; + event.mouse.y /= _scaleFactor; + if (_adjustAspectRatio) + event.mouse.y = aspect2Real(event.mouse.y); + } +} + +void OSystem_SDL::handleKbdMouse() { + uint32 curTime = getMillis(); + if (curTime >= _km.last_time + _km.delay_time) { + _km.last_time = curTime; + if (_km.x_down_count == 1) { + _km.x_down_time = curTime; + _km.x_down_count = 2; + } + if (_km.y_down_count == 1) { + _km.y_down_time = curTime; + _km.y_down_count = 2; + } + + if (_km.x_vel || _km.y_vel) { + if (_km.x_down_count) { + if (curTime > _km.x_down_time + _km.delay_time * 12) { + if (_km.x_vel > 0) + _km.x_vel++; + else + _km.x_vel--; + } else if (curTime > _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 (curTime > _km.y_down_time + _km.delay_time * 12) { + if (_km.y_vel > 0) + _km.y_vel++; + else + _km.y_vel--; + } else if (curTime > _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); + } + } +} + +static byte SDLModToOSystemKeyFlags(SDLMod mod) { + byte b = 0; +#ifdef LINUPY + // Yopy has no ALT key, steal the SHIFT key + // (which isn't used much anyway) + if (mod & KMOD_SHIFT) + b |= OSystem::KBD_ALT; +#else + if (mod & KMOD_SHIFT) + b |= OSystem::KBD_SHIFT; + if (mod & KMOD_ALT) + b |= OSystem::KBD_ALT; +#endif + if (mod & KMOD_CTRL) + b |= OSystem::KBD_CTRL; + + return b; +} + +bool OSystem_SDL::pollEvent(Event &event) { + SDL_Event ev; + int axis; + byte b = 0; + + handleKbdMouse(); + + // If the screen mode changed, send an EVENT_SCREEN_CHANGED + if (_modeChanged) { + _modeChanged = false; + event.type = EVENT_SCREEN_CHANGED; + return true; + } + + while(SDL_PollEvent(&ev)) { + switch(ev.type) { + case SDL_KEYDOWN:{ + b = event.kbd.flags = SDLModToOSystemKeyFlags(SDL_GetModState()); + + // Alt-Return and Alt-Enter toggle full screen mode + if (b == KBD_ALT && (ev.key.keysym.sym == SDLK_RETURN + || ev.key.keysym.sym == SDLK_KP_ENTER)) { + setFullscreenMode(!_fullscreen); +#ifdef USE_OSD + if (_fullscreen) + displayMessageOnOSD("Fullscreen mode"); + else + displayMessageOnOSD("Windowed mode"); +#endif + + break; + } + + // Alt-S: Create a screenshot + if (b == KBD_ALT && ev.key.keysym.sym == 's') { + char filename[20]; + + for (int n = 0;; n++) { + SDL_RWops *file; + + sprintf(filename, "scummvm%05d.bmp", n); + file = SDL_RWFromFile(filename, "r"); + if (!file) + break; + SDL_RWclose(file); + } + if (saveScreenshot(filename)) + printf("Saved '%s'\n", filename); + else + printf("Could not save screenshot!\n"); + break; + } + + // Ctrl-m toggles mouse capture + if (b == KBD_CTRL && ev.key.keysym.sym == 'm') { + toggleMouseGrab(); + break; + } + +#ifdef MACOSX + // On Macintosh', Cmd-Q quits + if ((ev.key.keysym.mod & KMOD_META) && ev.key.keysym.sym == 'q') { + event.type = EVENT_QUIT; + return true; + } +#elif defined(UNIX) + // On other unices, Control-Q quits + if ((ev.key.keysym.mod & KMOD_CTRL) && ev.key.keysym.sym == 'q') { + event.type = EVENT_QUIT; + return true; + } +#else + // Ctrl-z and Alt-X quit + if ((b == KBD_CTRL && ev.key.keysym.sym == 'z') || (b == KBD_ALT && ev.key.keysym.sym == 'x')) { + event.type = EVENT_QUIT; + return true; + } +#endif + + // Ctrl-Alt-<key> will change the GFX mode + if ((b & (KBD_CTRL|KBD_ALT)) == (KBD_CTRL|KBD_ALT)) { + + handleScalerHotkeys(ev.key); + break; + } + const bool event_complete = remapKey(ev,event); + + if (event_complete) + return true; + + event.type = 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); + + return true; + } + case SDL_KEYUP: + { + const bool event_complete = remapKey(ev,event); + + if (event_complete) + return true; + + event.type = 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); + b = event.kbd.flags = SDLModToOSystemKeyFlags(SDL_GetModState()); + + // Ctrl-Alt-<key> will change the GFX mode + if ((b & (KBD_CTRL|KBD_ALT)) == (KBD_CTRL|KBD_ALT)) { + // Swallow these key up events + break; + } + + return true; + } + case SDL_MOUSEMOTION: + event.type = EVENT_MOUSEMOVE; + fillMouseEvent(event, ev.motion.x, ev.motion.y); + + setMousePos(event.mouse.x, event.mouse.y); + return true; + + case SDL_MOUSEBUTTONDOWN: + if (ev.button.button == SDL_BUTTON_LEFT) + event.type = EVENT_LBUTTONDOWN; + else if (ev.button.button == SDL_BUTTON_RIGHT) + event.type = EVENT_RBUTTONDOWN; +#if defined(SDL_BUTTON_WHEELUP) && defined(SDL_BUTTON_WHEELDOWN) + else if (ev.button.button == SDL_BUTTON_WHEELUP) + event.type = EVENT_WHEELUP; + else if (ev.button.button == SDL_BUTTON_WHEELDOWN) + event.type = EVENT_WHEELDOWN; +#endif + else + break; + + fillMouseEvent(event, ev.button.x, ev.button.y); + + return true; + + case SDL_MOUSEBUTTONUP: + if (ev.button.button == SDL_BUTTON_LEFT) + event.type = EVENT_LBUTTONUP; + else if (ev.button.button == SDL_BUTTON_RIGHT) + event.type = EVENT_RBUTTONUP; + else + break; + fillMouseEvent(event, ev.button.x, ev.button.y); + + return true; + + case SDL_JOYBUTTONDOWN: + if (ev.jbutton.button == JOY_BUT_LMOUSE) { + event.type = EVENT_LBUTTONDOWN; + fillMouseEvent(event, _km.x, _km.y); + } else if (ev.jbutton.button == JOY_BUT_RMOUSE) { + event.type = EVENT_RBUTTONDOWN; + fillMouseEvent(event, _km.x, _km.y); + } else { + event.type = 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.type = EVENT_LBUTTONUP; + fillMouseEvent(event, _km.x, _km.y); + } else if (ev.jbutton.button == JOY_BUT_RMOUSE) { + event.type = EVENT_RBUTTONUP; + fillMouseEvent(event, _km.x, _km.y); + } else { + event.type = 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.type = EVENT_MOUSEMOVE; + } else if ( axis < -JOY_DEADZONE ) { + axis += JOY_DEADZONE; + event.type = 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 + } + + fillMouseEvent(event, _km.x, _km.y); + + return true; + + case SDL_VIDEOEXPOSE: + _forceFull = true; + break; + + case SDL_QUIT: + event.type = EVENT_QUIT; + return true; + } + } + return false; +} + +bool OSystem_SDL::remapKey(SDL_Event &ev,Event &event) { +#ifdef LINUPY + // On Yopy map the End button to quit + if ((ev.key.keysym.sym == 293)) { + event.type = EVENT_QUIT; + return true; + } + // Map menu key to f5 (scumm menu) + if (ev.key.keysym.sym == 306) { + event.type = EVENT_KEYDOWN; + event.kbd.keycode = SDLK_F5; + event.kbd.ascii = mapKey(SDLK_F5, ev.key.keysym.mod, 0); + return true; + } + // Map action key to action + if (ev.key.keysym.sym == 291) { + event.type = EVENT_KEYDOWN; + event.kbd.keycode = SDLK_TAB; + event.kbd.ascii = mapKey(SDLK_TAB, ev.key.keysym.mod, 0); + return true; + } + // Map OK key to skip cinematic + if (ev.key.keysym.sym == 292) { + event.type = EVENT_KEYDOWN; + event.kbd.keycode = SDLK_ESCAPE; + event.kbd.ascii = mapKey(SDLK_ESCAPE, ev.key.keysym.mod, 0); + return true; + } +#endif + +#ifdef QTOPIA + // Quit on fn+backspace on zaurus + if (ev.key.keysym.sym == 127) { + event.type = EVENT_QUIT; + return true; + } + + // Map menu key (f11) to f5 (scumm menu) + if (ev.key.keysym.sym == SDLK_F11) { + event.type = EVENT_KEYDOWN; + event.kbd.keycode = SDLK_F5; + event.kbd.ascii = mapKey(SDLK_F5, ev.key.keysym.mod, 0); + } + // Nap 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.type = 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.type = 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.type = 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 + return false; +} + diff --git a/backends/platform/sdl/graphics.cpp b/backends/platform/sdl/graphics.cpp new file mode 100644 index 0000000000..eb560c5769 --- /dev/null +++ b/backends/platform/sdl/graphics.cpp @@ -0,0 +1,1745 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "backends/platform/sdl/sdl-common.h" +#include "common/util.h" +#include "graphics/font.h" +#include "graphics/fontman.h" +#include "graphics/scaler.h" +#include "graphics/surface.h" + +static const OSystem::GraphicsMode s_supportedGraphicsModes[] = { + {"1x", "Normal (no scaling)", GFX_NORMAL}, + {"2x", "2x", GFX_DOUBLESIZE}, + {"3x", "3x", GFX_TRIPLESIZE}, + {"2xsai", "2xSAI", GFX_2XSAI}, + {"super2xsai", "Super2xSAI", GFX_SUPER2XSAI}, + {"supereagle", "SuperEagle", GFX_SUPEREAGLE}, + {"advmame2x", "AdvMAME2x", GFX_ADVMAME2X}, + {"advmame3x", "AdvMAME3x", GFX_ADVMAME3X}, +#ifndef DISABLE_HQ_SCALERS + {"hq2x", "HQ2x", GFX_HQ2X}, + {"hq3x", "HQ3x", GFX_HQ3X}, +#endif + {"tv2x", "TV2x", GFX_TV2X}, + {"dotmatrix", "DotMatrix", GFX_DOTMATRIX}, + {0, 0, 0} +}; + +// Table of relative scalers magnitudes +// [definedScale - 1][_scaleFactor - 1] +static ScalerProc *scalersMagn[3][3] = { +#ifndef DISABLE_SCALERS + { Normal1x, AdvMame2x, AdvMame3x }, + { Normal1x, Normal1x, Normal1o5x }, + { Normal1x, Normal1x, Normal1x } +#else // remove dependencies on other scalers + { Normal1x, Normal1x, Normal1x }, + { Normal1x, Normal1x, Normal1x }, + { Normal1x, Normal1x, Normal1x } +#endif +}; + +static const int s_gfxModeSwitchTable[][4] = { + { GFX_NORMAL, GFX_DOUBLESIZE, GFX_TRIPLESIZE, -1 }, + { GFX_NORMAL, GFX_ADVMAME2X, GFX_ADVMAME3X, -1 }, + { GFX_NORMAL, GFX_HQ2X, GFX_HQ3X, -1 }, + { GFX_NORMAL, GFX_2XSAI, -1, -1 }, + { GFX_NORMAL, GFX_SUPER2XSAI, -1, -1 }, + { GFX_NORMAL, GFX_SUPEREAGLE, -1, -1 }, + { GFX_NORMAL, GFX_TV2X, -1, -1 }, + { GFX_NORMAL, GFX_DOTMATRIX, -1, -1 } + }; + +#ifndef DISABLE_SCALERS +static int cursorStretch200To240(uint8 *buf, uint32 pitch, int width, int height, int srcX, int srcY, int origSrcY); +#endif + +const OSystem::GraphicsMode *OSystem_SDL::getSupportedGraphicsModes() const { + return s_supportedGraphicsModes; +} + +int OSystem_SDL::getDefaultGraphicsMode() const { + return GFX_DOUBLESIZE; +} + +void OSystem_SDL::beginGFXTransaction(void) { + assert (_transactionMode == kTransactionNone); + + _transactionMode = kTransactionActive; + + _transactionDetails.modeChanged = false; + _transactionDetails.sizeChanged = false; + _transactionDetails.arChanged = false; + _transactionDetails.fsChanged = false; + + _transactionDetails.needHotswap = false; + _transactionDetails.needUpdatescreen = false; + _transactionDetails.needUnload = false; + + _transactionDetails.normal1xScaler = false; +} + +void OSystem_SDL::endGFXTransaction(void) { + // for each engine we run initCommonGFX() as first thing in the transaction + // and initSize() is called later. If user runs launcher at 320x200 with + // 2x overlay, setting to Nomral1x sclaler in that case will be suppressed + // and backend is forced to 2x + // + // This leads to bad results such as 1280x960 window for 640x480 engines. + // To prevent that we rerun setGraphicsMode() if there was 1x scaler request + if (_transactionDetails.normal1xScaler) + setGraphicsMode(GFX_NORMAL); + + assert (_transactionMode == kTransactionActive); + + _transactionMode = kTransactionCommit; + if (_transactionDetails.modeChanged) + setGraphicsMode(_transactionDetails.mode); + + if (_transactionDetails.sizeChanged) + initSize(_transactionDetails.w, _transactionDetails.h); + + if (_transactionDetails.arChanged) + setAspectRatioCorrection(_transactionDetails.ar); + + if (_transactionDetails.needUnload) { + unloadGFXMode(); + loadGFXMode(); + clearOverlay(); + } else { + if (!_transactionDetails.fsChanged) { + if (_transactionDetails.needHotswap) + hotswapGFXMode(); + else if (_transactionDetails.needUpdatescreen) + internUpdateScreen(); + } + } + + if (_transactionDetails.fsChanged) + setFullscreenMode(_transactionDetails.fs); + + _transactionMode = kTransactionNone; +} + +bool OSystem_SDL::setGraphicsMode(int mode) { + Common::StackLock lock(_graphicsMutex); + + int newScaleFactor = 1; + ScalerProc *newScalerProc; + + switch(mode) { + case GFX_NORMAL: + newScaleFactor = 1; + newScalerProc = Normal1x; + break; +#ifndef DISABLE_SCALERS + case GFX_DOUBLESIZE: + newScaleFactor = 2; + newScalerProc = Normal2x; + break; + case GFX_TRIPLESIZE: + newScaleFactor = 3; + newScalerProc = Normal3x; + break; + + case GFX_2XSAI: + newScaleFactor = 2; + newScalerProc = _2xSaI; + break; + case GFX_SUPER2XSAI: + newScaleFactor = 2; + newScalerProc = Super2xSaI; + break; + case GFX_SUPEREAGLE: + newScaleFactor = 2; + newScalerProc = SuperEagle; + break; + case GFX_ADVMAME2X: + newScaleFactor = 2; + newScalerProc = AdvMame2x; + break; + case GFX_ADVMAME3X: + newScaleFactor = 3; + newScalerProc = AdvMame3x; + break; +#ifndef DISABLE_HQ_SCALERS + case GFX_HQ2X: + newScaleFactor = 2; + newScalerProc = HQ2x; + break; + case GFX_HQ3X: + newScaleFactor = 3; + newScalerProc = HQ3x; + break; +#endif + case GFX_TV2X: + newScaleFactor = 2; + newScalerProc = TV2x; + break; + case GFX_DOTMATRIX: + newScaleFactor = 2; + newScalerProc = DotMatrix; + break; +#endif // DISABLE_SCALERS + + default: + warning("unknown gfx mode %d", mode); + return false; + } + + _transactionDetails.normal1xScaler = (mode == GFX_NORMAL); + + _mode = mode; + _scalerProc = newScalerProc; + + if (_transactionMode == kTransactionActive) { + _transactionDetails.mode = mode; + _transactionDetails.modeChanged = true; + + if (newScaleFactor != _scaleFactor) { + _transactionDetails.needHotswap = true; + _scaleFactor = newScaleFactor; + } + + _transactionDetails.needUpdatescreen = true; + + return true; + } + + // NOTE: This should not be executed at transaction commit + // Otherwise there is some unsolicited setGraphicsMode() call + // which should be properly removed + if (newScaleFactor != _scaleFactor) { + assert(_transactionMode != kTransactionCommit); + + _scaleFactor = newScaleFactor; + hotswapGFXMode(); + } + + // Determine the "scaler type", i.e. essentially an index into the + // s_gfxModeSwitchTable array defined in events.cpp. + if (_mode != GFX_NORMAL) { + for (int i = 0; i < ARRAYSIZE(s_gfxModeSwitchTable); i++) { + if (s_gfxModeSwitchTable[i][1] == _mode || s_gfxModeSwitchTable[i][2] == _mode) { + _scalerType = i; + break; + } + } + } + + if (!_screen) + return true; + + // Blit everything to the screen + _forceFull = true; + + // Even if the old and new scale factors are the same, we may have a + // different scaler for the cursor now. + blitCursor(); + + if (_transactionMode != kTransactionCommit) + internUpdateScreen(); + + // Make sure that an EVENT_SCREEN_CHANGED gets sent later + _modeChanged = true; + + return true; +} + +int OSystem_SDL::getGraphicsMode() const { + assert (_transactionMode == kTransactionNone); + return _mode; +} + +void OSystem_SDL::initSize(uint w, uint h) { + // Avoid redundant res changes + if ((int)w == _screenWidth && (int)h == _screenHeight && + _transactionMode != kTransactionCommit) + return; + + _screenWidth = w; + _screenHeight = h; + + _cksumNum = (_screenWidth * _screenHeight / (8 * 8)); + + if (_transactionMode == kTransactionActive) { + _transactionDetails.w = w; + _transactionDetails.h = h; + _transactionDetails.sizeChanged = true; + + _transactionDetails.needUnload = true; + + return; + } + + free(_dirtyChecksums); + _dirtyChecksums = (uint32 *)calloc(_cksumNum * 2, sizeof(uint32)); + + if (_transactionMode != kTransactionCommit) { + unloadGFXMode(); + loadGFXMode(); + + // if initSize() gets called in the middle, overlay is not transparent + clearOverlay(); + } +} + +void OSystem_SDL::loadGFXMode() { + assert(_inited); + _forceFull = true; + _modeFlags |= DF_UPDATE_EXPAND_1_PIXEL; + + int hwW, hwH; + +#ifndef __MAEMO__ + _overlayWidth = _screenWidth * _scaleFactor; + _overlayHeight = _screenHeight * _scaleFactor; + + if (_screenHeight != 200) + _adjustAspectRatio = false; + + if (_adjustAspectRatio) + _overlayHeight = real2Aspect(_overlayHeight); + + hwW = _screenWidth * _scaleFactor; + hwH = effectiveScreenHeight(); +#else + hwW = _overlayWidth; + hwH = _overlayHeight; +#endif + + // + // Create the surface that contains the 8 bit game data + // + _screen = SDL_CreateRGBSurface(SDL_SWSURFACE, _screenWidth, _screenHeight, 8, 0, 0, 0, 0); + if (_screen == NULL) + error("allocating _screen failed"); + + // + // Create the surface that contains the scaled graphics in 16 bit mode + // + + _hwscreen = SDL_SetVideoMode(hwW, hwH, 16, + _fullscreen ? (SDL_FULLSCREEN|SDL_SWSURFACE) : SDL_SWSURFACE + ); + if (_hwscreen == NULL) { + // DON'T use error(), as this tries to bring up the debug + // console, which WON'T WORK now that _hwscreen is hosed. + + // FIXME: We should be able to continue the game without + // shutting down or bringing up the debug console, but at + // this point we've already screwed up all our member vars. + // We need to find a way to call SDL_SetVideoMode *before* + // that happens and revert to all the old settings if we + // can't pull off the switch to the new settings. + // + // Fingolfin says: the "easy" way to do that is not to modify + // the member vars before we are sure everything is fine. Think + // of "transactions, commit, rollback" style... we use local vars + // in place of the member vars, do everything etc. etc.. In case + // of a failure, rollback is trivial. Only if everything worked fine + // do we "commit" the changed values to the member vars. + warning("SDL_SetVideoMode says we can't switch to that mode (%s)", SDL_GetError()); + quit(); + } + + // + // Create the surface used for the graphics in 16 bit before scaling, and also the overlay + // + + // Distinguish 555 and 565 mode + if (_hwscreen->format->Rmask == 0x7C00) + InitScalers(555); + else + InitScalers(565); + + // Need some extra bytes around when using 2xSaI + _tmpscreen = SDL_CreateRGBSurface(SDL_SWSURFACE, _screenWidth + 3, _screenHeight + 3, + 16, + _hwscreen->format->Rmask, + _hwscreen->format->Gmask, + _hwscreen->format->Bmask, + _hwscreen->format->Amask); + + if (_tmpscreen == NULL) + error("allocating _tmpscreen failed"); + + _overlayscreen = SDL_CreateRGBSurface(SDL_SWSURFACE, _overlayWidth, _overlayHeight, + 16, + _hwscreen->format->Rmask, + _hwscreen->format->Gmask, + _hwscreen->format->Bmask, + _hwscreen->format->Amask); + + if (_overlayscreen == NULL) + error("allocating _overlayscreen failed"); + + _tmpscreen2 = SDL_CreateRGBSurface(SDL_SWSURFACE, _overlayWidth + 3, _overlayHeight + 3, + 16, + _hwscreen->format->Rmask, + _hwscreen->format->Gmask, + _hwscreen->format->Bmask, + _hwscreen->format->Amask); + + if (_tmpscreen2 == NULL) + error("allocating _tmpscreen2 failed"); + +#ifdef USE_OSD + _osdSurface = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA, + _hwscreen->w, + _hwscreen->h, + 16, + _hwscreen->format->Rmask, + _hwscreen->format->Gmask, + _hwscreen->format->Bmask, + _hwscreen->format->Amask); + if (_osdSurface == NULL) + error("allocating _osdSurface failed"); + SDL_SetColorKey(_osdSurface, SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA, kOSDColorKey); +#endif + + // keyboard cursor control, some other better place for it? + _km.x_max = _screenWidth * _scaleFactor - 1; + _km.y_max = effectiveScreenHeight() - 1; + _km.delay_time = 25; + _km.last_time = 0; +} + +void OSystem_SDL::unloadGFXMode() { + if (_screen) { + SDL_FreeSurface(_screen); + _screen = NULL; + } + + if (_hwscreen) { + SDL_FreeSurface(_hwscreen); + _hwscreen = NULL; + } + + if (_tmpscreen) { + SDL_FreeSurface(_tmpscreen); + _tmpscreen = NULL; + } + + if (_tmpscreen2) { + SDL_FreeSurface(_tmpscreen2); + _tmpscreen2 = NULL; + } + + if (_overlayscreen) { + SDL_FreeSurface(_overlayscreen); + _overlayscreen = NULL; + } + +#ifdef USE_OSD + if (_osdSurface) { + SDL_FreeSurface(_osdSurface); + _osdSurface = NULL; + } +#endif +} + +void OSystem_SDL::hotswapGFXMode() { + if (!_screen) + return; + + // Keep around the old _screen & _overlayscreen so we can restore the screen data + // after the mode switch. + SDL_Surface *old_screen = _screen; + SDL_Surface *old_overlayscreen = _overlayscreen; + + // Release the HW screen surface + SDL_FreeSurface(_hwscreen); + + SDL_FreeSurface(_tmpscreen); + SDL_FreeSurface(_tmpscreen2); + +#ifdef USE_OSD + // Release the OSD surface + SDL_FreeSurface(_osdSurface); +#endif + + // Setup the new GFX mode + loadGFXMode(); + + // reset palette + SDL_SetColors(_screen, _currentPalette, 0, 256); + + // Restore old screen content + SDL_BlitSurface(old_screen, NULL, _screen, NULL); + SDL_BlitSurface(old_overlayscreen, NULL, _overlayscreen, NULL); + + // Free the old surfaces + SDL_FreeSurface(old_screen); + SDL_FreeSurface(old_overlayscreen); + + // Update cursor to new scale + blitCursor(); + + // Blit everything to the screen + internUpdateScreen(); + + // Make sure that an EVENT_SCREEN_CHANGED gets sent later + _modeChanged = true; +} + +void OSystem_SDL::updateScreen() { + assert (_transactionMode == kTransactionNone); + + Common::StackLock lock(_graphicsMutex); // Lock the mutex until this function ends + + internUpdateScreen(); +} + +void OSystem_SDL::internUpdateScreen() { + SDL_Surface *srcSurf, *origSurf; + int height, width; + ScalerProc *scalerProc; + int scale1; + +#if defined (DEBUG) && ! defined(_WIN32_WCE) // definitions not available for non-DEBUG here. (needed this to compile in SYMBIAN32 & linux?) + assert(_hwscreen != NULL); + assert(_hwscreen->map->sw_data != NULL); +#endif + + // If the shake position changed, fill the dirty area with blackness + if (_currentShakePos != _newShakePos) { + SDL_Rect blackrect = {0, 0, _screenWidth * _scaleFactor, _newShakePos * _scaleFactor}; + + if (_adjustAspectRatio && !_overlayVisible) + blackrect.h = real2Aspect(blackrect.h - 1) + 1; + + SDL_FillRect(_hwscreen, &blackrect, 0); + + _currentShakePos = _newShakePos; + + _forceFull = true; + } + + // Check whether the palette was changed in the meantime and update the + // screen surface accordingly. + if (_paletteDirtyEnd != 0) { + SDL_SetColors(_screen, _currentPalette + _paletteDirtyStart, + _paletteDirtyStart, + _paletteDirtyEnd - _paletteDirtyStart); + + _paletteDirtyEnd = 0; + + _forceFull = true; + } + +#ifdef USE_OSD + // OSD visible (i.e. non-transparent)? + if (_osdAlpha != SDL_ALPHA_TRANSPARENT) { + // Updated alpha value + const int diff = SDL_GetTicks() - _osdFadeStartTime; + if (diff > 0) { + if (diff >= kOSDFadeOutDuration) { + // Back to full transparency + _osdAlpha = SDL_ALPHA_TRANSPARENT; + } else { + // Do a linear fade out... + const int startAlpha = SDL_ALPHA_TRANSPARENT + kOSDInitialAlpha * (SDL_ALPHA_OPAQUE - SDL_ALPHA_TRANSPARENT) / 100; + _osdAlpha = startAlpha + diff * (SDL_ALPHA_TRANSPARENT - startAlpha) / kOSDFadeOutDuration; + } + SDL_SetAlpha(_osdSurface, SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA, _osdAlpha); + _forceFull = true; + } + } +#endif + + if (!_overlayVisible) { + origSurf = _screen; + srcSurf = _tmpscreen; + width = _screenWidth; + height = _screenHeight; + scalerProc = _scalerProc; + scale1 = _scaleFactor; + } else { + origSurf = _overlayscreen; + srcSurf = _tmpscreen2; + width = _overlayWidth; + height = _overlayHeight; + scalerProc = Normal1x; + + scale1 = 1; + } + + // Force a full redraw if requested + if (_forceFull) { + _numDirtyRects = 1; + _dirtyRectList[0].x = 0; + _dirtyRectList[0].y = 0; + _dirtyRectList[0].w = width; + _dirtyRectList[0].h = height; + } else + undrawMouse(); + + // Only draw anything if necessary + if (_numDirtyRects > 0) { + + SDL_Rect *r; + SDL_Rect dst; + uint32 srcPitch, dstPitch; + SDL_Rect *lastRect = _dirtyRectList + _numDirtyRects; + + if (scalerProc == Normal1x && !_adjustAspectRatio && 0) { + for (r = _dirtyRectList; r != lastRect; ++r) { + dst = *r; + + dst.y += _currentShakePos; + if (SDL_BlitSurface(origSurf, r, _hwscreen, &dst) != 0) + error("SDL_BlitSurface failed: %s", SDL_GetError()); + } + } else { + for (r = _dirtyRectList; r != lastRect; ++r) { + dst = *r; + dst.x++; // Shift rect by one since 2xSai needs to acces the data around + dst.y++; // any pixel to scale it, and we want to avoid mem access crashes. + + if (SDL_BlitSurface(origSurf, r, srcSurf, &dst) != 0) + error("SDL_BlitSurface failed: %s", SDL_GetError()); + } + + SDL_LockSurface(srcSurf); + SDL_LockSurface(_hwscreen); + + srcPitch = srcSurf->pitch; + dstPitch = _hwscreen->pitch; + + for (r = _dirtyRectList; r != lastRect; ++r) { + register int dst_y = r->y + _currentShakePos; + register int dst_h = 0; + register int orig_dst_y = 0; + register int rx1 = r->x * scale1; + + if (dst_y < height) { + dst_h = r->h; + if (dst_h > height - dst_y) + dst_h = height - dst_y; + + orig_dst_y = dst_y; + dst_y = dst_y * scale1; + + if (_adjustAspectRatio && !_overlayVisible) + dst_y = real2Aspect(dst_y); + + assert(scalerProc != NULL); + scalerProc((byte *)srcSurf->pixels + (r->x * 2 + 2) + (r->y + 1) * srcPitch, srcPitch, + (byte *)_hwscreen->pixels + rx1 * 2 + dst_y * dstPitch, dstPitch, r->w, dst_h); + } + + r->x = rx1; + r->y = dst_y; + r->w = r->w * scale1; + r->h = dst_h * scale1; + +#ifndef DISABLE_SCALERS + if (_adjustAspectRatio && orig_dst_y < height && !_overlayVisible) + r->h = stretch200To240((uint8 *) _hwscreen->pixels, dstPitch, r->w, r->h, r->x, r->y, orig_dst_y * scale1); +#endif + } + SDL_UnlockSurface(srcSurf); + SDL_UnlockSurface(_hwscreen); + } + + // Readjust the dirty rect list in case we are doing a full update. + // This is necessary if shaking is active. + if (_forceFull) { + _dirtyRectList[0].y = 0; + _dirtyRectList[0].h = effectiveScreenHeight(); + } + + drawMouse(); + +#ifdef USE_OSD + if (_osdAlpha != SDL_ALPHA_TRANSPARENT) { + SDL_BlitSurface(_osdSurface, 0, _hwscreen, 0); + } +#endif + // Finally, blit all our changes to the screen + SDL_UpdateRects(_hwscreen, _numDirtyRects, _dirtyRectList); + } else { + drawMouse(); + if (_numDirtyRects) + SDL_UpdateRects(_hwscreen, _numDirtyRects, _dirtyRectList); + } + + _numDirtyRects = 0; + _forceFull = false; +} + +bool OSystem_SDL::saveScreenshot(const char *filename) { + assert(_hwscreen != NULL); + + Common::StackLock lock(_graphicsMutex); // Lock the mutex until this function ends + return SDL_SaveBMP(_hwscreen, filename) == 0; +} + +void OSystem_SDL::setFullscreenMode(bool enable) { + Common::StackLock lock(_graphicsMutex); + + if (_fullscreen != enable || _transactionMode == kTransactionCommit) { + assert(_hwscreen != 0); + _fullscreen = enable; + + if (_transactionMode == kTransactionActive) { + _transactionDetails.fs = enable; + _transactionDetails.fsChanged = true; + + _transactionDetails.needHotswap = true; + + return; + } + +#if (defined(MACOSX) && !SDL_VERSION_ATLEAST(1, 2, 6)) || defined(__MAEMO__) + // On OS X, SDL_WM_ToggleFullScreen is currently not implemented. Worse, + // before SDL 1.2.6 it always returned -1 (which would indicate a + // successful switch). So we simply don't call it at all and use + // hotswapGFXMode() directly to switch to fullscreen mode. + hotswapGFXMode(); +#else + if (!SDL_WM_ToggleFullScreen(_hwscreen)) { + // if ToggleFullScreen fails, achieve the same effect with hotswap gfx mode + hotswapGFXMode(); + } else { + // Blit everything to the screen + internUpdateScreen(); + + // Make sure that an EVENT_SCREEN_CHANGED gets sent later + _modeChanged = true; + } +#endif + } +} + +void OSystem_SDL::setAspectRatioCorrection(bool enable) { + if ((_screenHeight == 200 && _adjustAspectRatio != enable) || + _transactionMode == kTransactionCommit) { + Common::StackLock lock(_graphicsMutex); + + //assert(_hwscreen != 0); + _adjustAspectRatio = enable; + + if (_transactionMode == kTransactionActive) { + _transactionDetails.ar = enable; + _transactionDetails.arChanged = true; + + _transactionDetails.needHotswap = true; + + return; + } else { + if (_transactionMode != kTransactionCommit) + hotswapGFXMode(); + } + + // Make sure that an EVENT_SCREEN_CHANGED gets sent later + _modeChanged = true; + } +} + +void OSystem_SDL::clearScreen() { + assert (_transactionMode == kTransactionNone); + + // Try to lock the screen surface + if (SDL_LockSurface(_screen) == -1) + error("SDL_LockSurface failed: %s", SDL_GetError()); + + byte *dst = (byte *)_screen->pixels; + + // Clear the screen + memset(dst, 0, _screenWidth * _screenHeight); + + // Unlock the screen surface + SDL_UnlockSurface(_screen); +} + +void OSystem_SDL::copyRectToScreen(const byte *src, int pitch, int x, int y, int w, int h) { + assert (_transactionMode == kTransactionNone); + assert(src); + + if (_screen == NULL) + return; + + Common::StackLock lock(_graphicsMutex); // Lock the mutex until this function ends + + assert(x >= 0 && x < _screenWidth); + assert(y >= 0 && y < _screenHeight); + assert(h > 0 && y + h <= _screenHeight); + assert(w > 0 && x + w <= _screenWidth); + + if (((long)src & 3) == 0 && pitch == _screenWidth && x == 0 && y == 0 && + w == _screenWidth && h == _screenHeight && _modeFlags & 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. */ + addDirtyRgnAuto(src); + } else { + /* Clip the coordinates */ + if (x < 0) { + w += x; + src -= x; + x = 0; + } + + if (y < 0) { + h += y; + src -= y * pitch; + y = 0; + } + + if (w > _screenWidth - x) { + w = _screenWidth - x; + } + + if (h > _screenHeight - y) { + h = _screenHeight - y; + } + + if (w <= 0 || h <= 0) + return; + + _cksumValid = false; + addDirtyRect(x, y, w, h); + } + + // Try to lock the screen surface + if (SDL_LockSurface(_screen) == -1) + error("SDL_LockSurface failed: %s", SDL_GetError()); + + byte *dst = (byte *)_screen->pixels + y * _screenWidth + x; + + if (_screenWidth == pitch && pitch == w) { + memcpy(dst, src, h*w); + } else { + do { + memcpy(dst, src, w); + src += pitch; + dst += _screenWidth; + } while (--h); + } + + // Unlock the screen surface + SDL_UnlockSurface(_screen); +} + +bool OSystem_SDL::grabRawScreen(Graphics::Surface *surf) { + assert(_screen); + assert(surf); + + Common::StackLock lock(_graphicsMutex); // Lock the mutex until this function ends + + surf->create(_screenWidth, _screenHeight, _screen->format->BytesPerPixel); + + // Try to lock the screen surface + if (SDL_LockSurface(_screen) == -1) + error("SDL_LockSurface failed: %s", SDL_GetError()); + + memcpy(surf->pixels, _screen->pixels, _screenWidth * _screenHeight * _screen->format->BytesPerPixel); + + // Unlock the screen surface + SDL_UnlockSurface(_screen); + + return true; +} + +void OSystem_SDL::addDirtyRect(int x, int y, int w, int h, bool realCoordinates) { + if (_forceFull) + return; + + if (_numDirtyRects == NUM_DIRTY_RECT) { + _forceFull = true; + return; + } + + int height, width; + + if (!_overlayVisible && !realCoordinates) { + width = _screenWidth; + height = _screenHeight; + } else { + width = _overlayWidth; + height = _overlayHeight; + } + + // Extend the dirty region by 1 pixel for scalers + // that "smear" the screen, e.g. 2xSAI + if ((_modeFlags & DF_UPDATE_EXPAND_1_PIXEL) && !realCoordinates) { + x--; + y--; + w+=2; + h+=2; + } + + // clip + if (x < 0) { + w += x; + x = 0; + } + + if (y < 0) { + h += y; + y=0; + } + + if (w > width - x) { + w = width - x; + } + + if (h > height - y) { + h = height - y; + } + +#ifndef DISABLE_SCALERS + if (_adjustAspectRatio && !_overlayVisible && !realCoordinates) { + makeRectStretchable(x, y, w, h); + } +#endif + + if (w == width && h == height) { + _forceFull = true; + return; + } + + if (w > 0 && h > 0) { + SDL_Rect *r = &_dirtyRectList[_numDirtyRects++]; + + r->x = x; + r->y = y; + r->w = w; + r->h = h; + } +} + + +void OSystem_SDL::makeChecksums(const byte *buf) { + assert(buf); + uint32 *sums = _dirtyChecksums; + uint x,y; + const uint last_x = (uint)_screenWidth / 8; + const uint last_y = (uint)_screenHeight / 8; + + const uint BASE = 65521; /* largest prime smaller than 65536 */ + + /* 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) { + // Adler32 checksum algorithm (from RFC1950, used by gzip and zlib). + // This computes the Adler32 checksum of a 8x8 pixel block. Note + // that we can do the modulo operation (which is the slowest part) + // of the algorithm) at the end, instead of doing each iteration, + // since we only have 64 iterations in total - and thus s1 and + // s2 can't overflow anyway. + uint32 s1 = 1; + uint32 s2 = 0; + const byte *ptr = buf; + for (int subY = 0; subY < 8; subY++) { + for (int subX = 0; subX < 8; subX++) { + s1 += ptr[subX]; + s2 += s1; + } + ptr += _screenWidth; + } + + s1 %= BASE; + s2 %= BASE; + + /* output the checksum for this block */ + *sums++ = (s2 << 16) + s1; + } +} + +void OSystem_SDL::addDirtyRgnAuto(const byte *buf) { + assert(buf); + assert(((long)buf & 3) == 0); + + /* generate a table of the checksums */ + makeChecksums(buf); + + if (!_cksumValid) { + _forceFull = true; + _cksumValid = 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 = _dirtyChecksums; + + for(y = 0; y != _screenHeight / 8; y++) { + for(x = 0; x != _screenWidth / 8; x++, ck++) { + if (ck[0] != ck[_cksumNum]) { + /* 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 + _cksumNum] = ck[w]; + w++; + } while (x + w != _screenWidth / 8 && ck[w] != ck[w + _cksumNum]); + + addDirtyRect(x * 8, y * 8, w * 8, 8); + + if (_forceFull) + goto get_out; + } + } + } + } else { + get_out:; + /* Copy old checksums to new */ + memcpy(_dirtyChecksums + _cksumNum, _dirtyChecksums, _cksumNum * sizeof(uint32)); + } +} + +int16 OSystem_SDL::getHeight() { + return _screenHeight; +} + +int16 OSystem_SDL::getWidth() { + return _screenWidth; +} + +void OSystem_SDL::setPalette(const byte *colors, uint start, uint num) { + assert(colors); + const byte *b = colors; + uint i; + SDL_Color *base = _currentPalette + start; + for (i = 0; i < num; i++) { + base[i].r = b[0]; + base[i].g = b[1]; + base[i].b = b[2]; + b += 4; + } + + if (start < _paletteDirtyStart) + _paletteDirtyStart = start; + + if (start + num > _paletteDirtyEnd) + _paletteDirtyEnd = start + num; + + // Some games blink cursors with palette + if (_cursorPaletteDisabled) + blitCursor(); +} + +void OSystem_SDL::grabPalette(byte *colors, uint start, uint num) { + assert(colors); + const SDL_Color *base = _currentPalette + start; + + for (uint i = 0; i < num; ++i) { + colors[i * 4] = base[i].r; + colors[i * 4 + 1] = base[i].g; + colors[i * 4 + 2] = base[i].b; + colors[i * 4 + 3] = 0xFF; + } +} + +void OSystem_SDL::setCursorPalette(const byte *colors, uint start, uint num) { + assert(colors); + const byte *b = colors; + uint i; + SDL_Color *base = _cursorPalette + start; + for (i = 0; i < num; i++) { + base[i].r = b[0]; + base[i].g = b[1]; + base[i].b = b[2]; + b += 4; + } + + _cursorPaletteDisabled = false; + + blitCursor(); +} + +void OSystem_SDL::setShakePos(int shake_pos) { + assert (_transactionMode == kTransactionNone); + + _newShakePos = shake_pos; +} + + +#pragma mark - +#pragma mark --- Overlays --- +#pragma mark - + +void OSystem_SDL::showOverlay() { + assert (_transactionMode == kTransactionNone); + + int x, y; + + if (_overlayVisible) + return; + + _overlayVisible = true; + + // Since resolution could change, put mouse to adjusted position + // Fixes bug #1349059 + x = _mouseCurState.x * _scaleFactor; + if (_adjustAspectRatio) + y = real2Aspect(_mouseCurState.y) * _scaleFactor; + else + y = _mouseCurState.y * _scaleFactor; + + warpMouse(x, y); + + clearOverlay(); +} + +void OSystem_SDL::hideOverlay() { + assert (_transactionMode == kTransactionNone); + + if (!_overlayVisible) + return; + + int x, y; + + _overlayVisible = false; + + // Since resolution could change, put mouse to adjusted position + // Fixes bug #1349059 + x = _mouseCurState.x / _scaleFactor; + y = _mouseCurState.y / _scaleFactor; + if (_adjustAspectRatio) + y = aspect2Real(y); + + warpMouse(x, y); + + clearOverlay(); + + _forceFull = true; +} + +void OSystem_SDL::clearOverlay() { + //assert (_transactionMode == kTransactionNone); + + Common::StackLock lock(_graphicsMutex); // Lock the mutex until this function ends + + if (!_overlayVisible) + return; + + // 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()); + + SDL_LockSurface(_tmpscreen); + SDL_LockSurface(_overlayscreen); + _scalerProc((byte *)(_tmpscreen->pixels) + _tmpscreen->pitch + 2, _tmpscreen->pitch, + (byte *)_overlayscreen->pixels, _overlayscreen->pitch, _screenWidth, _screenHeight); + +#ifndef DISABLE_SCALERS + if (_adjustAspectRatio) + stretch200To240((uint8 *)_overlayscreen->pixels, _overlayscreen->pitch, + _overlayWidth, _screenHeight * _scaleFactor, 0, 0, 0); +#endif + SDL_UnlockSurface(_tmpscreen); + SDL_UnlockSurface(_overlayscreen); + + _forceFull = true; +} + +void OSystem_SDL::grabOverlay(OverlayColor *buf, int pitch) { + assert (_transactionMode == kTransactionNone); + + if (_overlayscreen == NULL) + return; + + if (SDL_LockSurface(_overlayscreen) == -1) + error("SDL_LockSurface failed: %s", SDL_GetError()); + + byte *src = (byte *)_overlayscreen->pixels; + int h = _overlayHeight; + do { + memcpy(buf, src, _overlayWidth * 2); + src += _overlayscreen->pitch; + buf += pitch; + } while (--h); + + SDL_UnlockSurface(_overlayscreen); +} + +void OSystem_SDL::copyRectToOverlay(const OverlayColor *buf, int pitch, int x, int y, int w, int h) { + assert (_transactionMode == kTransactionNone); + + if (_overlayscreen == 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 > _overlayWidth - x) { + w = _overlayWidth - x; + } + + if (h > _overlayHeight - y) { + h = _overlayHeight - y; + } + + if (w <= 0 || h <= 0) + return; + + // Mark the modified region as dirty + _cksumValid = false; + addDirtyRect(x, y, w, h); + + if (SDL_LockSurface(_overlayscreen) == -1) + error("SDL_LockSurface failed: %s", SDL_GetError()); + + byte *dst = (byte *)_overlayscreen->pixels + y * _overlayscreen->pitch + x * 2; + do { + memcpy(dst, buf, w * 2); + dst += _overlayscreen->pitch; + buf += pitch; + } while (--h); + + SDL_UnlockSurface(_overlayscreen); +} + +OverlayColor OSystem_SDL::RGBToColor(uint8 r, uint8 g, uint8 b) { + return SDL_MapRGB(_overlayscreen->format, r, g, b); +} + +void OSystem_SDL::colorToRGB(OverlayColor color, uint8 &r, uint8 &g, uint8 &b) { + SDL_GetRGB(color, _overlayscreen->format, &r, &g, &b); +} + + +#pragma mark - +#pragma mark --- Mouse --- +#pragma mark - + +bool OSystem_SDL::showMouse(bool visible) { + if (_mouseVisible == visible) + return visible; + + bool last = _mouseVisible; + _mouseVisible = visible; + + return last; +} + +void OSystem_SDL::setMousePos(int x, int y) { + if (x != _mouseCurState.x || y != _mouseCurState.y) { + _mouseCurState.x = x; + _mouseCurState.y = y; + } +} + +void OSystem_SDL::warpMouse(int x, int y) { + int y1 = y; + + if (_adjustAspectRatio && !_overlayVisible) + y1 = real2Aspect(y); + + if (_mouseCurState.x != x || _mouseCurState.y != y) { + if (!_overlayVisible) + SDL_WarpMouse(x * _scaleFactor, y1 * _scaleFactor); + else + SDL_WarpMouse(x, y1); + + // SDL_WarpMouse() generates a mouse movement event, so + // setMousePos() would be called eventually. However, the + // cannon script in CoMI calls this function twice each time + // the cannon is reloaded. Unless we update the mouse position + // immediately the second call is ignored, causing the cannon + // to change its aim. + + setMousePos(x, y); + } +} + +void OSystem_SDL::setMouseCursor(const byte *buf, uint w, uint h, int hotspot_x, int hotspot_y, byte keycolor, int cursorTargetScale) { + if (w == 0 || h == 0) + return; + + _mouseCurState.hotX = hotspot_x; + _mouseCurState.hotY = hotspot_y; + + _mouseKeyColor = keycolor; + + _cursorTargetScale = cursorTargetScale; + + if (_mouseCurState.w != (int)w || _mouseCurState.h != (int)h) { + _mouseCurState.w = w; + _mouseCurState.h = h; + + if (_mouseOrigSurface) + SDL_FreeSurface(_mouseOrigSurface); + + // Allocate bigger surface because AdvMame2x adds black pixel at [0,0] + _mouseOrigSurface = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA, + _mouseCurState.w + 2, + _mouseCurState.h + 2, + 16, + _hwscreen->format->Rmask, + _hwscreen->format->Gmask, + _hwscreen->format->Bmask, + _hwscreen->format->Amask); + + if (_mouseOrigSurface == NULL) + error("allocating _mouseOrigSurface failed"); + SDL_SetColorKey(_mouseOrigSurface, SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA, kMouseColorKey); + } + + free(_mouseData); + + _mouseData = (byte *)malloc(w * h); + memcpy(_mouseData, buf, w * h); + blitCursor(); +} + +void OSystem_SDL::blitCursor() { + byte *dstPtr; + const byte *srcPtr = _mouseData; + byte color; + int w, h, i, j; + + if (!_mouseOrigSurface || !_mouseData) + return; + + w = _mouseCurState.w; + h = _mouseCurState.h; + + SDL_LockSurface(_mouseOrigSurface); + + // Make whole surface transparent + for (i = 0; i < h + 2; i++) { + dstPtr = (byte *)_mouseOrigSurface->pixels + _mouseOrigSurface->pitch * i; + for (j = 0; j < w + 2; j++) { + *(uint16 *)dstPtr = kMouseColorKey; + dstPtr += 2; + } + } + + // Draw from [1,1] since AdvMame2x adds artefact at 0,0 + dstPtr = (byte *)_mouseOrigSurface->pixels + _mouseOrigSurface->pitch + 2; + + SDL_Color *palette; + + if (_cursorPaletteDisabled) + palette = _currentPalette; + else + palette = _cursorPalette; + + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + color = *srcPtr; + if (color != _mouseKeyColor) { // transparent, don't draw + *(uint16 *)dstPtr = SDL_MapRGB(_mouseOrigSurface->format, + palette[color].r, palette[color].g, palette[color].b); + } + dstPtr += 2; + srcPtr++; + } + dstPtr += _mouseOrigSurface->pitch - w * 2; + } + + int rW, rH; + + if (_cursorTargetScale >= _scaleFactor) { + // The cursor target scale is greater or equal to the scale at + // which the rest of the screen is drawn. We do not downscale + // the cursor image, we draw it at its original size. It will + // appear too large on screen. + + rW = w; + rH = h; + _mouseCurState.rHotX = _mouseCurState.hotX; + _mouseCurState.rHotY = _mouseCurState.hotY; + + // The virtual dimensions may be larger than the original. + + _mouseCurState.vW = w * _cursorTargetScale / _scaleFactor; + _mouseCurState.vH = h * _cursorTargetScale / _scaleFactor; + _mouseCurState.vHotX = _mouseCurState.hotX * _cursorTargetScale / + _scaleFactor; + _mouseCurState.vHotY = _mouseCurState.hotY * _cursorTargetScale / + _scaleFactor; + } else { + // The cursor target scale is smaller than the scale at which + // the rest of the screen is drawn. We scale up the cursor + // image to make it appear correct. + + rW = w * _scaleFactor / _cursorTargetScale; + rH = h * _scaleFactor / _cursorTargetScale; + _mouseCurState.rHotX = _mouseCurState.hotX * _scaleFactor / + _cursorTargetScale; + _mouseCurState.rHotY = _mouseCurState.hotY * _scaleFactor / + _cursorTargetScale; + + // The virtual dimensions will be the same as the original. + + _mouseCurState.vW = w; + _mouseCurState.vH = h; + _mouseCurState.vHotX = _mouseCurState.hotX; + _mouseCurState.vHotY = _mouseCurState.hotY; + } + + int rH1 = rH; // store original to pass to aspect-correction function later + if (_adjustAspectRatio && _cursorTargetScale == 1) { + rH = real2Aspect(rH - 1) + 1; + _mouseCurState.rHotY = real2Aspect(_mouseCurState.rHotY); + } + + if (_mouseCurState.rW != rW || _mouseCurState.rH != rH) { + _mouseCurState.rW = rW; + _mouseCurState.rH = rH; + + if (_mouseSurface) + SDL_FreeSurface(_mouseSurface); + + _mouseSurface = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA, + _mouseCurState.rW, + _mouseCurState.rH, + 16, + _hwscreen->format->Rmask, + _hwscreen->format->Gmask, + _hwscreen->format->Bmask, + _hwscreen->format->Amask); + + if (_mouseSurface == NULL) + error("allocating _mouseSurface failed"); + + SDL_SetColorKey(_mouseSurface, SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA, kMouseColorKey); + } + + SDL_LockSurface(_mouseSurface); + + ScalerProc *scalerProc; + + // If possible, use the same scaler for the cursor as for the rest of + // the game. This only works well with the non-blurring scalers so we + // actually only use the 1x, 1.5x, 2x and AdvMame scalers. + + if (_cursorTargetScale == 1 && (_mode == GFX_DOUBLESIZE || _mode == GFX_TRIPLESIZE)) + scalerProc = _scalerProc; + else + scalerProc = scalersMagn[_cursorTargetScale - 1][_scaleFactor - 1]; + + scalerProc((byte *)_mouseOrigSurface->pixels + _mouseOrigSurface->pitch + 2, + _mouseOrigSurface->pitch, (byte *)_mouseSurface->pixels, _mouseSurface->pitch, + _mouseCurState.w, _mouseCurState.h); + +#ifndef DISABLE_SCALERS + if (_adjustAspectRatio && _cursorTargetScale == 1) + cursorStretch200To240((uint8 *)_mouseSurface->pixels, _mouseSurface->pitch, rW, rH1, 0, 0, 0); +#endif + + SDL_UnlockSurface(_mouseSurface); + SDL_UnlockSurface(_mouseOrigSurface); +} + +#ifndef DISABLE_SCALERS +// Basically it is kVeryFastAndUglyAspectMode of stretch200To240 from +// common/scale/aspect.cpp +static int cursorStretch200To240(uint8 *buf, uint32 pitch, int width, int height, int srcX, int srcY, int origSrcY) { + int maxDstY = real2Aspect(origSrcY + height - 1); + int y; + const uint8 *startSrcPtr = buf + srcX * 2 + (srcY - origSrcY) * pitch; + uint8 *dstPtr = buf + srcX * 2 + maxDstY * pitch; + + for (y = maxDstY; y >= srcY; y--) { + const uint8 *srcPtr = startSrcPtr + aspect2Real(y) * pitch; + + if (srcPtr == dstPtr) + break; + memcpy(dstPtr, srcPtr, width * 2); + dstPtr -= pitch; + } + + return 1 + maxDstY - srcY; +} +#endif + +void OSystem_SDL::toggleMouseGrab() { + if (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_OFF) + SDL_WM_GrabInput(SDL_GRAB_ON); + else + SDL_WM_GrabInput(SDL_GRAB_OFF); +} + +void OSystem_SDL::undrawMouse() { + const int x = _mouseBackup.x; + const int y = _mouseBackup.y; + + // When we switch bigger overlay off mouse jumps. Argh! + // This is intended to prevent undrawing offscreen mouse + if (!_overlayVisible && (x >= _screenWidth || y >= _screenHeight)) { + return; + } + + if (_mouseBackup.w != 0 && _mouseBackup.h != 0) + addDirtyRect(x, y, _mouseBackup.w, _mouseBackup.h); +} + +void OSystem_SDL::drawMouse() { + if (!_mouseVisible || !_mouseSurface) { + _mouseBackup.x = _mouseBackup.y = _mouseBackup.w = _mouseBackup.h = 0; + return; + } + + SDL_Rect dst; + int scale; + int width, height; + int hotX, hotY; + + dst.x = _mouseCurState.x; + dst.y = _mouseCurState.y; + + if (!_overlayVisible) { + scale = _scaleFactor; + width = _screenWidth; + height = _screenHeight; + dst.w = _mouseCurState.vW; + dst.h = _mouseCurState.vH; + hotX = _mouseCurState.vHotX; + hotY = _mouseCurState.vHotY; + } else { + scale = 1; + width = _overlayWidth; + height = _overlayHeight; + dst.w = _mouseCurState.rW; + dst.h = _mouseCurState.rH; + hotX = _mouseCurState.rHotX; + hotY = _mouseCurState.rHotY; + } + + // The mouse is undrawn using virtual coordinates, i.e. they may be + // scaled and aspect-ratio corrected. + + _mouseBackup.x = dst.x - hotX; + _mouseBackup.y = dst.y - hotY; + _mouseBackup.w = dst.w; + _mouseBackup.h = dst.h; + + // We draw the pre-scaled cursor image, so now we need to adjust for + // scaling, shake position and aspect ratio correction manually. + + if (!_overlayVisible) { + dst.y += _currentShakePos; + } + + if (_adjustAspectRatio && !_overlayVisible) + dst.y = real2Aspect(dst.y); + + dst.x = scale * dst.x - _mouseCurState.rHotX; + dst.y = scale * dst.y - _mouseCurState.rHotY; + dst.w = _mouseCurState.rW; + dst.h = _mouseCurState.rH; + + // Note that SDL_BlitSurface() and addDirtyRect() will both perform any + // clipping necessary + + if (SDL_BlitSurface(_mouseSurface, NULL, _hwscreen, &dst) != 0) + error("SDL_BlitSurface failed: %s", SDL_GetError()); + + // The screen will be updated using real surface coordinates, i.e. + // they will not be scaled or aspect-ratio corrected. + + addDirtyRect(dst.x, dst.y, dst.w, dst.h, true); +} + +#pragma mark - +#pragma mark --- On Screen Display --- +#pragma mark - + +#ifdef USE_OSD +void OSystem_SDL::displayMessageOnOSD(const char *msg) { + assert (_transactionMode == kTransactionNone); + assert(msg); + + uint i; + + // Lock the OSD surface for drawing + if (SDL_LockSurface(_osdSurface)) + error("displayMessageOnOSD: SDL_LockSurface failed: %s", SDL_GetError()); + + Graphics::Surface dst; + dst.pixels = _osdSurface->pixels; + dst.w = _osdSurface->w; + dst.h = _osdSurface->h; + dst.pitch = _osdSurface->pitch; + dst.bytesPerPixel = _osdSurface->format->BytesPerPixel; + + // The font we are going to use: + const Graphics::Font *font = FontMan.getFontByUsage(Graphics::FontManager::kOSDFont); + + // Clear everything with the "transparent" color, i.e. the colorkey + SDL_FillRect(_osdSurface, 0, kOSDColorKey); + + // Split the message into separate lines. + Common::StringList lines; + const char *ptr; + for (ptr = msg; *ptr; ++ptr) { + if (*ptr == '\n') { + lines.push_back(Common::String(msg, ptr - msg)); + msg = ptr + 1; + } + } + lines.push_back(Common::String(msg, ptr - msg)); + + // Determine a rect which would contain the message string (clipped to the + // screen dimensions). + const int vOffset = 6; + const int lineSpacing = 1; + const int lineHeight = font->getFontHeight() + 2 * lineSpacing; + int width = 0; + int height = lineHeight * lines.size() + 2 * vOffset; + for (i = 0; i < lines.size(); i++) { + width = MAX(width, font->getStringWidth(lines[i]) + 14); + } + + // Clip the rect + if (width > dst.w) + width = dst.w; + if (height > dst.h) + height = dst.h; + + // Draw a dark gray rect + // TODO: Rounded corners ? Border? + SDL_Rect osdRect; + osdRect.x = (dst.w - width) / 2; + osdRect.y = (dst.h - height) / 2; + osdRect.w = width; + osdRect.h = height; + SDL_FillRect(_osdSurface, &osdRect, SDL_MapRGB(_osdSurface->format, 64, 64, 64)); + + // Render the message, centered, and in white + for (i = 0; i < lines.size(); i++) { + font->drawString(&dst, lines[i], + osdRect.x, osdRect.y + i * lineHeight + vOffset + lineSpacing, osdRect.w, + SDL_MapRGB(_osdSurface->format, 255, 255, 255), + Graphics::kTextAlignCenter); + } + + // Finished drawing, so unlock the OSD surface again + SDL_UnlockSurface(_osdSurface); + + // Init the OSD display parameters, and the fade out + _osdAlpha = SDL_ALPHA_TRANSPARENT + kOSDInitialAlpha * (SDL_ALPHA_OPAQUE - SDL_ALPHA_TRANSPARENT) / 100; + _osdFadeStartTime = SDL_GetTicks() + kOSDFadeOutDelay; + SDL_SetAlpha(_osdSurface, SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA, _osdAlpha); + + // Ensure a full redraw takes place next time the screen is updated + _forceFull = true; +} +#endif + + +#pragma mark - +#pragma mark --- Misc --- +#pragma mark - + +void OSystem_SDL::handleScalerHotkeys(const SDL_KeyboardEvent &key) { + // Ctrl-Alt-a toggles aspect ratio correction + if (key.keysym.sym == 'a') { + setFeatureState(kFeatureAspectRatioCorrection, !_adjustAspectRatio); +#ifdef USE_OSD + char buffer[128]; + if (_adjustAspectRatio) + sprintf(buffer, "Enabled aspect ratio correction\n%d x %d -> %d x %d", + _screenWidth, _screenHeight, + _hwscreen->w, _hwscreen->h + ); + else + sprintf(buffer, "Disabled aspect ratio correction\n%d x %d -> %d x %d", + _screenWidth, _screenHeight, + _hwscreen->w, _hwscreen->h + ); + displayMessageOnOSD(buffer); +#endif + + return; + } + + int newMode = -1; + int factor = _scaleFactor - 1; + + // Increase/decrease the scale factor + if (key.keysym.sym == SDLK_EQUALS || key.keysym.sym == SDLK_PLUS || key.keysym.sym == SDLK_MINUS || + key.keysym.sym == SDLK_KP_PLUS || key.keysym.sym == SDLK_KP_MINUS) { + factor += (key.keysym.sym == SDLK_MINUS || key.keysym.sym == SDLK_KP_MINUS) ? -1 : +1; + if (0 <= factor && factor <= 3) { + newMode = s_gfxModeSwitchTable[_scalerType][factor]; + } + } + + const bool isNormalNumber = (SDLK_1 <= key.keysym.sym && key.keysym.sym <= SDLK_9); + const bool isKeypadNumber = (SDLK_KP1 <= key.keysym.sym && key.keysym.sym <= SDLK_KP9); + if (isNormalNumber || isKeypadNumber) { + _scalerType = key.keysym.sym - (isNormalNumber ? SDLK_1 : SDLK_KP1); + if (_scalerType >= ARRAYSIZE(s_gfxModeSwitchTable)) + return; + + while (s_gfxModeSwitchTable[_scalerType][factor] < 0) { + assert(factor > 0); + factor--; + } + newMode = s_gfxModeSwitchTable[_scalerType][factor]; + } + + if (newMode >= 0) { + setGraphicsMode(newMode); +#ifdef USE_OSD + if (_osdSurface) { + const char *newScalerName = 0; + const GraphicsMode *g = getSupportedGraphicsModes(); + while (g->name) { + if (g->id == _mode) { + newScalerName = g->description; + break; + } + g++; + } + if (newScalerName) { + char buffer[128]; + sprintf(buffer, "Active graphics filter: %s\n%d x %d -> %d x %d", + newScalerName, + _screenWidth, _screenHeight, + _hwscreen->w, _hwscreen->h + ); + displayMessageOnOSD(buffer); + } + } +#endif + + } + +} diff --git a/backends/platform/sdl/module.mk b/backends/platform/sdl/module.mk new file mode 100644 index 0000000000..18e56bc4fe --- /dev/null +++ b/backends/platform/sdl/module.mk @@ -0,0 +1,12 @@ +MODULE := backends/platform/sdl + +MODULE_OBJS := \ + events.o \ + graphics.o \ + sdl.o + +MODULE_DIRS += \ + backends/platform/sdl/ + +# We don't use the rules.mk here on purpose +OBJS := $(addprefix $(MODULE)/, $(MODULE_OBJS)) $(OBJS) diff --git a/backends/platform/sdl/sdl-common.h b/backends/platform/sdl/sdl-common.h new file mode 100644 index 0000000000..f4c9383c63 --- /dev/null +++ b/backends/platform/sdl/sdl-common.h @@ -0,0 +1,392 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef SDL_COMMON_H +#define SDL_COMMON_H + +#include <SDL.h> + +#include "common/stdafx.h" +#include "common/scummsys.h" +#include "common/system.h" +#include "graphics/scaler.h" +#include "backends/intern.h" + + +#if !defined(_WIN32_WCE) && !defined(__SYMBIAN32__) +// Uncomment this to enable the 'on screen display' code. +#define USE_OSD 1 +#endif + + +enum { + GFX_NORMAL = 0, + GFX_DOUBLESIZE = 1, + GFX_TRIPLESIZE = 2, + GFX_2XSAI = 3, + GFX_SUPER2XSAI = 4, + GFX_SUPEREAGLE = 5, + GFX_ADVMAME2X = 6, + GFX_ADVMAME3X = 7, + GFX_HQ2X = 8, + GFX_HQ3X = 9, + GFX_TV2X = 10, + GFX_DOTMATRIX = 11 +}; + + +class OSystem_SDL : public OSystem { +public: + OSystem_SDL(); + virtual ~OSystem_SDL(); + + virtual void initBackend(); + + void beginGFXTransaction(void); + void endGFXTransaction(void); + + // Set the size of the video bitmap. + // Typically, 320x200 + virtual void initSize(uint w, uint h); // overloaded by CE backend + + // Set colors of the palette + void setPalette(const byte *colors, uint start, uint num); + + // Get colors of the palette + void grabPalette(byte *colors, uint start, uint num); + + // Draw a bitmap to screen. + // The screen will not be updated to reflect the new bitmap + virtual void copyRectToScreen(const byte *src, int pitch, int x, int y, int w, int h); // overloaded by CE backend (FIXME) + + // Copies the screen to a buffer + bool grabRawScreen(Graphics::Surface *surf); + + // Clear the screen + void clearScreen(); + + // Update the dirty areas of the screen + void updateScreen(); + + // Either show or hide the mouse cursor + bool showMouse(bool visible); + + // Warp the mouse cursor. Where set_mouse_pos() only informs the + // backend of the mouse cursor's current position, this function + // actually moves the cursor to the specified position. + virtual void warpMouse(int x, int y); // overloaded by CE backend (FIXME) + + // Set the bitmap that's used when drawing the cursor. + virtual void setMouseCursor(const byte *buf, uint w, uint h, int hotspot_x, int hotspot_y, byte keycolor, int cursorTargetScale); // overloaded by CE backend (FIXME) + + // Set colors of cursor palette + void setCursorPalette(const byte *colors, uint start, uint num); + + // Disables or enables cursor palette + void disableCursorPalette(bool disable) { + _cursorPaletteDisabled = disable; + blitCursor(); + }; + + // Shaking is used in SCUMM. Set current shake position. + void setShakePos(int shake_pos); + + // Get the number of milliseconds since the program was started. + uint32 getMillis(); + + // Delay for a specified amount of milliseconds + void delayMillis(uint msecs); + + // Get the next event. + // Returns true if an event was retrieved. + virtual bool pollEvent(Event &event); // overloaded by CE backend + + // Set function that generates samples + virtual bool setSoundCallback(SoundProc proc, void *param); // overloaded by CE backend + + void clearSoundCallback(); + + // Poll CD status + // Returns true if cd audio is playing + bool pollCD(); + + // Play CD audio track + void playCD(int track, int num_loops, int start_frame, int duration); + + // Stop CD audio track + void stopCD(); + + // Update CD audio status + void updateCD(); + + // Quit + virtual void quit(); // overloaded by CE backend + + + // Add a callback timer + void setTimerCallback(TimerProc callback, int timer); + + // Mutex handling + MutexRef createMutex(); + void lockMutex(MutexRef mutex); + void unlockMutex(MutexRef mutex); + void deleteMutex(MutexRef mutex); + + // Overlay + virtual void showOverlay(); // WinCE FIXME + virtual void hideOverlay(); // WinCE FIXME + virtual void clearOverlay(); + virtual void grabOverlay(OverlayColor *buf, int pitch); + virtual void copyRectToOverlay(const OverlayColor *buf, int pitch, int x, int y, int w, int h); // WinCE FIXME + virtual int16 getHeight(); + virtual int16 getWidth(); + virtual int16 getOverlayHeight() { return _overlayHeight; } + virtual int16 getOverlayWidth() { return _overlayWidth; } + + // Methods that convert RGB to/from colors suitable for the overlay. + virtual OverlayColor RGBToColor(uint8 r, uint8 g, uint8 b); + virtual void colorToRGB(OverlayColor color, uint8 &r, uint8 &g, uint8 &b); + + + virtual const GraphicsMode *getSupportedGraphicsModes() const; + virtual int getDefaultGraphicsMode() const; + virtual bool setGraphicsMode(int mode); + virtual int getGraphicsMode() const; + + virtual void setWindowCaption(const char *caption); + virtual bool openCD(int drive); + virtual int getOutputSampleRate() const; + + virtual bool hasFeature(Feature f); + virtual void setFeatureState(Feature f, bool enable); + virtual bool getFeatureState(Feature f); + +#ifdef USE_OSD + void displayMessageOnOSD(const char *msg); +#endif + +protected: + bool _inited; + +#ifdef USE_OSD + SDL_Surface *_osdSurface; + Uint8 _osdAlpha; // Transparency level of the OSD + uint32 _osdFadeStartTime; // When to start the fade out + enum { + kOSDFadeOutDelay = 2 * 1000, // Delay before the OSD is faded out (in milliseconds) + kOSDFadeOutDuration = 500, // Duration of the OSD fade out (in milliseconds) + kOSDColorKey = 1, + kOSDInitialAlpha = 80 // Initial alpha level, in percent + }; +#endif + + // hardware screen + SDL_Surface *_hwscreen; + + // unseen game screen + SDL_Surface *_screen; + int _screenWidth, _screenHeight; + + // temporary screen (for scalers) + SDL_Surface *_tmpscreen; + SDL_Surface *_tmpscreen2; + + // overlay + SDL_Surface *_overlayscreen; + int _overlayWidth, _overlayHeight; + bool _overlayVisible; + + // Audio + int _samplesPerSec; + + // CD Audio + SDL_CD *_cdrom; + int _cdTrack, _cdNumLoops, _cdStartFrame, _cdDuration; + uint32 _cdEndTime, _cdStopTime; + + enum { + DF_WANT_RECT_OPTIM = 1 << 0, + DF_UPDATE_EXPAND_1_PIXEL = 1 << 1 + }; + + enum { + kTransactionNone = 0, + kTransactionCommit = 1, + kTransactionActive = 2 + }; + + struct TransactionDetails { + int mode; + bool modeChanged; + int w; + int h; + bool sizeChanged; + bool fs; + bool fsChanged; + bool ar; + bool arChanged; + bool needHotswap; + bool needUpdatescreen; + bool needUnload; + bool needToggle; + bool normal1xScaler; + }; + TransactionDetails _transactionDetails; + + /** Force full redraw on next updateScreen */ + bool _forceFull; + ScalerProc *_scalerProc; + int _scalerType; + int _scaleFactor; + int _mode; + int _transactionMode; + bool _fullscreen; + + /** Current video mode flags (see DF_* constants) */ + uint32 _modeFlags; + bool _modeChanged; + + /** True if aspect ratio correction is enabled. */ + bool _adjustAspectRatio; + + enum { + NUM_DIRTY_RECT = 100, + + MAX_MOUSE_W = 80, + MAX_MOUSE_H = 80, + MAX_SCALING = 3 + }; + + // Dirty rect management + SDL_Rect _dirtyRectList[NUM_DIRTY_RECT]; + int _numDirtyRects; + uint32 *_dirtyChecksums; + bool _cksumValid; + int _cksumNum; + + // Keyboard mouse emulation. Disabled by fingolfin 2004-12-18. + // I am keeping the rest of the code in for now, since the joystick + // code (or rather, "hack") uses it, too. + struct KbdMouse { + int16 x, y, x_vel, y_vel, x_max, y_max, x_down_count, y_down_count; + uint32 last_time, delay_time, x_down_time, y_down_time; + }; + + struct MousePos { + // The mouse position, using either virtual (game) or real + // (overlay) coordinates. + int16 x, y; + + // The size and hotspot of the original cursor image. + int16 w, h; + int16 hotX, hotY; + + // The size and hotspot of the pre-scaled cursor image, in real + // coordinates. + int16 rW, rH; + int16 rHotX, rHotY; + + // The size and hotspot of the pre-scaled cursor image, in game + // coordinates. + int16 vW, vH; + int16 vHotX, vHotY; + + MousePos() : x(0), y(0), w(0), h(0), hotX(0), hotY(0), + rW(0), rH(0), rHotX(0), rHotY(0), vW(0), vH(0), + vHotX(0), vHotY(0) + { } + }; + + // mouse + KbdMouse _km; + bool _mouseVisible; + bool _mouseDrawn; + byte *_mouseData; + SDL_Rect _mouseBackup; + MousePos _mouseCurState; + byte _mouseKeyColor; + int _cursorTargetScale; + bool _cursorPaletteDisabled; + SDL_Surface *_mouseOrigSurface; + SDL_Surface *_mouseSurface; + enum { + kMouseColorKey = 1 + }; + + // joystick + SDL_Joystick *_joystick; + + // Shake mode + int _currentShakePos; + int _newShakePos; + + // Palette data + SDL_Color *_currentPalette; + uint _paletteDirtyStart, _paletteDirtyEnd; + + // Cursor palette data + SDL_Color *_cursorPalette; + + /** + * Mutex which prevents multiple threads from interfering with each other + * when accessing the screen. + */ + MutexRef _graphicsMutex; + + + void addDirtyRgnAuto(const byte *buf); + void makeChecksums(const byte *buf); + + virtual void addDirtyRect(int x, int y, int w, int h, bool realCoordinates = false); // overloaded by CE backend + + virtual void drawMouse(); // overloaded by CE backend + virtual void undrawMouse(); // overloaded by CE backend (FIXME) + virtual void blitCursor(); // overloaded by CE backend (FIXME) + + /** Set the position of the virtual mouse cursor. */ + void setMousePos(int x, int y); + virtual void fillMouseEvent(Event &event, int x, int y); // overloaded by CE backend + void toggleMouseGrab(); + + virtual void internUpdateScreen(); // overloaded by CE backend + + virtual void loadGFXMode(); // overloaded by CE backend + virtual void unloadGFXMode(); // overloaded by CE backend + virtual void hotswapGFXMode(); // overloaded by CE backend + + void setFullscreenMode(bool enable); + void setAspectRatioCorrection(bool enable); + + virtual bool saveScreenshot(const char *filename); // overloaded by CE backend + + int effectiveScreenHeight() const { return (_adjustAspectRatio ? 240 : _screenHeight) * _scaleFactor; } + + void setupIcon(); + void handleKbdMouse(); + + virtual bool remapKey(SDL_Event &ev, Event &event); + + void handleScalerHotkeys(const SDL_KeyboardEvent &key); +}; + +#endif diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp new file mode 100644 index 0000000000..b6d2d675df --- /dev/null +++ b/backends/platform/sdl/sdl.cpp @@ -0,0 +1,488 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "backends/platform/sdl/sdl-common.h" +#include "common/config-manager.h" +#include "common/util.h" +#include "base/main.h" + +#include "icons/scummvm.xpm" + +#if defined(__SYMBIAN32__) +#include "SymbianOs.h" +#endif + +#if !defined(_WIN32_WCE) && !defined(__MAEMO__) + +#if defined (WIN32) +int __stdcall WinMain(HINSTANCE /*hInst*/, HINSTANCE /*hPrevInst*/, LPSTR /*lpCmdLine*/, int /*iShowCmd*/) { + SDL_SetModuleHandle(GetModuleHandle(NULL)); + return main(__argc, __argv); +} +#endif + +int main(int argc, char *argv[]) { + +#if defined(__SYMBIAN32__) + // + // Set up redirects for stdout/stderr under Windows and Symbian. + // Code copied from SDL_main. + // + + // Symbian does not like any output to the console through any *print* function + char STDOUT_FILE[256], STDERR_FILE[256]; // shhh, don't tell anybody :) + strcpy(STDOUT_FILE, Symbian::GetExecutablePath()); + strcpy(STDERR_FILE, Symbian::GetExecutablePath()); + strcat(STDOUT_FILE, "scummvm.stdout.txt"); + strcat(STDERR_FILE, "scummvm.stderr.txt"); + + /* Flush the output in case anything is queued */ + fclose(stdout); + fclose(stderr); + + /* Redirect standard input and standard output */ + FILE *newfp = freopen(STDOUT_FILE, "w", stdout); + if (newfp == NULL) { /* This happens on NT */ +#if !defined(stdout) + stdout = fopen(STDOUT_FILE, "w"); +#else + newfp = fopen(STDOUT_FILE, "w"); + if (newfp) { + *stdout = *newfp; + } +#endif + } + newfp = freopen(STDERR_FILE, "w", stderr); + if (newfp == NULL) { /* This happens on NT */ +#if !defined(stderr) + stderr = fopen(STDERR_FILE, "w"); +#else + newfp = fopen(STDERR_FILE, "w"); + if (newfp) { + *stderr = *newfp; + } +#endif + } + setbuf(stderr, NULL); /* No buffering */ + +#endif // defined(__SYMBIAN32__) + + // Create our OSystem instance +#if defined(__SYMBIAN32__) + g_system = new OSystem_SDL_Symbian(); +#else + g_system = new OSystem_SDL(); +#endif + assert(g_system); + + // Invoke the actual ScummVM main entry point: + int res = scummvm_main(argc, argv); + g_system->quit(); // TODO: Consider removing / replacing this! + return res; +} +#endif + +void OSystem_SDL::initBackend() { + assert(!_inited); + + int joystick_num = ConfMan.getInt("joystick_num"); + uint32 sdlFlags = SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER; + + if (ConfMan.hasKey("disable_sdl_parachute")) + sdlFlags |= SDL_INIT_NOPARACHUTE; + +#ifdef _WIN32_WCE + if (ConfMan.hasKey("use_GDI") && ConfMan.getBool("use_GDI")) { + SDL_VideoInit("windib", 0); + sdlFlags ^= SDL_INIT_VIDEO; + } +#endif + + if (joystick_num > -1) + sdlFlags |= SDL_INIT_JOYSTICK; + + if (SDL_Init(sdlFlags) == -1) { + error("Could not initialize SDL: %s", SDL_GetError()); + } + + _graphicsMutex = createMutex(); + + SDL_ShowCursor(SDL_DISABLE); + + // Enable unicode support if possible + SDL_EnableUNICODE(1); + + _cksumValid = false; +#if !defined(_WIN32_WCE) && !defined(__SYMBIAN32__) && !defined(DISABLE_SCALERS) + _mode = GFX_DOUBLESIZE; + _scaleFactor = 2; + _scalerProc = Normal2x; + _fullscreen = ConfMan.getBool("fullscreen"); + _adjustAspectRatio = ConfMan.getBool("aspect_ratio"); +#else // for small screen platforms + _mode = GFX_NORMAL; + _scaleFactor = 1; + _scalerProc = Normal1x; + +#if !defined(_WIN32_WCE) && !defined(__SYMBIAN32__) + _fullscreen = ConfMan.getBool("fullscreen"); +#else + _fullscreen = true; +#endif + + _adjustAspectRatio = false; +#endif + _scalerType = 0; + _modeFlags = 0; + +#if !defined(MACOSX) && !defined(__SYMBIAN32__) // Don't set icon on OS X, as we use a nicer external icon there + setupIcon(); // Don't for Symbian: it uses the EScummVM.aif file for the icon +#endif + + // enable joystick + if (joystick_num > -1 && SDL_NumJoysticks() > 0) { + printf("Using joystick: %s\n", SDL_JoystickName(0)); + _joystick = SDL_JoystickOpen(joystick_num); + } + + _inited = true; +} + +OSystem_SDL::OSystem_SDL() + : +#ifdef USE_OSD + _osdSurface(0), _osdAlpha(SDL_ALPHA_TRANSPARENT), _osdFadeStartTime(0), +#endif + _hwscreen(0), _screen(0), _screenWidth(0), _screenHeight(0), + _tmpscreen(0), _overlayWidth(0), _overlayHeight(0), + _overlayVisible(false), + _overlayscreen(0), _tmpscreen2(0), + _samplesPerSec(0), + _cdrom(0), _scalerProc(0), _modeChanged(false), _dirtyChecksums(0), + _mouseVisible(false), _mouseDrawn(false), _mouseData(0), _mouseSurface(0), + _mouseOrigSurface(0), _cursorTargetScale(1), _cursorPaletteDisabled(true), + _joystick(0), + _currentShakePos(0), _newShakePos(0), + _paletteDirtyStart(0), _paletteDirtyEnd(0), + _graphicsMutex(0), _transactionMode(kTransactionNone) { + + // allocate palette storage + _currentPalette = (SDL_Color *)calloc(sizeof(SDL_Color), 256); + _cursorPalette = (SDL_Color *)calloc(sizeof(SDL_Color), 256); + + _mouseBackup.x = _mouseBackup.y = _mouseBackup.w = _mouseBackup.h = 0; + + // reset mouse state + memset(&_km, 0, sizeof(_km)); + memset(&_mouseCurState, 0, sizeof(_mouseCurState)); + + _inited = false; +} + +OSystem_SDL::~OSystem_SDL() { + free(_dirtyChecksums); + free(_currentPalette); + free(_cursorPalette); + free(_mouseData); +} + +uint32 OSystem_SDL::getMillis() { + return SDL_GetTicks(); +} + +void OSystem_SDL::delayMillis(uint msecs) { + SDL_Delay(msecs); +} + +void OSystem_SDL::setTimerCallback(TimerProc callback, int timer) { + SDL_SetTimer(timer, (SDL_TimerCallback) callback); +} + +void OSystem_SDL::setWindowCaption(const char *caption) { + SDL_WM_SetCaption(caption, caption); +} + +bool OSystem_SDL::hasFeature(Feature f) { + return + (f == kFeatureFullscreenMode) || + (f == kFeatureAspectRatioCorrection) || + (f == kFeatureAutoComputeDirtyRects) || + (f == kFeatureCursorHasPalette); +} + +void OSystem_SDL::setFeatureState(Feature f, bool enable) { + switch (f) { + case kFeatureFullscreenMode: + setFullscreenMode(enable); + break; + case kFeatureAspectRatioCorrection: + setAspectRatioCorrection(enable); + break; + case kFeatureAutoComputeDirtyRects: + if (enable) + _modeFlags |= DF_WANT_RECT_OPTIM; + else + _modeFlags &= ~DF_WANT_RECT_OPTIM; + break; + default: + break; + } +} + +bool OSystem_SDL::getFeatureState(Feature f) { + assert (_transactionMode == kTransactionNone); + + switch (f) { + case kFeatureFullscreenMode: + return _fullscreen; + case kFeatureAspectRatioCorrection: + return _adjustAspectRatio; + case kFeatureAutoComputeDirtyRects: + return _modeFlags & DF_WANT_RECT_OPTIM; + default: + return false; + } +} + +void OSystem_SDL::quit() { + if (_cdrom) { + SDL_CDStop(_cdrom); + SDL_CDClose(_cdrom); + } + unloadGFXMode(); + deleteMutex(_graphicsMutex); + + if (_joystick) + SDL_JoystickClose(_joystick); + SDL_ShowCursor(SDL_ENABLE); + SDL_Quit(); + + exit(0); +} + +void OSystem_SDL::setupIcon() { + 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++) { + const char *line = scummvm_icon[1 + ncols + h]; + for (w = 0; w < 32; w++) { + icon[w + 32 * h] = rgba[(int)line[w]]; + if (rgba[(int)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); + SDL_FreeSurface(sdl_surf); +} + +OSystem::MutexRef OSystem_SDL::createMutex(void) { + return (MutexRef) SDL_CreateMutex(); +} + +void OSystem_SDL::lockMutex(MutexRef mutex) { + SDL_mutexP((SDL_mutex *) mutex); +} + +void OSystem_SDL::unlockMutex(MutexRef mutex) { + SDL_mutexV((SDL_mutex *) mutex); +} + +void OSystem_SDL::deleteMutex(MutexRef mutex) { + SDL_DestroyMutex((SDL_mutex *) mutex); +} + +#pragma mark - +#pragma mark --- Audio --- +#pragma mark - + +bool OSystem_SDL::setSoundCallback(SoundProc proc, void *param) { + SDL_AudioSpec desired; + SDL_AudioSpec obtained; + + memset(&desired, 0, sizeof(desired)); + + _samplesPerSec = 0; + + if (ConfMan.hasKey("output_rate")) + _samplesPerSec = ConfMan.getInt("output_rate"); + + if (_samplesPerSec <= 0) + _samplesPerSec = SAMPLES_PER_SEC; + + // Originally, we always used 2048 samples. This loop will produce the + // same result at 22050 Hz, and should hopefully produce something + // sensible for other frequencies. Note that it must be a power of two. + + uint32 samples = 0x8000; + + for (;;) { + if ((1000 * samples) / _samplesPerSec < 100) + break; + samples >>= 1; + } + + desired.freq = _samplesPerSec; + desired.format = AUDIO_S16SYS; + desired.channels = 2; + desired.samples = (uint16)samples; + desired.callback = proc; + desired.userdata = param; + if (SDL_OpenAudio(&desired, &obtained) != 0) { + warning("Could not open audio device: %s", SDL_GetError()); + return false; + } + // Note: This should be the obtained output rate, but it seems that at + // least on some platforms SDL will lie and claim it did get the rate + // even if it didn't. Probably only happens for "weird" rates, though. + _samplesPerSec = obtained.freq; + SDL_PauseAudio(0); + return true; +} + +void OSystem_SDL::clearSoundCallback() { + SDL_CloseAudio(); +} + +int OSystem_SDL::getOutputSampleRate() const { + return _samplesPerSec; +} + +#pragma mark - +#pragma mark --- CD Audio --- +#pragma mark - + +bool OSystem_SDL::openCD(int drive) { + if (SDL_InitSubSystem(SDL_INIT_CDROM) == -1) + _cdrom = NULL; + else { + _cdrom = SDL_CDOpen(drive); + // Did it open? Check if _cdrom is NULL + if (!_cdrom) { + warning("Couldn't open drive: %s", SDL_GetError()); + } else { + _cdNumLoops = 0; + _cdStopTime = 0; + _cdEndTime = 0; + } + } + + return (_cdrom != NULL); +} + +void OSystem_SDL::stopCD() { /* Stop CD Audio in 1/10th of a second */ + _cdStopTime = SDL_GetTicks() + 100; + _cdNumLoops = 0; +} + +void OSystem_SDL::playCD(int track, int num_loops, int start_frame, int duration) { + if (!num_loops && !start_frame) + return; + + if (!_cdrom) + return; + + if (duration > 0) + duration += 5; + + _cdTrack = track; + _cdNumLoops = num_loops; + _cdStartFrame = start_frame; + + SDL_CDStatus(_cdrom); + if (start_frame == 0 && duration == 0) + SDL_CDPlayTracks(_cdrom, track, 0, 1, 0); + else + SDL_CDPlayTracks(_cdrom, track, start_frame, 0, duration); + _cdDuration = duration; + _cdStopTime = 0; + _cdEndTime = SDL_GetTicks() + _cdrom->track[track].length * 1000 / CD_FPS; +} + +bool OSystem_SDL::pollCD() { + if (!_cdrom) + return false; + + return (_cdNumLoops != 0 && (SDL_GetTicks() < _cdEndTime || SDL_CDStatus(_cdrom) != CD_STOPPED)); +} + +void OSystem_SDL::updateCD() { + if (!_cdrom) + return; + + if (_cdStopTime != 0 && SDL_GetTicks() >= _cdStopTime) { + SDL_CDStop(_cdrom); + _cdNumLoops = 0; + _cdStopTime = 0; + return; + } + + if (_cdNumLoops == 0 || SDL_GetTicks() < _cdEndTime) + return; + + if (_cdNumLoops != 1 && SDL_CDStatus(_cdrom) != CD_STOPPED) { + // Wait another second for it to be done + _cdEndTime += 1000; + return; + } + + if (_cdNumLoops > 0) + _cdNumLoops--; + + if (_cdNumLoops != 0) { + if (_cdStartFrame == 0 && _cdDuration == 0) + SDL_CDPlayTracks(_cdrom, _cdTrack, 0, 1, 0); + else + SDL_CDPlayTracks(_cdrom, _cdTrack, _cdStartFrame, 0, _cdDuration); + _cdEndTime = SDL_GetTicks() + _cdrom->track[_cdTrack].length * 1000 / CD_FPS; + } +} diff --git a/backends/platform/x11/build.rules b/backends/platform/x11/build.rules new file mode 100644 index 0000000000..1540d452d6 --- /dev/null +++ b/backends/platform/x11/build.rules @@ -0,0 +1,7 @@ +# Build settings for the X11 backend +MODULES += backends/x11 +OBJS += backends/x11/x11.o +DEFINES += -DUNIX -DX11_BACKEND +LDFLAGS += -L/usr/X11R6/lib -L/usr/local/lib +INCLUDES+= -I/usr/X11R6/include +LIBS += -lpthread -lXext -lX11 diff --git a/backends/platform/x11/module.mk b/backends/platform/x11/module.mk new file mode 100644 index 0000000000..22015b53be --- /dev/null +++ b/backends/platform/x11/module.mk @@ -0,0 +1,10 @@ +MODULE := backends/platform/x11 + +MODULE_OBJS := \ + x11.o + +MODULE_DIRS += \ + backends/platform/x11/ + +# We don't use the rules.mk here on purpose +OBJS := $(addprefix $(MODULE)/, $(MODULE_OBJS)) $(OBJS) diff --git a/backends/platform/x11/x11.cpp b/backends/platform/x11/x11.cpp new file mode 100644 index 0000000000..ce020c7a8e --- /dev/null +++ b/backends/platform/x11/x11.cpp @@ -0,0 +1,1037 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +/* The bare pure X11 port done by Lionel 'BBrox' Ulmer */ + +#include "common/stdafx.h" +#include "common/scummsys.h" +#include "common/system.h" +#include "common/util.h" +#include "base/main.h" +#include "backends/intern.h" +#include "backends/platform/x11/x11.h" + +#include <stdio.h> +#include <assert.h> + +#include <sys/time.h> +#include <unistd.h> + +#include <sys/ipc.h> +#include <sys/shm.h> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/extensions/XShm.h> + +#ifdef __linux__ +#include <linux/soundcard.h> +#else +#include <sys/soundcard.h> +#endif + +#include <sched.h> +#include <pthread.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/ioctl.h> + +int main(int argc, char *argv[]) { + g_system = OSystem_X11::create(0, 0); + assert(g_system); + + // Invoke the actual ScummVM main entry point: + int res = scummvm_main(argc, argv); + g_system->quit(); // TODO: Consider removing / replacing this! + return res; +} + +OSystem *OSystem_X11::create(int gfx_mode, bool full_screen) { + OSystem_X11 *syst = new OSystem_X11(); + return syst; +} + +OSystem_X11::OSystem_X11() { + /* Some members initialization */ + _fake_right_mouse = 0; + _report_presses = 1; + _current_shake_pos = 0; + _new_shake_pos = 0; + _palette_changed = false; + _num_of_dirty_rects = 0; + _overlay_visible = false; + _mouse_state_changed = true; + _mouse_visible = true; + _ms_buf = NULL; + _curMouseState.x = 0; + _curMouseState.y = 0; + _curMouseState.hot_x = 0; + _curMouseState.hot_y = 0; + _curMouseState.w = 0; + _curMouseState.h = 0; + _palette16 = 0; + _palette32 = 0; + _bytesPerPixel = 0; + _image = 0; + _local_fb = 0; + _local_fb_overlay = 0; +} + +OSystem_X11::~OSystem_X11() { + XFree(_image); + if (_palette16) + free(_palette16); + + if (_palette32) + free(_palette32); + + if (_ms_buf) + free(_ms_buf); + + free(_local_fb_overlay); + free(_local_fb); +} + +void OSystem_X11::initBackend() { + char buf[512]; + XWMHints *wm_hints; + XGCValues values; + XTextProperty window_name; + char *name = (char *)&buf; + /* For the_window title */ + sprintf(buf, "ScummVM"); + + _display = XOpenDisplay(NULL); + if (_display == NULL) { + error("Could not open display !\n"); + exit(1); + } + + if (XShmQueryExtension(_display)!=True) + error("No Shared Memory Extension present"); + + _screen = DefaultScreen(_display); + _depth = DefaultDepth(_display, _screen); + switch (_depth) { + case 16 : + _bytesPerPixel = 2; + break; + case 24 : + case 32 : + _bytesPerPixel = 4; + break; + } + + if (!_bytesPerPixel) + error("Your screen depth is %ibit. Values other than 16, 24 and 32bit are currently not supported", _depth); + + _window_width = 320; + _window_height = 200; + _scumm_x = 0; + _scumm_y = 0; + _window = XCreateSimpleWindow(_display, XRootWindow(_display, _screen), 0, 0, 320, 200, 0, 0, 0); + wm_hints = XAllocWMHints(); + if (wm_hints == NULL) { + error("Not enough memory to allocate Hints !\n"); + exit(1); + } + wm_hints->flags = InputHint | StateHint; + wm_hints->input = True; + wm_hints->initial_state = NormalState; + XStringListToTextProperty(&name, 1, &window_name); + XSetWMProperties(_display, _window, &window_name, &window_name, + NULL /* argv */ , 0 /* argc */ , NULL /* size hints */ , + wm_hints, NULL /* class hints */ ); + XFree(wm_hints); + + XSelectInput(_display, _window, + ExposureMask | KeyPressMask | KeyReleaseMask | + PointerMotionMask | ButtonPressMask | ButtonReleaseMask | StructureNotifyMask); + + values.foreground = BlackPixel(_display, _screen); + _black_gc = XCreateGC(_display, _window, GCForeground, &values); + + XMapWindow(_display, _window); + XFlush(_display); + + _fb_width = 0; + _fb_height = 0; + + if (!_palette16) + _palette16 = (uint16 *)calloc(256, sizeof(uint16)); + if (!_palette32 && _bytesPerPixel == 4) + _palette32 = (uint32 *)calloc(256, sizeof(uint32)); + + while (1) { + XEvent event; + XNextEvent(_display, &event); + switch (event.type) { + case Expose: + goto out_of_loop; + } + } +out_of_loop: + create_empty_cursor(); + + /* Initialize the timer routines */ + _timer_active = false; + + /* And finally start the local timer */ + gettimeofday(&_start_time, NULL); + +} + +#undef CAPTURE_SOUND +#define FRAG_SIZE 4096 + +static void *sound_and_music_thread(void *params) { + /* Init sound */ + int sound_fd, param, frag_size; + uint8 sound_buffer[FRAG_SIZE]; + OSystem::SoundProc sound_proc = ((THREAD_PARAM *)params)->sound_proc; + void *proc_param = ((THREAD_PARAM *)params)->param; + +#ifdef CAPTURE_SOUND + FILE *f = fopen("sound.raw", "wb"); +#endif + + sound_fd = open("/dev/dsp", O_WRONLY); + audio_buf_info info; + if (sound_fd < 0) { + warning("Error opening sound device!\n"); + return NULL; + } + param = 0; + frag_size = FRAG_SIZE /* audio fragment size */ ; + while (frag_size) { + frag_size >>= 1; + param++; + } + param--; + param |= /* audio_fragment_num */ 3 << 16; + if (ioctl(sound_fd, SNDCTL_DSP_SETFRAGMENT, ¶m) != 0) { + warning("Error in the SNDCTL_DSP_SETFRAGMENT ioctl!\n"); + return NULL; + } + param = AFMT_S16_LE; + if (ioctl(sound_fd, SNDCTL_DSP_SETFMT, ¶m) == -1) { + warning("Error in the SNDCTL_DSP_SETFMT ioctl!\n"); + return NULL;; + } + if (param != AFMT_S16_LE) { + warning("AFMT_S16_LE not supported!\n"); + return NULL; + } + param = 2; + if (ioctl(sound_fd, SNDCTL_DSP_CHANNELS, ¶m) == -1) { + warning("Error in the SNDCTL_DSP_CHANNELS ioctl!\n"); + return NULL; + } + if (param != 2) { + warning("Stereo mode not supported!\n"); + return NULL; + } + param = SAMPLES_PER_SEC; + if (ioctl(sound_fd, SNDCTL_DSP_SPEED, ¶m) == -1) { + warning("Error in the SNDCTL_DSP_SPEED ioctl!\n"); + return NULL; + } + if (param != SAMPLES_PER_SEC) { + warning("%d kHz not supported!\n", SAMPLES_PER_SEC); + return NULL; + } + if (ioctl(sound_fd, SNDCTL_DSP_GETOSPACE, &info) != 0) { + warning("SNDCTL_DSP_GETOSPACE"); + return NULL; + } + + sched_yield(); + while (1) { + uint8 *buf = (uint8 *)sound_buffer; + int size, written; + + sound_proc(proc_param, (byte *)sound_buffer, FRAG_SIZE); +#ifdef CAPTURE_SOUND + fwrite(buf, 2, FRAG_SIZE >> 1, f); + fflush(f); +#endif + size = FRAG_SIZE; + while (size > 0) { + written = write(sound_fd, buf, size); + buf += written; + size -= written; + } + } + + return NULL; +} + +/* Function used to hide the mouse cursor */ +void OSystem_X11::create_empty_cursor() { + XColor bg; + Pixmap pixmapBits; + Cursor cursor = None; + static const char data[] = { 0 }; + + bg.red = bg.green = bg.blue = 0x0000; + pixmapBits = XCreateBitmapFromData(_display, XRootWindow(_display, _screen), data, 1, 1); + if (pixmapBits) { + cursor = XCreatePixmapCursor(_display, pixmapBits, pixmapBits, &bg, &bg, 0, 0); + XFreePixmap(_display, pixmapBits); + } + XDefineCursor(_display, _window, cursor); +} + +bool OSystem_X11::hasFeature(Feature f) { + return false; +} + +void OSystem_X11::setFeatureState(Feature f, bool enable) { +} + +bool OSystem_X11::getFeatureState(Feature f) { + return false; +} + +const OSystem::GraphicsMode *OSystem_X11::getSupportedGraphicsModes() const { + static const OSystem::GraphicsMode mode = {"1x", "Normal mode", 0}; + return &mode; +} + +int OSystem_X11::getDefaultGraphicsMode() const { + return 0; +} + +bool OSystem_X11::setGraphicsMode(int mode) { + return (mode == 0); +} + +int OSystem_X11::getGraphicsMode() const { + return 0; +} + + +uint32 OSystem_X11::getMillis() { + struct timeval current_time; + gettimeofday(¤t_time, NULL); + return (uint32)(((current_time.tv_sec - _start_time.tv_sec) * 1000) + + ((current_time.tv_usec - _start_time.tv_usec) / 1000)); +} + +void OSystem_X11::initSize(uint w, uint h) { + //debug("initSize(%i, %i)", w, h); + static XShmSegmentInfo shminfo; + + if (((uint)_fb_width != w) || ((uint)_fb_height != w)) { + _fb_width = w; + _fb_height = h; + + /* We need to change the size of the X11_window */ + XWindowChanges new_values; + + new_values.width = _fb_width; + new_values.height = _fb_height; + + XConfigureWindow(_display,_window, CWWidth | CWHeight, &new_values); + + if (_image) + XFree(_image); + _image = XShmCreateImage(_display, DefaultVisual(_display, _screen), _depth, ZPixmap, NULL, &shminfo,_fb_width,_fb_height); + if (!_image) + error("Couldn't get image by XShmCreateImage()"); + + shminfo.shmid = shmget(IPC_PRIVATE, _image->bytes_per_line * _image->height, IPC_CREAT | 0700); + if (shminfo.shmid < 0) + error("Couldn't allocate image data by shmget()"); + + _image->data = shminfo.shmaddr = (char *)shmat(shminfo.shmid, 0, 0); + shminfo.readOnly = False; + if (XShmAttach(_display, &shminfo) == 0) { + error("Could not attach shared memory segment !\n"); + exit(1); + } + shmctl(shminfo.shmid, IPC_RMID, 0); + + if (_local_fb) + free(_local_fb); + if (_local_fb_overlay) + free(_local_fb_overlay); + /* Initialize the 'local' frame buffer and the palette */ + _local_fb = (uint8 *)calloc(_fb_width * _fb_height, sizeof(uint8)); + _local_fb_overlay = (uint16 *)calloc(_fb_width * _fb_height, sizeof(uint16)); + + } +} + +bool OSystem_X11::setSoundCallback(SoundProc proc, void *param) { + static THREAD_PARAM thread_param; + + /* And finally start the music thread */ + thread_param.param = param; + thread_param.sound_proc = proc; + + pthread_create(&_sound_thread, NULL, sound_and_music_thread, (void *)&thread_param); + + return true; +} + +void OSystem_X11::clearSoundCallback() { + // TODO implement this... + // The sound_thread has to be stopped in a nice way. In particular, + // using pthread_kill would be a bad idea. Rather, use pthread_cancel, + // or maybe a global variable, to achieve this. + // This method shouldn't return until the sound thread really has stopped. +} + + +void OSystem_X11::setPalette(const byte *colors, uint start, uint num) { + uint16 *pal = &(_palette16[start]); + const byte *data = colors; + + if (_bytesPerPixel == 4) { + for (uint i = start; i < start+num; i++) { + //_palette32[i] = ((uint32 *)colors)[i]; + _palette32[i] = (colors[i * 4 + 0] << 16) | (colors[i * 4 + 1] << 8) | (colors[i * 4 + 2] << 0); + } + } + + do { + *pal++ = ((data[0] & 0xF8) << 8) | ((data[1] & 0xFC) << 3) | (data[2] >> 3); + data += 4; + num--; + } while (num > 0); + + _palette_changed = true; +} + +#define AddDirtyRec(xi,yi,wi,hi) \ + if (_num_of_dirty_rects < MAX_NUMBER_OF_DIRTY_RECTS) { \ + _ds[_num_of_dirty_rects].x = xi; \ + _ds[_num_of_dirty_rects].y = yi; \ + _ds[_num_of_dirty_rects].w = wi; \ + _ds[_num_of_dirty_rects].h = hi; \ + _num_of_dirty_rects++; \ + } + +void OSystem_X11::copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h) { + uint8 *dst; + + if (y < 0) { + h += y; + buf -= y * pitch; + y = 0; + } + if (h > (_fb_height - y)) { + h = _fb_height - y; + } + + dst = _local_fb + _fb_width * y + x; + + if (h <= 0) + return; + + AddDirtyRec(x, y, w, h); + while (h-- > 0) { + memcpy(dst, buf, w); + dst +=_fb_width; + buf += pitch; + } +} + +void OSystem_X11::blit(const DirtyRect *d, uint16 *dst, int pitch) { + uint8 *ptr_src = _local_fb + (_fb_width * d->y) + d->x; + uint16 *ptr_dst = dst + ((_fb_width * d->y) + d->x); + int x, y; + + for (y = 0; y < d->h; y++) { + for (x = 0; x < d->w; x++) { + *ptr_dst++ = _palette16[*ptr_src++]; + } + ptr_dst += pitch - d->w; + ptr_src +=_fb_width - d->w; + } +} + +void OSystem_X11::blit_convert(const DirtyRect *d, uint8 *dst, int pitch) { + uint8 *ptr_src = _local_fb + (_fb_width * d->y) + d->x; + uint8 *ptr_dst = dst + ((_fb_width * d->y) + d->x) * _bytesPerPixel; + int x, y; + + switch (_bytesPerPixel) { + case 2: + for (y = 0; y < d->h; y++) { + for (x = 0; x < d->w; x++) { + *ptr_dst = _palette16[*ptr_src++]; + ptr_dst += _bytesPerPixel; + } + ptr_dst += (pitch - d->w) * _bytesPerPixel; + ptr_src +=_fb_width - d->w; + } + break; + case 4: + for (y = 0; y < d->h; y++) { + for (x = 0; x < d->w; x++) { + *(uint32 *)ptr_dst = _palette32[*ptr_src]; + ptr_dst += _bytesPerPixel; + ptr_src++; + } + ptr_dst += (pitch - d->w) * _bytesPerPixel; + ptr_src += _fb_width - d->w; + } + } +} + +void OSystem_X11::updateScreen_helper(const DirtyRect *d, DirtyRect *dout) { + + if (_overlay_visible == false) { + blit_convert(d, (uint8 *)_image->data, _fb_width); + } else { + uint16 *ptr_src = _local_fb_overlay + (_fb_width * d->y) + d->x; + uint8 *ptr_dst = (uint8 *)_image->data + ((_fb_width * d->y) + d->x) * _bytesPerPixel; + + int y; + + switch (_bytesPerPixel) { + case 2: + for (y = 0; y < d->h; y++) { + memcpy(ptr_dst, ptr_src, d->w * sizeof(uint16)); + ptr_dst += _fb_width * sizeof(uint16); + ptr_src += _fb_width; + } + break; + case 4: + uint16 currLine, x; + register uint16 currPixel; + for (y = d->y; y < d->y + d->h; y++) { + currLine = y * _fb_width; + for (x = d->x; x < d->x + d->w; x++) { + currPixel = _local_fb_overlay[(currLine + x)]; + *(uint32 *)ptr_dst = ((currPixel & 0xF800) << 8) + ((currPixel & 0x07E0) << 5) + + ((currPixel & 0x001F) << 3); + ptr_dst += sizeof(uint32); + } + ptr_dst += (_fb_width - d->w) * _bytesPerPixel; + } + + } + } + if (d->x < dout->x) + dout->x = d->x; + if (d->y < dout->y) + dout->y = d->y; + if ((d->x + d->w) > dout->w) + dout->w = d->x + d->w; + if ((d->y + d->h) > dout->h) + dout->h = d->y + d->h; +} + +void OSystem_X11::updateScreen() { + bool full_redraw = false; + bool need_redraw = false; + static const DirtyRect ds_full = { 0, 0, _fb_width, _fb_height }; + DirtyRect dout = {_fb_width, _fb_height, 0, 0 }; + + if (_palette_changed) { + full_redraw = true; + _num_of_dirty_rects = 0; + _palette_changed = false; + } else if (_num_of_dirty_rects >= MAX_NUMBER_OF_DIRTY_RECTS) { + full_redraw = true; + _num_of_dirty_rects = 0; + } + + if (full_redraw) { + updateScreen_helper(&ds_full, &dout); + need_redraw = true; + } else if ((_num_of_dirty_rects > 0) || (_mouse_state_changed == true)) { + need_redraw = true; + while (_num_of_dirty_rects > 0) { + _num_of_dirty_rects--; + updateScreen_helper(&(_ds[_num_of_dirty_rects]), &dout); + } + } + + /* Then 'overlay' the mouse on the image */ + draw_mouse(&dout); + + if (_current_shake_pos != _new_shake_pos) { + /* Redraw first the 'black borders' in case of resize */ + if (_current_shake_pos < _new_shake_pos) + XFillRectangle(_display,_window, _black_gc, 0, _current_shake_pos, _window_width, _new_shake_pos); + else + XFillRectangle(_display,_window, _black_gc, 0, _window_height - _current_shake_pos, + _window_width,_window_height - _new_shake_pos); + XShmPutImage(_display, _window, DefaultGC(_display, _screen), _image, + 0, 0, _scumm_x, _scumm_y + _new_shake_pos, _fb_width, _fb_height, 0); + _current_shake_pos = _new_shake_pos; + } else if (need_redraw == true) { + XShmPutImage(_display, _window, DefaultGC(_display, _screen), _image, + dout.x, dout.y, _scumm_x + dout.x, _scumm_y + dout.y + _current_shake_pos, + dout.w - dout.x, dout.h - dout.y, 0); + XFlush(_display); + } +} + +bool OSystem_X11::showMouse(bool visible) +{ + if (_mouse_visible == visible) + return visible; + + bool last = _mouse_visible; + _mouse_visible = visible; + + if ((visible == false) && (_mouse_state_changed == false)) { + undraw_mouse(); + } + _mouse_state_changed = true; + + return last; +} + +void OSystem_X11::quit() { + exit(0); +} + +void OSystem_X11::setWindowCaption(const char *caption) { + //debug("setWindowCaption('%s')", caption); +} + +void OSystem_X11::undraw_mouse() { + AddDirtyRec(_oldMouseState.x, _oldMouseState.y, _oldMouseState.w, _oldMouseState.h); +} + +void OSystem_X11::draw_mouse(DirtyRect *dout) { + //debug("draw_mouse()"); + _mouse_state_changed = false; + + if (_mouse_visible == false) + return; + + int xdraw = _curMouseState.x - _curMouseState.hot_x; + int ydraw = _curMouseState.y - _curMouseState.hot_y; + int w = _curMouseState.w; + int h = _curMouseState.h; + int real_w; + int real_h; + + uint8 *dst; + const byte *buf = _ms_buf; + + if (ydraw < 0) { + real_h = h + ydraw; + buf += (-ydraw) * w; + ydraw = 0; + } else { + real_h = (ydraw + h) > _fb_height ? (_fb_height - ydraw) : h; + } + if (xdraw < 0) { + real_w = w + xdraw; + buf += (-xdraw); + xdraw = 0; + } else { + real_w = (xdraw + w) > _fb_width ? (_fb_width - xdraw) : w; + } + + dst = (uint8 *)_image->data + ((ydraw *_fb_width) + xdraw) * _bytesPerPixel; + + if ((real_h == 0) || (real_w == 0)) { + return; + } + + + if (xdraw < dout->x) + dout->x = xdraw; + if (ydraw < dout->y) + dout->y = ydraw; + if ((xdraw + real_w) > dout->w) + dout->w = xdraw + real_w; + if ((ydraw + real_h) > dout->h) + dout->h = ydraw + real_h; + + _oldMouseState.x = xdraw; + _oldMouseState.y = ydraw; + _oldMouseState.w = real_w; + _oldMouseState.h = real_h; + + while (real_h > 0) { + int width = real_w; + while (width > 0) { + byte color = *buf; + if (color != _mouseKeycolor) { + if (_depth == 16) + *(uint16 *)dst = _palette16[color]; + else { + *(uint32 *)dst = _palette32[color]; + } + } + buf++; + dst += _bytesPerPixel; + width--; + } + buf += w - real_w; + dst += (_fb_width - real_w) * _bytesPerPixel; + real_h--; + } +} + +void OSystem_X11::set_mouse_pos(int x, int y) { + if ((x != _curMouseState.x) || (y != _curMouseState.y)) { + _curMouseState.x = x; + _curMouseState.y = y; + if (_mouse_state_changed == false) { + undraw_mouse(); + } + _mouse_state_changed = true; + } +} + +void OSystem_X11::warpMouse(int x, int y) { + set_mouse_pos(x, y); +} + +void OSystem_X11::setMouseCursor(const byte *buf, uint w, uint h, int hotspot_x, int hotspot_y, byte keycolor, int cursorTargetScale) { + _curMouseState.w = w; + _curMouseState.h = h; + _curMouseState.hot_x = hotspot_x; + _curMouseState.hot_y = hotspot_y; + + if (_ms_buf) + free(_ms_buf); + _ms_buf = (byte *) malloc(w * h); + memcpy(_ms_buf, buf, w * h); + + if (_mouse_state_changed == false) { + undraw_mouse(); + } + _mouseKeycolor = keycolor; + _mouse_state_changed = true; +} + +void OSystem_X11::setShakePos(int shake_pos) { + if (_new_shake_pos != shake_pos) { + if (_mouse_state_changed == false) { + undraw_mouse(); + } + _mouse_state_changed = true; + } + _new_shake_pos = shake_pos; +} + +int OSystem_X11::getOutputSampleRate() const { + return SAMPLES_PER_SEC; +} + +void OSystem_X11::delayMillis(uint msecs) { + usleep(msecs * 1000); +} + +bool OSystem_X11::pollEvent(Event &scumm_event) { + /* First, handle timers */ + uint32 current_msecs = getMillis(); + + if (_timer_active && (current_msecs >= _timer_next_expiry)) { + _timer_duration = _timer_callback(_timer_duration); + _timer_next_expiry = current_msecs + _timer_duration; + } + + while (XPending(_display)) { + XEvent event; + + XNextEvent(_display, &event); + switch (event.type) { + case Expose:{ + int real_w, real_h; + int real_x, real_y; + real_x = event.xexpose.x; + real_y = event.xexpose.y; + real_w = event.xexpose.width; + real_h = event.xexpose.height; + if (real_x < _scumm_x) { + real_w -= _scumm_x - real_x; + real_x = 0; + } else { + real_x -= _scumm_x; + } + if (real_y < _scumm_y) { + real_h -= _scumm_y - real_y; + real_y = 0; + } else { + real_y -= _scumm_y; + } + if ((real_h <= 0) || (real_w <= 0)) + break; + if ((real_x >=_fb_width) || (real_y >=_fb_height)) + break; + + if ((real_x + real_w) >=_fb_width) { + real_w =_fb_width - real_x; + } + if ((real_y + real_h) >=_fb_height) { + real_h =_fb_height - real_y; + } + + /* Compute the intersection of the expose event with the real ScummVM display zone */ + AddDirtyRec(real_x, real_y, real_w, real_h); + } + break; + + case KeyPress:{ + /* I am using keycodes here and NOT keysyms to be sure that even if the user + remaps his iPAQ's keyboard, it will still work. + */ + int keycode = -1; + int ascii = -1; + byte mode = 0; + + if (event.xkey.state & 0x01) + mode |= KBD_SHIFT; + if (event.xkey.state & 0x04) + mode |= KBD_CTRL; + if (event.xkey.state & 0x08) + mode |= KBD_ALT; + switch (event.xkey.keycode) { + + case 9: /* Escape on my PC */ + case 130: /* Calendar on the iPAQ */ + keycode = 27; + break; + + case 71: /* F5 on my PC */ + case 128: /* Record on the iPAQ */ + keycode = 319; + break; + + case 65: /* Space on my PC */ + case 131: /* Schedule on the iPAQ */ + keycode = 32; + break; + + case 132: + _report_presses = 0; + break; + + case 133: + _fake_right_mouse = 1; + break; + + default:{ + KeySym xsym; + xsym = XKeycodeToKeysym(_display, event.xkey.keycode, 0); + keycode = xsym; + if ((xsym >= 'a') && (xsym <= 'z') && (event.xkey.state & 0x01)) + xsym &= ~0x20; /* Handle shifted keys */ + ascii = xsym; + } + } + if (keycode != -1) { + scumm_event.type = EVENT_KEYDOWN; + scumm_event.kbd.keycode = keycode; + scumm_event.kbd.ascii = (ascii != -1 ? ascii : keycode); + scumm_event.kbd.flags = mode; + return true; + } + } + break; + + case KeyRelease:{ + /* I am using keycodes here and NOT keysyms to be sure that even if the user + remaps his iPAQ's keyboard, it will still work. + */ + int keycode = -1; + int ascii = -1; + byte mode = 0; + + if (event.xkey.state & 0x01) + mode |= KBD_SHIFT; + if (event.xkey.state & 0x04) + mode |= KBD_CTRL; + if (event.xkey.state & 0x08) + mode |= KBD_ALT; + switch (event.xkey.keycode) { + case 132: /* 'Q' on the iPAQ */ + _report_presses = 1; + break; + + case 133: /* Arrow on the iPAQ */ + _fake_right_mouse = 0; + break; + + default:{ + KeySym xsym; + xsym = XKeycodeToKeysym(_display, event.xkey.keycode, 0); + keycode = xsym; + if ((xsym >= 'a') && (xsym <= 'z') && (event.xkey.state & 0x01)) + xsym &= ~0x20; /* Handle shifted keys */ + ascii = xsym; + } + } + if (keycode != -1) { + scumm_event.type = EVENT_KEYUP; + scumm_event.kbd.keycode = keycode; + scumm_event.kbd.ascii = (ascii != -1 ? ascii : keycode); + scumm_event.kbd.flags = mode; + return true; + } + } + break; + + case ButtonPress: + if (_report_presses != 0) { + if (event.xbutton.button == 1) { + if (_fake_right_mouse == 0) { + scumm_event.type = EVENT_LBUTTONDOWN; + } else { + scumm_event.type = EVENT_RBUTTONDOWN; + } + } else if (event.xbutton.button == 3) + scumm_event.type = EVENT_RBUTTONDOWN; + scumm_event.mouse.x = event.xbutton.x - _scumm_x; + scumm_event.mouse.y = event.xbutton.y - _scumm_y; + return true; + } + break; + + case ButtonRelease: + if (_report_presses != 0) { + if (event.xbutton.button == 1) { + if (_fake_right_mouse == 0) { + scumm_event.type = EVENT_LBUTTONUP; + } else { + scumm_event.type = EVENT_RBUTTONUP; + } + } else if (event.xbutton.button == 3) + scumm_event.type = EVENT_RBUTTONUP; + scumm_event.mouse.x = event.xbutton.x - _scumm_x; + scumm_event.mouse.y = event.xbutton.y - _scumm_y; + return true; + } + break; + + case MotionNotify: + scumm_event.type = EVENT_MOUSEMOVE; + scumm_event.mouse.x = event.xmotion.x - _scumm_x; + scumm_event.mouse.y = event.xmotion.y - _scumm_y; + set_mouse_pos(scumm_event.mouse.x, scumm_event.mouse.y); + return true; + + case ConfigureNotify:{ + if ((_window_width != event.xconfigure.width) || (_window_height != event.xconfigure.height)) { + _window_width = event.xconfigure.width; + _window_height = event.xconfigure.height; + _scumm_x = (_window_width -_fb_width) / 2; + _scumm_y = (_window_height -_fb_height) / 2; + XFillRectangle(_display, _window, _black_gc, 0, 0, _window_width, _window_height); + } + } + break; + + default: + printf("Unhandled event : %d\n", event.type); + break; + } + } + + return false; +} + +void OSystem_X11::setTimerCallback(TimerProc callback, int interval) { + if (callback != NULL) { + _timer_duration = interval; + _timer_next_expiry = getMillis() + interval; + _timer_callback = callback; + _timer_active = true; + } else { + _timer_active = false; + } +} + +OSystem::MutexRef OSystem_X11::createMutex(void) { + pthread_mutex_t *mutex = (pthread_mutex_t *) malloc(sizeof(pthread_mutex_t)); + pthread_mutex_init(mutex, NULL); + return (MutexRef)mutex; +} + +void OSystem_X11::lockMutex(MutexRef mutex) { + pthread_mutex_lock((pthread_mutex_t *) mutex); +} + +void OSystem_X11::unlockMutex(MutexRef mutex) { + pthread_mutex_unlock((pthread_mutex_t *) mutex); +} + +void OSystem_X11::deleteMutex(MutexRef mutex) { + pthread_mutex_destroy((pthread_mutex_t *) mutex); + free(mutex); +} + +void OSystem_X11::showOverlay() { + _overlay_visible = true; +} + +void OSystem_X11::hideOverlay() { + _overlay_visible = false; + _palette_changed = true; // This is to force a full redraw to hide the overlay +} + +void OSystem_X11::clearOverlay() { + if (_overlay_visible == false) + return; + DirtyRect d = { 0, 0, _fb_width, _fb_height }; + AddDirtyRec(0, 0, _fb_width, _fb_height); + blit(&d, _local_fb_overlay, _fb_width); +} + +void OSystem_X11::grabOverlay(int16 *dest, int pitch) { + if (_overlay_visible == false) + return; + + DirtyRect d = { 0, 0, _fb_width, _fb_height }; + blit(&d, (uint16 *)dest, pitch); +} + +void OSystem_X11::copyRectToOverlay(const int16 *src, int pitch, int x, int y, int w, int h) { + if (_overlay_visible == false) + return; + uint16 *dst = _local_fb_overlay + x + (y * _fb_width); + AddDirtyRec(x, y, w, h); + while (h > 0) { + memcpy(dst, src, w * sizeof(*dst)); + dst +=_fb_width; + src += pitch; + h--; + } +} + +int16 OSystem_X11::getHeight() { + return _fb_height; +} + +int16 OSystem_X11::getWidth() { + return _fb_width; +} + +void OSystem_X11::grabPalette(byte *colors, uint start, uint num) { + warning("Dummy: grabPalette()"); +} diff --git a/backends/platform/x11/x11.h b/backends/platform/x11/x11.h new file mode 100644 index 0000000000..ba29bc7eab --- /dev/null +++ b/backends/platform/x11/x11.h @@ -0,0 +1,195 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +class OSystem_X11:public OSystem { +public: + OSystem_X11(); + ~OSystem_X11(); + + void initBackend(); + + // Determine whether the backend supports the specified feature. + bool hasFeature(Feature f); + + // En-/disable the specified feature. + void setFeatureState(Feature f, bool enable); + + // Query the state of the specified feature. + bool getFeatureState(Feature f); + + // Retrieve a list of all graphics modes supported by this backend. + const GraphicsMode *getSupportedGraphicsModes() const; + + // Return the ID of the 'default' graphics mode. + int getDefaultGraphicsMode() const; + + // Switch to the specified graphics mode. + bool setGraphicsMode(int mode); + + // Determine which graphics mode is currently active. + int getGraphicsMode() const; + + // Set colors of the palette + void setPalette(const byte *colors, uint start, uint num); + + // Set the size of the video bitmap. + // Typically, 320x200 + void initSize(uint w, uint h); + + // Draw a bitmap to screen. + // The screen will not be updated to reflect the new bitmap + void copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h); + + // Update the dirty areas of the screen + void updateScreen(); + + // Either show or hide the mouse cursor + bool showMouse(bool visible); + + // Set the position of the mouse cursor + void set_mouse_pos(int x, int y); + + // Warp the mouse cursor. Where set_mouse_pos() only informs the + // backend of the mouse cursor's current position, this function + // actually moves the cursor to the specified position. + void warpMouse(int x, int y); + + // Set the bitmap that's used when drawing the cursor. + void setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, byte keycolor = 255, int cursorTargetScale = 1); + + // Shaking is used in SCUMM. Set current shake position. + void setShakePos(int shake_pos); + + // Get the number of milliseconds since the program was started. + uint32 getMillis(); + + // Delay for a specified amount of milliseconds + void delayMillis(uint msecs); + + // Get the next event. + // Returns true if an event was retrieved. + bool pollEvent(Event &event); + + // Set function that generates samples + bool setSoundCallback(SoundProc proc, void *param); + void clearSoundCallback(); + + // Determine the output sample rate. Audio data provided by the sound + // callback will be played using this rate. + int getOutputSampleRate() const; + + // Quit + void quit(); + + // Add a callback timer + void setTimerCallback(TimerProc callback, int interval); + + // Mutex handling + MutexRef createMutex(); + void lockMutex(MutexRef mutex); + void unlockMutex(MutexRef mutex); + void deleteMutex(MutexRef mutex); + + // Overlay handling for the new menu system + void showOverlay(); + void hideOverlay(); + void clearOverlay(); + void grabOverlay(int16 *, int); + void copyRectToOverlay(const int16 *, int, int, int, int, int); + virtual int16 getHeight(); + virtual int16 getWidth(); + + virtual void grabPalette(byte *colors, uint start, uint num); + + // Set a window caption or any other comparable status display to the + // given value. + void setWindowCaption(const char *caption); + + static OSystem *create(int gfx_mode, bool full_screen); + +private: + typedef struct { + int x, y; + int w, h; + int hot_x, hot_y; + } MouseState; + + typedef struct { + int x, y, w, h; + } DirtyRect; + + enum { + MAX_NUMBER_OF_DIRTY_RECTS = 32 + }; + + void create_empty_cursor(); + void draw_mouse(DirtyRect *dout); + void undraw_mouse(); + void updateScreen_helper(const DirtyRect *d, DirtyRect *dout); + void blit_convert(const DirtyRect *d, uint8 *dst, int pitch); + void blit(const DirtyRect *d, uint16 *dst, int pitch); + + uint8 *_local_fb; + uint16 *_local_fb_overlay; + bool _overlay_visible; + + int _window_width, _window_height; + int _fb_width, _fb_height; + int _scumm_x, _scumm_y; + + uint16 *_palette16; + uint32 *_palette32; + + bool _palette_changed; + Display *_display; + int _screen, _depth; + uint8 _bytesPerPixel; + Window _window; + GC _black_gc; + XImage *_image; + pthread_t _sound_thread; + + struct timeval _start_time; + + int _fake_right_mouse; + int _report_presses; + int _current_shake_pos; + int _new_shake_pos; + DirtyRect _ds[MAX_NUMBER_OF_DIRTY_RECTS]; + int _num_of_dirty_rects; + + MouseState _oldMouseState, _curMouseState; + byte *_ms_buf; + bool _mouse_visible; + bool _mouse_state_changed; + byte _mouseKeycolor; + + uint32 _timer_duration, _timer_next_expiry; + bool _timer_active; + int (*_timer_callback) (int); +}; + +typedef struct { + OSystem::SoundProc sound_proc; + void *param; +} THREAD_PARAM; |