From 5726169e35124748ae2a147ad2a79454eae6f493 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Fri, 17 Sep 2004 13:20:10 +0000 Subject: [PATCH] Date: Fri, 25 Jun 2004 13:29:15 +0100 From: "alan buckley" Subject: Modification for RISC OS version of SDL Ive attached a zip file with the changes to this email, it contains the following: The file sdldiff.txt is the output from cvs diff u. . The directory thread/riscos contains all the new files to support threading. Readme.riscos is a new readme file to add. --- README.RISCOS | 112 +++++++++++++++ configure.in | 43 ++++-- src/audio/SDL_audio.c | 2 +- src/audio/riscos/SDL_drenderer.c | 107 ++++++++++++-- src/thread/riscos/SDL_syscond.c | 167 +++++++++++++++++++++ src/thread/riscos/SDL_sysmutex.c | 160 +++++++++++++++++++++ src/thread/riscos/SDL_sysmutex_c.h | 38 +++++ src/thread/riscos/SDL_syssem.c | 216 ++++++++++++++++++++++++++++ src/thread/riscos/SDL_systhread.c | 149 +++++++++++++++++++ src/thread/riscos/SDL_systhread_c.h | 33 +++++ src/timer/riscos/SDL_systimer.c | 92 +++++++++++- src/video/riscos/SDL_riscosevents.c | 6 + src/video/riscos/SDL_riscostask.c | 9 ++ src/video/riscos/SDL_wimppoll.c | 81 +++++++---- 14 files changed, 1167 insertions(+), 48 deletions(-) create mode 100644 README.RISCOS create mode 100644 src/thread/riscos/SDL_syscond.c create mode 100644 src/thread/riscos/SDL_sysmutex.c create mode 100644 src/thread/riscos/SDL_sysmutex_c.h create mode 100644 src/thread/riscos/SDL_syssem.c create mode 100644 src/thread/riscos/SDL_systhread.c create mode 100644 src/thread/riscos/SDL_systhread_c.h diff --git a/README.RISCOS b/README.RISCOS new file mode 100644 index 000000000..ee7f71670 --- /dev/null +++ b/README.RISCOS @@ -0,0 +1,112 @@ +Readme for RISC OS port of SDL +============================== + +This document last updated on 25th June 2004 + +This is a RISC OS port of the Simple Direct Media Layer (SDL) by Alan Buckley. + +Details of the SDL can be found at http://www.libsdl.org. + +The source code including the RISC OS version can be obtained from http://www.libsdl.org. + +RISC OS makefiles, pre built libraries and many games and applications compiled for RISC OS using this library can be downloaded from The Unix Porting Project at http://www.chocky.org/unix/index.html. + +This is released under the LGPL see the file COPYING for details. + + +RISCOS port of SDL runtime information +====================================== + +Runtime requirements +-------------------- + +This library currently needs a minimum of RISCOS 3.6. The source code for the library (and a lot of the programs built with it) also need long file names. + +To use the audio you also need 16 bit sound and to have installed the Digital Render module by Andreas Dehmel version 0.51 available from his web site: + http://home.t-online.de/~zarquon + +Note: As most programs ported from other OS's use high resolution graphics and a memory back buffer a machine with a StrongARM processor and 1 or 2MB of VRAM (or a better machine) is recomended. + + +RISCOS runtime parameters +------------------------- + +Several environmental variables have been defined to make porting programs easier (i.e. By setting these variable you do not need to have source code differences between OS's). + +They are all defined on an application basis. + +The used below is found as follows: +1. Use the name of the program unless it is !RunImage +2. Check the folder specification for the folder !RunImage is run from. If it is a folder name use that name, otherwise if it is an environmental variable of the form use the value of XXX. + +The variables are: + +SDL$$TaskName - The name of the task for RISCOS. If omitted then is used for the task name, + +SDL$$BackBuffer - set to 1 to use a system memory back buffer for the screen in full screen mode. Some programs on other systems assume their is always a back buffer even though the SDL specification specifies this is not the case. The current RISCOS implementation uses direct writes to the screen if a hardware fullscreen is requested. + +SDL$$CloseAction - set the action for the close icon. Again as programs don't match the specification you can set this to 0 to remove the close icon from the main window for applications where this does not affect the program. + +SDL$$AudioBuffer - set the minimum size of the audio buffer in samples in steps of 512. If this is less than the minimum calculated from the parameters requested in the program by SDL_OpenAudio these will be used instead. If this parameter is not used or set to 0 the buffer size will be calculated to buffer 10 centisecond of sound. The time buffered can be calculated as specified size/frequency in seconds. The larger the buffer the more audio data that will be buffered, but this can lead to a lag between a sound being requested and it being heard, so the size should be kept to a minimum. You should only set this parameter if the sound is being missed out. + + + +RISCOS SDL port API notes +========================= + +Current level of implementation +------------------------------- + +The following list is an overview of how much of the SDL is implemented. The areas match the main areas of the SDL. + +video - Mostly done. Doesn't cover gamma, YUV-overlay or open gl. +Window Manager - Mostly done. SetIcon/IconifyWindow not implemented. +Events - Mostly done. Resize and some joystick events missing. +Joystick - Currently assumes a single joystick with 4 buttons. +Audio - Requires Digital Renderer module. +CDROM - Not implemented. +Threads - Done +Timers - Done + +Thread support can be removed by defining DISABLE_THREADS and recompiling the library. + +SDL API notes +------------- + +This section contains additional notes on some specific commands. + +SDL_SetVideoMode + On RISCOS a fullscreen mode directly accesses the screen. This can be modified by the environmental variable (SDL$$BackBuffer) or by using the SDL_SWSURFACE flag to write to an offscreen buffer that is updated using SDL_UpdateRects. + Open GL is not supported so SDL_OPENGL and SDL_OPENGLBLIT flags fail. + SDL_RESIZEABLE and SDL_NOFRAME flags are not supported. + +SDL_SetColors + In a wimp mode the screen colours are not changed for a hardware palette instead the RISCOS sprite colour mapping is used to get the best matching colours. + +SDL_CreateCursor + Inverted colour is not supported. + +SDL_WM_ToggleFullScreen + Currently this won't work if the application starts up in Fullscreen mode. + Toggling to fullscreen will only work if the monitor is set up to support the exact screen size requested. + +SDL_EnableUNICODE + Unicode translation used here is only really accurate for 7 bit characters. + +SDL_NumJoysticks/JoystickName etc. + Hardcoded to expect only 1 joystick with 4 buttons if the Joystick module is loaded. + +SDL_GetTicks + Timer used has only a centisecond accuracy. This applies to other time related functions. + +SDL_Delay + Modified to poll keyboard/mouse during the delay on the event thread. + + +Notes on current implementation +------------------------------- + +Keyboard and mouse are polled so if too long a time is spent between a call to SDL_PumpEvents, functions that use it, or SDL_Delay events can be missed. + +The sound is sent to the digital renderer player in a seperate thread. If there is too long a delay between this thread being processed (possibly if a lot else is running in the Wimp) the sound may sound incorrect or dissappear. + diff --git a/configure.in b/configure.in index 7073bf1d3..53ad01159 100644 --- a/configure.in +++ b/configure.in @@ -2605,20 +2605,45 @@ case "$target" in ;; *-*-riscos) ARCH=riscos - JOYSTICK_SUBDIRS="$JOYSTICK_SUBDIRS riscos" - JOYSTICK_DRIVERS="$JOYSTICK_DRIVERS riscos/libjoystick_riscos.la" - COPY_ARCH_SRC(src/timer, riscos, SDL_systimer.c) - VIDEO_SUBDIRS="$VIDEO_SUBDIRS riscos" - VIDEO_DRIVERS="$VIDEO_DRIVERS riscos/libvideo_riscos.la" - AUDIO_SUBDIRS="$AUDIO_SUBDIRS riscos" - AUDIO_DRIVERS="$AUDIO_DRIVERS riscos/libaudio_riscos.la" - + # Set up files for the video library + if test x$enable_video = xyes; then + VIDEO_SUBDIRS="$VIDEO_SUBDIRS riscos" + VIDEO_DRIVERS="$VIDEO_DRIVERS riscos/libvideo_riscos.la" + fi + # Set up files for the audio library + if test x$enable_audio = xyes; then + AUDIO_SUBDIRS="$AUDIO_SUBDIRS riscos" + AUDIO_DRIVERS="$AUDIO_DRIVERS riscos/libaudio_riscos.la" + fi + # Set up files for the joystick library + if test x$enable_joystick = xyes; then + JOYSTICK_SUBDIRS="$JOYSTICK_SUBDIRS riscos" + JOYSTICK_DRIVERS="$JOYSTICK_DRIVERS riscos/libjoystick_riscos.la" + fi + # Set up files for the cdrom library if test x$enable_cdrom = xyes; then CDROM_SUBDIRS="$CDROM_SUBDIRS dummy" CDROM_DRIVERS="$CDROM_DRIVERS dummy/libcdrom_dummy.la" fi + # Set up files for the thread library + if test x$enable_threads = xyes; then + COPY_ARCH_SRC(src/thread, riscos, SDL_systhread.c) + COPY_ARCH_SRC(src/thread, riscos, SDL_systhread_c.h) + COPY_ARCH_SRC(src/thread, riscos, SDL_sysmutex.c) + COPY_ARCH_SRC(src/thread, riscos, SDL_sysmutex_c.h) + COPY_ARCH_SRC(src/thread, riscos, SDL_syssem.c) + COPY_ARCH_SRC(src/thread, generic, SDL_syssem_c.h) + COPY_ARCH_SRC(src/thread, riscos, SDL_syscond.c) + COPY_ARCH_SRC(src/thread, generic, SDL_syscond_c.h) + else + CFLAGS="$CFLAGS -DDISABLE_THREADS" + fi + # Set up files for the timer library + if test x$enable_timers = xyes; then + COPY_ARCH_SRC(src/timer, riscos, SDL_systimer.c) + fi - CFLAGS="$CFLAGS -DDISABLE_THREADS -DENABLE_RISCOS -DDRENDERER_SUPPORT" + CFLAGS="$CFLAGS -DENABLE_RISCOS -DDRENDERER_SUPPORT" SYSTEM_LIBS="$SYSTEM_LIBS -ljpeg -ltiff -lpng -lz" ;; diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c index 3abe64b1b..7bbb1a0af 100644 --- a/src/audio/SDL_audio.c +++ b/src/audio/SDL_audio.c @@ -399,7 +399,7 @@ int SDL_OpenAudio(SDL_AudioSpec *desired, SDL_AudioSpec *obtained) return(-1); } -#if defined(macintosh) || defined(__riscos__) +#if defined(macintosh) || (defined(__riscos__) && !defined(DISABLE_THREADS)) /* FIXME: Need to implement PPC interrupt asm for SDL_LockAudio() */ #else #if defined(__MINT__) && !defined(ENABLE_THREADS) diff --git a/src/audio/riscos/SDL_drenderer.c b/src/audio/riscos/SDL_drenderer.c index e6e718dda..8e766a99d 100644 --- a/src/audio/riscos/SDL_drenderer.c +++ b/src/audio/riscos/SDL_drenderer.c @@ -35,6 +35,10 @@ #include "SDL_sysaudio.h" #include "SDL_drenderer.h" +#ifndef DISABLE_THREADS +#include +#endif + #define DigitalRenderer_Activate 0x4F700 #define DigitalRenderer_Deactivate 0x4F701 #define DigitalRenderer_ReadState 0x4F705 @@ -57,6 +61,12 @@ extern int riscos_audiobuffer; /* Override for audio buffer size */ static void DRenderer_CloseAudio(_THIS); static int DRenderer_OpenAudio(_THIS, SDL_AudioSpec *spec); +#ifndef DISABLE_THREADS +static void DRenderer_WaitAudio(_THIS); +static Uint8 *DRenderer_GetAudioBuf(_THIS); +static void DRenderer_PlayAudio(_THIS); +#endif + /* Audio driver bootstrap functions */ /* Define following to dump stats to stdout */ @@ -104,6 +114,11 @@ static SDL_AudioDevice *Audio_CreateDevice(int devindex) /* Set the function pointers */ this->OpenAudio = DRenderer_OpenAudio; this->CloseAudio = DRenderer_CloseAudio; +#ifndef DISABLE_THREADS + this->GetAudioBuf = DRenderer_GetAudioBuf; + this->PlayAudio = DRenderer_PlayAudio; + this->WaitAudio = DRenderer_WaitAudio; +#endif this->free = Audio_DeleteDevice; return this; @@ -117,6 +132,9 @@ AudioBootStrap DRENDERER_bootstrap = { /* Routine called to check and fill audio buffers if necessary */ static Uint8 *buffer = NULL; +#ifdef DISABLE_THREADS + +/* Buffer fill routine called during polling */ void DRenderer_FillBuffers() { SDL_AudioDevice *audio = current_audio; @@ -182,15 +200,33 @@ void DRenderer_FillBuffers() } } +#endif + /* Size of DMA buffer to use */ #define DRENDERER_BUFFER_SIZE 512 /* Number of centiseconds of sound to buffer. Hopefully more than the maximum time between calls to the - FillBuffers routine above + FillBuffers routine above (non-threaded) or the PlayAudio + routine below (threaded). */ + #define DRENDERER_CSEC_TO_BUFFER 10 +static void DeactivateAudio() +{ + _kernel_swi_regs regs; + + /* Close down the digital renderer */ + _kernel_swi(DigitalRenderer_Deactivate, ®s, ®s); + + if (buffer != NULL) + { + free(buffer); + buffer = NULL; + } +} + static int DRenderer_OpenAudio(_THIS, SDL_AudioSpec *spec) { _kernel_swi_regs regs; @@ -200,6 +236,7 @@ static int DRenderer_OpenAudio(_THIS, SDL_AudioSpec *spec) printf("Request format %d\n", spec->format); printf("Request freq %d\n", spec->freq); printf("Samples %d\n", spec->samples); + printf("Channels %d\n", spec->channels); #endif /* Only support signed 16bit format */ @@ -207,9 +244,6 @@ static int DRenderer_OpenAudio(_THIS, SDL_AudioSpec *spec) if (spec->samples < DRENDERER_BUFFER_SIZE) spec->samples = DRENDERER_BUFFER_SIZE; - SDL_CalculateAudioSpec(spec); - - buffers_per_sample = spec->samples / DRENDERER_BUFFER_SIZE; if ((spec->samples % DRENDERER_BUFFER_SIZE) != 0) @@ -217,6 +251,7 @@ static int DRenderer_OpenAudio(_THIS, SDL_AudioSpec *spec) buffers_per_sample++; spec->samples = buffers_per_sample * DRENDERER_BUFFER_SIZE; } + /* Set number of buffers to use - the following should give enough data between calls to the sound polling. @@ -227,7 +262,19 @@ static int DRenderer_OpenAudio(_THIS, SDL_AudioSpec *spec) FillBuffer = (int)((double)DRENDERER_CSEC_TO_BUFFER / ((double)DRENDERER_BUFFER_SIZE * 100.0 / (double)spec->freq)) + 1; } else FillBuffer = riscos_audiobuffer/DRENDERER_BUFFER_SIZE - buffers_per_sample; - if (FillBuffer < buffers_per_sample) FillBuffer = buffers_per_sample; + if (FillBuffer < buffers_per_sample) FillBuffer = buffers_per_sample; +#ifndef DISABLE_THREADS + if (buffers_per_sample < FillBuffer) + { + /* For the threaded version we are only called once per cycle + so the callback needs to give us the full data we need in + one go, rather than multiple calls as it the case for the + non threaded version */ + buffers_per_sample = FillBuffer; + spec->samples = buffers_per_sample * DRENDERER_BUFFER_SIZE; + } +#endif + SDL_CalculateAudioSpec(spec); regs.r[0] = FillBuffer + buffers_per_sample; #ifdef DUMP_AUDIO @@ -259,6 +306,9 @@ static int DRenderer_OpenAudio(_THIS, SDL_AudioSpec *spec) spec->freq = regs.r[0]; } + /* Ensure sound is deactivated if we exit without calling SDL_Quit */ + atexit(DeactivateAudio); + #ifdef DUMP_AUDIO printf("Got format %d\n", spec->format); printf("Frequency %d\n", spec->freq); @@ -277,17 +327,56 @@ static int DRenderer_OpenAudio(_THIS, SDL_AudioSpec *spec) return -1; } +#ifdef DISABLE_THREADS /* Hopefully returning 2 will show success, but not start up an audio thread */ return 2; +#else + /* Success and start audio thread */ + return 0; +#endif } static void DRenderer_CloseAudio(_THIS) { - _kernel_swi_regs regs; + DeactivateAudio(); +} - /* Close down the digital renderer */ - _kernel_swi(DigitalRenderer_Deactivate, ®s, ®s); +#ifndef DISABLE_THREADS + +/* Routines for threaded version of audio */ - if (buffer != NULL) free(buffer); +void DRenderer_WaitAudio(_THIS) +{ + _kernel_swi_regs regs; + int waiting = 1; + + while (waiting) + { + /* Check filled buffers count */ + _kernel_swi(DigitalRenderer_StreamStatistics, ®s, ®s); +#if 0 + if (regs.r[0] <= FillBuffer) + { + printf("Buffers in use %d\n", regs.r[0]); + } +#endif + if (regs.r[0] <= FillBuffer) waiting = 0; + else pthread_yield(); + } +} + +Uint8 *DRenderer_GetAudioBuf(_THIS) +{ + return buffer; } +void DRenderer_PlayAudio(_THIS) +{ + _kernel_swi_regs regs; + + regs.r[0] = (int)buffer; + regs.r[1] = current_audio->spec.samples * current_audio->spec.channels; + _kernel_swi(DigitalRenderer_Stream16BitSamples, ®s, ®s); +} + +#endif diff --git a/src/thread/riscos/SDL_syscond.c b/src/thread/riscos/SDL_syscond.c new file mode 100644 index 000000000..5e5e6edcf --- /dev/null +++ b/src/thread/riscos/SDL_syscond.c @@ -0,0 +1,167 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2004 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +/* RISC OS implementations uses pthreads based on linux code */ + +#ifdef SAVE_RCSID +static char rcsid = + "@(#) $Id$"; +#endif + +#ifdef DISABLE_THREADS +#include "../generic/SDL_syscond.c" +#else +#include +#include +#include +#include +#include + +#include "SDL_error.h" +#include "SDL_thread.h" +#include "SDL_sysmutex_c.h" + +struct SDL_cond +{ + pthread_cond_t cond; +}; + +/* Create a condition variable */ +SDL_cond * SDL_CreateCond(void) +{ + SDL_cond *cond; + + cond = (SDL_cond *) malloc(sizeof(SDL_cond)); + if ( cond ) { + if ( pthread_cond_init(&cond->cond, NULL) < 0 ) { + SDL_SetError("pthread_cond_init() failed"); + free(cond); + cond = NULL; + } + } + return(cond); +} + +/* Destroy a condition variable */ +void SDL_DestroyCond(SDL_cond *cond) +{ + if ( cond ) { + pthread_cond_destroy(&cond->cond); + free(cond); + } +} + +/* Restart one of the threads that are waiting on the condition variable */ +int SDL_CondSignal(SDL_cond *cond) +{ + int retval; + + if ( ! cond ) { + SDL_SetError("Passed a NULL condition variable"); + return -1; + } + + retval = 0; + if ( pthread_cond_signal(&cond->cond) != 0 ) { + SDL_SetError("pthread_cond_signal() failed"); + retval = -1; + } + return retval; +} + +/* Restart all threads that are waiting on the condition variable */ +int SDL_CondBroadcast(SDL_cond *cond) +{ + int retval; + + if ( ! cond ) { + SDL_SetError("Passed a NULL condition variable"); + return -1; + } + + retval = 0; + if ( pthread_cond_broadcast(&cond->cond) != 0 ) { + SDL_SetError("pthread_cond_broadcast() failed"); + retval = -1; + } + return retval; +} + +int SDL_CondWaitTimeout(SDL_cond *cond, SDL_mutex *mutex, Uint32 ms) +{ + int retval; + struct timeval delta; + struct timespec abstime; + + if ( ! cond ) { + SDL_SetError("Passed a NULL condition variable"); + return -1; + } + + gettimeofday(&delta, NULL); + + abstime.tv_sec = delta.tv_sec + (ms/1000); + abstime.tv_nsec = (delta.tv_usec + (ms%1000) * 1000) * 1000; + if ( abstime.tv_nsec > 1000000000 ) { + abstime.tv_sec += 1; + abstime.tv_nsec -= 1000000000; + } + + tryagain: + retval = pthread_cond_timedwait(&cond->cond, &mutex->id, &abstime); + switch (retval) { + case EINTR: + goto tryagain; + break; + case ETIMEDOUT: + retval = SDL_MUTEX_TIMEDOUT; + break; + case 0: + break; + default: + SDL_SetError("pthread_cond_timedwait() failed"); + retval = -1; + break; + } + return retval; +} + +/* Wait on the condition variable, unlocking the provided mutex. + The mutex must be locked before entering this function! + */ +int SDL_CondWait(SDL_cond *cond, SDL_mutex *mutex) +{ + int retval; + + if ( ! cond ) { + SDL_SetError("Passed a NULL condition variable"); + return -1; + } + + retval = 0; + if ( pthread_cond_wait(&cond->cond, &mutex->id) != 0 ) { + SDL_SetError("pthread_cond_wait() failed"); + retval = -1; + } + return retval; +} +#endif diff --git a/src/thread/riscos/SDL_sysmutex.c b/src/thread/riscos/SDL_sysmutex.c new file mode 100644 index 000000000..3523a8afa --- /dev/null +++ b/src/thread/riscos/SDL_sysmutex.c @@ -0,0 +1,160 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2004 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +/* RISC OS implementations uses pthreads based on linux code */ + +#ifdef SAVE_RCSID +static char rcsid = + "@(#) $Id$"; +#endif + +#ifdef DISABLE_THREADS +#include "../generic/SDL_sysmutex.c" +#else + +#include +#include +#include + +#include "SDL_error.h" +#include "SDL_thread.h" + +struct SDL_mutex { + pthread_mutex_t id; +#ifdef PTHREAD_NO_RECURSIVE_MUTEX + int recursive; + pthread_t owner; +#endif +}; + +SDL_mutex *SDL_CreateMutex (void) +{ + SDL_mutex *mutex; + pthread_mutexattr_t attr; + + /* Allocate the structure */ + mutex = (SDL_mutex *)calloc(1, sizeof(*mutex)); + if ( mutex ) { + pthread_mutexattr_init(&attr); +#ifdef PTHREAD_NO_RECURSIVE_MUTEX + /* No extra attributes necessary */ +#else + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); +#endif /* PTHREAD_NO_RECURSIVE_MUTEX */ + if ( pthread_mutex_init(&mutex->id, &attr) != 0 ) { + SDL_SetError("pthread_mutex_init() failed"); + free(mutex); + mutex = NULL; + } + } else { + SDL_OutOfMemory(); + } + return(mutex); +} + +void SDL_DestroyMutex(SDL_mutex *mutex) +{ + if ( mutex ) { + pthread_mutex_destroy(&mutex->id); + free(mutex); + } +} + +/* Lock the mutex */ +int SDL_mutexP(SDL_mutex *mutex) +{ + int retval; +#ifdef PTHREAD_NO_RECURSIVE_MUTEX + pthread_t this_thread; +#endif + + if ( mutex == NULL ) { + SDL_SetError("Passed a NULL mutex"); + return -1; + } + + retval = 0; +#ifdef PTHREAD_NO_RECURSIVE_MUTEX + this_thread = pthread_self(); + if ( mutex->owner == this_thread ) { + ++mutex->recursive; + } else { + /* The order of operations is important. + We set the locking thread id after we obtain the lock + so unlocks from other threads will fail. + */ + if ( pthread_mutex_lock(&mutex->id) == 0 ) { + mutex->owner = this_thread; + mutex->recursive = 0; + } else { + SDL_SetError("pthread_mutex_lock() failed"); + retval = -1; + } + } +#else + if ( pthread_mutex_lock(&mutex->id) < 0 ) { + SDL_SetError("pthread_mutex_lock() failed"); + retval = -1; + } +#endif + return retval; +} + +int SDL_mutexV(SDL_mutex *mutex) +{ + int retval; + + if ( mutex == NULL ) { + SDL_SetError("Passed a NULL mutex"); + return -1; + } + + retval = 0; +#ifdef PTHREAD_NO_RECURSIVE_MUTEX + /* We can only unlock the mutex if we own it */ + if ( pthread_self() == mutex->owner ) { + if ( mutex->recursive ) { + --mutex->recursive; + } else { + /* The order of operations is important. + First reset the owner so another thread doesn't lock + the mutex and set the ownership before we reset it, + then release the lock semaphore. + */ + mutex->owner = 0; + pthread_mutex_unlock(&mutex->id); + } + } else { + SDL_SetError("mutex not owned by this thread"); + retval = -1; + } + +#else + if ( pthread_mutex_unlock(&mutex->id) < 0 ) { + SDL_SetError("pthread_mutex_unlock() failed"); + retval = -1; + } +#endif /* PTHREAD_NO_RECURSIVE_MUTEX */ + + return retval; +} +#endif diff --git a/src/thread/riscos/SDL_sysmutex_c.h b/src/thread/riscos/SDL_sysmutex_c.h new file mode 100644 index 000000000..1e1b44425 --- /dev/null +++ b/src/thread/riscos/SDL_sysmutex_c.h @@ -0,0 +1,38 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2004 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +#ifdef SAVE_RCSID +static char rcsid = + "@(#) $Id$"; +#endif + +#ifndef _SDL_mutex_c_h +#define _SDL_mutex_c_h + +#ifndef DISABLE_THREADS +struct SDL_mutex { + pthread_mutex_t id; +}; +#endif + + +#endif /* _SDL_mutex_c_h */ diff --git a/src/thread/riscos/SDL_syssem.c b/src/thread/riscos/SDL_syssem.c new file mode 100644 index 000000000..62522dacc --- /dev/null +++ b/src/thread/riscos/SDL_syssem.c @@ -0,0 +1,216 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2004 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +/* RISC OS doesn't have semiphores so use code based on generic implementation */ + +#ifdef SAVE_RCSID +static char rcsid = + "@(#) $Id$"; +#endif + +/* An implementation of semaphores using mutexes and condition variables */ + +#include + +#include "SDL_error.h" +#include "SDL_timer.h" +#include "SDL_thread.h" + +#include "SDL_systhread_c.h" + +#ifdef DISABLE_THREADS + +SDL_sem *SDL_CreateSemaphore(Uint32 initial_value) +{ + SDL_SetError("SDL not configured with thread support"); + return (SDL_sem *)0; +} + +void SDL_DestroySemaphore(SDL_sem *sem) +{ + return; +} + +int SDL_SemTryWait(SDL_sem *sem) +{ + SDL_SetError("SDL not configured with thread support"); + return -1; +} + +int SDL_SemWaitTimeout(SDL_sem *sem, Uint32 timeout) +{ + SDL_SetError("SDL not configured with thread support"); + return -1; +} + +int SDL_SemWait(SDL_sem *sem) +{ + SDL_SetError("SDL not configured with thread support"); + return -1; +} + +Uint32 SDL_SemValue(SDL_sem *sem) +{ + return 0; +} + +int SDL_SemPost(SDL_sem *sem) +{ + SDL_SetError("SDL not configured with thread support"); + return -1; +} + +#else + +struct SDL_semaphore +{ + Uint32 count; + Uint32 waiters_count; + SDL_mutex *count_lock; + SDL_cond *count_nonzero; +}; + +SDL_sem *SDL_CreateSemaphore(Uint32 initial_value) +{ + SDL_sem *sem; + + sem = (SDL_sem *)malloc(sizeof(*sem)); + if ( ! sem ) { + SDL_OutOfMemory(); + return(0); + } + sem->count = initial_value; + sem->waiters_count = 0; + + sem->count_lock = SDL_CreateMutex(); + sem->count_nonzero = SDL_CreateCond(); + if ( ! sem->count_lock || ! sem->count_nonzero ) { + SDL_DestroySemaphore(sem); + return(0); + } + + return(sem); +} + +/* WARNING: + You cannot call this function when another thread is using the semaphore. +*/ +void SDL_DestroySemaphore(SDL_sem *sem) +{ + if ( sem ) { + sem->count = 0xFFFFFFFF; + while ( sem->waiters_count > 0) { + SDL_CondSignal(sem->count_nonzero); + SDL_Delay(10); + } + SDL_DestroyCond(sem->count_nonzero); + SDL_mutexP(sem->count_lock); + SDL_mutexV(sem->count_lock); + SDL_DestroyMutex(sem->count_lock); + free(sem); + } +} + +int SDL_SemTryWait(SDL_sem *sem) +{ + int retval; + + if ( ! sem ) { + SDL_SetError("Passed a NULL semaphore"); + return -1; + } + + retval = SDL_MUTEX_TIMEDOUT; + SDL_LockMutex(sem->count_lock); + if ( sem->count > 0 ) { + --sem->count; + retval = 0; + } + SDL_UnlockMutex(sem->count_lock); + + return retval; +} + +int SDL_SemWaitTimeout(SDL_sem *sem, Uint32 timeout) +{ + int retval; + + if ( ! sem ) { + SDL_SetError("Passed a NULL semaphore"); + return -1; + } + + /* A timeout of 0 is an easy case */ + if ( timeout == 0 ) { + return SDL_SemTryWait(sem); + } + + SDL_LockMutex(sem->count_lock); + ++sem->waiters_count; + retval = 0; + while ( (sem->count == 0) && (retval != SDL_MUTEX_TIMEDOUT) ) { + retval = SDL_CondWaitTimeout(sem->count_nonzero, + sem->count_lock, timeout); + } + --sem->waiters_count; + --sem->count; + SDL_UnlockMutex(sem->count_lock); + + return retval; +} + +int SDL_SemWait(SDL_sem *sem) +{ + return SDL_SemWaitTimeout(sem, SDL_MUTEX_MAXWAIT); +} + +Uint32 SDL_SemValue(SDL_sem *sem) +{ + Uint32 value; + + value = 0; + if ( sem ) { + SDL_LockMutex(sem->count_lock); + value = sem->count; + SDL_UnlockMutex(sem->count_lock); + } + return value; +} + +int SDL_SemPost(SDL_sem *sem) +{ + if ( ! sem ) { + SDL_SetError("Passed a NULL semaphore"); + return -1; + } + + SDL_LockMutex(sem->count_lock); + if ( sem->waiters_count > 0 ) { + SDL_CondSignal(sem->count_nonzero); + } + ++sem->count; + SDL_UnlockMutex(sem->count_lock); + + return 0; +} + +#endif /* DISABLE_THREADS */ diff --git a/src/thread/riscos/SDL_systhread.c b/src/thread/riscos/SDL_systhread.c new file mode 100644 index 000000000..4cc5790a1 --- /dev/null +++ b/src/thread/riscos/SDL_systhread.c @@ -0,0 +1,149 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2004 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +/* RISC OS version based on pthreads linux source */ + +#ifdef SAVE_RCSID +static char rcsid = + "@(#) $Id$"; +#endif + +#include "SDL_error.h" +#include "SDL_thread.h" +#include "SDL_systhread.h" + +#ifdef DISABLE_THREADS + +int SDL_SYS_CreateThread(SDL_Thread *thread, void *args) +{ + SDL_SetError("Threads have not been compiled into this version of the library"); + return(-1); +} + +void SDL_SYS_SetupThread(void) +{ + return; +} + +Uint32 SDL_ThreadID(void) +{ + return(0); +} + +void SDL_SYS_WaitThread(SDL_Thread *thread) +{ + return; +} + +void SDL_SYS_KillThread(SDL_Thread *thread) +{ + return; +} + +#else + +#include + +/* List of signals to mask in the subthreads */ +static int sig_list[] = { + SIGHUP, SIGINT, SIGQUIT, SIGPIPE, SIGALRM, SIGTERM, SIGCHLD, SIGWINCH, + SIGVTALRM, SIGPROF, 0 +}; + +#include + +int riscos_using_threads = 0; +Uint32 riscos_main_thread = 0; /* Thread running events */ + +static void *RunThread(void *data) +{ + SDL_RunThread(data); + pthread_exit((void*)0); + return((void *)0); /* Prevent compiler warning */ +} + +int SDL_SYS_CreateThread(SDL_Thread *thread, void *args) +{ + pthread_attr_t type; + + /* Set the thread attributes */ + if ( pthread_attr_init(&type) != 0 ) { + SDL_SetError("Couldn't initialize pthread attributes"); + return(-1); + } + pthread_attr_setdetachstate(&type, PTHREAD_CREATE_JOINABLE); + + /* Create the thread and go! */ + if ( pthread_create(&thread->handle, &type, RunThread, args) != 0 ) { + SDL_SetError("Not enough resources to create thread"); + return(-1); + } + + if (riscos_using_threads == 0) + { + riscos_using_threads = 1; + riscos_main_thread = SDL_ThreadID(); + } + + return(0); +} + +void SDL_SYS_SetupThread(void) +{ + int i; + sigset_t mask; + + /* Mask asynchronous signals for this thread */ + sigemptyset(&mask); + for ( i=0; sig_list[i]; ++i ) { + sigaddset(&mask, sig_list[i]); + } + pthread_sigmask(SIG_BLOCK, &mask, 0); + +#ifdef PTHREAD_CANCEL_ASYNCHRONOUS + /* Allow ourselves to be asynchronously cancelled */ + { int oldstate; + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldstate); + } +#endif +} + +Uint32 SDL_ThreadID(void) +{ + return((Uint32)pthread_self()); +} + +void SDL_SYS_WaitThread(SDL_Thread *thread) +{ + pthread_join(thread->handle, 0); +} + +void SDL_SYS_KillThread(SDL_Thread *thread) +{ +#ifdef PTHREAD_CANCEL_ASYNCHRONOUS + pthread_cancel(thread->handle); +#else + pthread_kill(thread->handle, SIGKILL); +#endif +} + +#endif diff --git a/src/thread/riscos/SDL_systhread_c.h b/src/thread/riscos/SDL_systhread_c.h new file mode 100644 index 000000000..5dbdb7457 --- /dev/null +++ b/src/thread/riscos/SDL_systhread_c.h @@ -0,0 +1,33 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2004 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +#ifdef DISABLE_THREADS + +typedef int SYS_ThreadHandle; + +#else + +#include + +typedef pthread_t SYS_ThreadHandle; + +#endif diff --git a/src/timer/riscos/SDL_systimer.c b/src/timer/riscos/SDL_systimer.c index 4493a0d71..b5aeca271 100644 --- a/src/timer/riscos/SDL_systimer.c +++ b/src/timer/riscos/SDL_systimer.c @@ -36,10 +36,19 @@ static char rcsid = #include "SDL_timer.h" #include "SDL_timer_c.h" +#ifdef DISABLE_THREADS /* Timer start/reset time */ static Uint32 timerStart; /* Timer running function */ void RISCOS_CheckTimer(); +#else +#include +extern Uint32 riscos_main_thread; +extern int riscos_using_threads; +extern Uint32 SDL_ThreadID(); +extern Uint32 SDL_EventThreadID(void); +#endif + extern void RISCOS_BackgroundTasks(void); @@ -75,18 +84,36 @@ Uint32 SDL_GetTicks (void) } -extern void DRenderer_FillBuffers(); - void SDL_Delay (Uint32 ms) { Uint32 now,then,elapsed; +#ifndef DISABLE_THREADS + int is_event_thread; + if (riscos_using_threads) + { + is_event_thread = 0; + if (SDL_EventThreadID()) + { + if (SDL_EventThreadID() == SDL_ThreadID()) is_event_thread = 1; + } else if (SDL_ThreadID() == riscos_main_thread) is_event_thread = 1; + } else is_event_thread = 1; +#endif + +/*TODO: Next version of unixlib may allow us to use usleep here */ +/* for non event threads */ /* Set the timeout interval - Linux only needs to do this once */ then = SDL_GetTicks(); do { /* Do background tasks required while sleeping as we are not multithreaded */ +#ifdef DISABLE_THREADS RISCOS_BackgroundTasks(); +#else + /* For threaded build only run background tasks in event thread */ + if (is_event_thread) RISCOS_BackgroundTasks(); +#endif + /* Calculate the time interval left (in case of interrupt) */ now = SDL_GetTicks(); elapsed = (now-then); @@ -95,10 +122,18 @@ void SDL_Delay (Uint32 ms) break; } ms -= elapsed; +#ifndef DISABLE_THREADS + /* Need to yield to let other threads have a go */ + if (riscos_using_threads) pthread_yield(); +#endif } while ( 1 ); } +#ifdef DISABLE_THREADS + +/* Non-threaded version of timer */ + int SDL_SYS_TimerInit(void) { return(0); @@ -144,3 +179,56 @@ void RISCOS_CheckTimer() if (SDL_alarm_interval) timerStart = SDL_GetTicks(); } } + +#else + +/* Threaded version of timer - based on code for linux */ + +#include "SDL_thread.h" + +/* Data to handle a single periodic alarm */ +static int timer_alive = 0; +static SDL_Thread *timer = NULL; + +static int RunTimer(void *unused) +{ + while ( timer_alive ) { + if ( SDL_timer_running ) { + SDL_ThreadedTimerCheck(); + } + SDL_Delay(1); + } + return(0); +} + +/* This is only called if the event thread is not running */ +int SDL_SYS_TimerInit(void) +{ + timer_alive = 1; + timer = SDL_CreateThread(RunTimer, NULL); + if ( timer == NULL ) + return(-1); + return(SDL_SetTimerThreaded(1)); +} + +void SDL_SYS_TimerQuit(void) +{ + timer_alive = 0; + if ( timer ) { + SDL_WaitThread(timer, NULL); + timer = NULL; + } +} + +int SDL_SYS_StartTimer(void) +{ + SDL_SetError("Internal logic error: RISCOS uses threaded timer"); + return(-1); +} + +void SDL_SYS_StopTimer(void) +{ + return; +} + +#endif /* DISABLE_THREADS */ diff --git a/src/video/riscos/SDL_riscosevents.c b/src/video/riscos/SDL_riscosevents.c index 8ec23fd31..39804f962 100644 --- a/src/video/riscos/SDL_riscosevents.c +++ b/src/video/riscos/SDL_riscosevents.c @@ -76,18 +76,23 @@ void RISCOS_PollKeyboard(); void RISCOS_PollMouseHelper(_THIS, int fullscreen); +#ifdef DISABLE_THREADS extern void DRenderer_FillBuffers(); /* Timer running function */ extern void RISCOS_CheckTimer(); +#endif + void FULLSCREEN_PumpEvents(_THIS) { /* Current implementation requires keyboard and mouse polling */ RISCOS_PollKeyboard(); RISCOS_PollMouse(this); +#ifdef DISABLE_THREADS DRenderer_FillBuffers(); if (SDL_timer_running) RISCOS_CheckTimer(); +#endif } @@ -538,3 +543,4 @@ static SDL_keysym *TranslateKey(int intkey, SDL_keysym *keysym, int pressed) } /* end of SDL_riscosevents.c ... */ + diff --git a/src/video/riscos/SDL_riscostask.c b/src/video/riscos/SDL_riscostask.c index 03f7856d7..c07c8fd6f 100644 --- a/src/video/riscos/SDL_riscostask.c +++ b/src/video/riscos/SDL_riscostask.c @@ -39,6 +39,11 @@ #include "SDL_riscostask.h" +#ifndef DISABLE_THREADS +#include +pthread_t main_thread; +#endif + /* RISCOS variables */ static int task_handle = 0; @@ -108,6 +113,10 @@ int RISCOS_InitTask() return 1; } +#ifndef DISABLE_THREADS + main_thread = pthread_self(); +#endif + return 0; } diff --git a/src/video/riscos/SDL_wimppoll.c b/src/video/riscos/SDL_wimppoll.c index e987bad69..624ea7fb6 100644 --- a/src/video/riscos/SDL_wimppoll.c +++ b/src/video/riscos/SDL_wimppoll.c @@ -43,6 +43,10 @@ #include "swis.h" #include "unixlib/os.h" +#ifndef DISABLE_THREADS +#include +#endif + /* Local functions */ void WIMP_Poll(_THIS, int waitTime); void WIMP_SetFocus(int win); @@ -56,11 +60,16 @@ void WIMP_PaletteChanged(_THIS); extern void WIMP_PollMouse(_THIS); extern void RISCOS_PollKeyboard(); +#ifdef DISABLE_THREADS extern void DRenderer_FillBuffers(); /* Timer running function */ extern void RISCOS_CheckTimer(); +#else +extern int riscos_using_threads; +#endif + /* Mouse cursor handling */ extern void WIMP_ReshowCursor(_THIS); @@ -75,8 +84,10 @@ void WIMP_PumpEvents(_THIS) WIMP_Poll(this, 0); if (hasFocus) RISCOS_PollKeyboard(); if (mouseInWindow) WIMP_PollMouse(this); +#ifdef DISABLE_THREADS DRenderer_FillBuffers(); if (SDL_timer_running) RISCOS_CheckTimer(); +#endif } @@ -100,10 +111,15 @@ void WIMP_Poll(_THIS, int waitTime) while (doPoll) { +#ifndef DISABLE_THREADS + /* Stop thread callbacks while program is paged out */ + if (riscos_using_threads) __pthread_stop_ticker(); +#endif + if (waitTime <= 0) { regs.r[0] = pollMask; /* Poll Mask */ - /* For no wait time mask out null event so we wait until something happens */ + /* For no wait time mask out null event so we wait until something happens */ if (waitTime < 0) regs.r[0] |= 1; regs.r[1] = (int)message; _kernel_swi(Wimp_Poll, ®s, ®s); @@ -116,34 +132,34 @@ void WIMP_Poll(_THIS, int waitTime) } /* Flag to specify if we post a SDL_SysWMEvent */ - sysEvent = 0; + sysEvent = 0; code = (unsigned int)regs.r[0]; - switch(code) - { - case 0: /* Null Event - drop out for standard processing*/ - doPoll = 0; - break; - - case 1: /* Redraw window */ - _kernel_swi(Wimp_RedrawWindow, ®s,®s); - if (message[0] == sdlWindow) - { - while (regs.r[0]) - { - WIMP_PlotSprite(this, message[1], message[2]); - _kernel_swi(Wimp_GetRectangle, ®s, ®s); - } - } else - { - /* TODO: Currently we just eat them - we may need to pass them on */ - while (regs.r[0]) - { - _kernel_swi(Wimp_GetRectangle, ®s, ®s); - } - } - break; + switch(code) + { + case 0: /* Null Event - drop out for standard processing*/ + doPoll = 0; + break; + + case 1: /* Redraw window */ + _kernel_swi(Wimp_RedrawWindow, ®s,®s); + if (message[0] == sdlWindow) + { + while (regs.r[0]) + { + WIMP_PlotSprite(this, message[1], message[2]); + _kernel_swi(Wimp_GetRectangle, ®s, ®s); + } + } else + { + /* TODO: Currently we just eat them - we may need to pass them on */ + while (regs.r[0]) + { + _kernel_swi(Wimp_GetRectangle, ®s, ®s); + } + } + break; case 2: /* Open window */ if ( resizeOnOpen && message[0] == sdlWindow) @@ -277,7 +293,16 @@ void WIMP_Poll(_THIS, int waitTime) /* Fall out of polling loop if message is successfully posted */ if (SDL_PrivateSysWMEvent(&wmmsg)) doPoll = 0; } - +#ifndef DISABLE_THREADS + if (riscos_using_threads) + { + /* Restart ticker here so other thread can not interfere + with the Redraw processing */ + if (riscos_using_threads) __pthread_start_ticker(); + /* Give other threads a better chance of running */ + pthread_yield(); + } +#endif } } @@ -303,7 +328,9 @@ void RISCOS_BackgroundTasks(void) { WIMP_Poll(current_video, 0); } +#ifdef DISABLE_THREADS /* Keep sound buffers running */ DRenderer_FillBuffers(); if (SDL_timer_running) RISCOS_CheckTimer(); +#endif }