From 0cbb62b329791874b1ee50161d98fb663d58e6b7 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 5 May 2025 05:44:31 +0300 Subject: [PATCH] engine: platform: try to restore SDL1.2 support (only ref_soft is tested, ref_gl doesn't work) --- engine/platform/platform.h | 1 + engine/platform/sdl1/host_sdl1.c | 354 ++++++++++++++++++ engine/platform/sdl1/in_sdl1.c | 156 ++++++++ engine/platform/sdl1/joy_sdl1.c | 36 ++ engine/platform/sdl1/platform_sdl1.h | 37 ++ engine/platform/sdl1/s_sdl1.c | 268 ++++++++++++++ engine/platform/sdl1/sys_sdl1.c | 61 ++++ engine/platform/sdl1/vid_sdl1.c | 525 +++++++++++++++++++++++++++ 8 files changed, 1438 insertions(+) create mode 100644 engine/platform/sdl1/host_sdl1.c create mode 100644 engine/platform/sdl1/in_sdl1.c create mode 100644 engine/platform/sdl1/joy_sdl1.c create mode 100644 engine/platform/sdl1/platform_sdl1.h create mode 100644 engine/platform/sdl1/s_sdl1.c create mode 100644 engine/platform/sdl1/sys_sdl1.c create mode 100644 engine/platform/sdl1/vid_sdl1.c diff --git a/engine/platform/platform.h b/engine/platform/platform.h index 0f6fadaf..2e5eedcf 100644 --- a/engine/platform/platform.h +++ b/engine/platform/platform.h @@ -256,6 +256,7 @@ void Platform_SetClipboardText( const char *buffer ); #if XASH_SDL == 1 #define SDL_SetWindowGrab( wnd, state ) SDL_WM_GrabInput( (state) ? SDL_GRAB_ON : SDL_GRAB_OFF ) +#define SDL_GetWindowGrab( wnd ) ( SDL_WM_GrabInput( SDL_GRAB_QUERY ) != SDL_GRAB_OFF ) #define SDL_MinimizeWindow( wnd ) SDL_WM_IconifyWindow() #define SDL_IsTextInputActive() host.textmode #endif diff --git a/engine/platform/sdl1/host_sdl1.c b/engine/platform/sdl1/host_sdl1.c new file mode 100644 index 00000000..05e8ba50 --- /dev/null +++ b/engine/platform/sdl1/host_sdl1.c @@ -0,0 +1,354 @@ +/* +host_sdl1.c - SDL event system handlers +Copyright (C) 2015-2025 a1batross + +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 3 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. +*/ +#include +#include + +#include "common.h" +#include "keydefs.h" +#include "input.h" +#include "client.h" +#include "vgui_draw.h" +#include "platform_sdl1.h" +#include "sound.h" +#include "vid_common.h" + +#define SDL_SCANCODE_A SDLK_a +#define SDL_SCANCODE_Z SDLK_z +#define SDL_SCANCODE_1 SDLK_1 +#define SDL_SCANCODE_9 SDLK_9 +#define SDL_SCANCODE_F1 SDLK_F1 +#define SDL_SCANCODE_F12 SDLK_F12 +#define SDL_SCANCODE_GRAVE SDLK_BACKQUOTE +#define SDL_SCANCODE_0 SDLK_0 +#define SDL_SCANCODE_BACKSLASH SDLK_BACKSLASH +#define SDL_SCANCODE_LEFTBRACKET SDLK_LEFTBRACKET +#define SDL_SCANCODE_RIGHTBRACKET SDLK_RIGHTBRACKET +#define SDL_SCANCODE_EQUALS SDLK_EQUALS +#define SDL_SCANCODE_MINUS SDLK_MINUS +#define SDL_SCANCODE_TAB SDLK_TAB +#define SDL_SCANCODE_RETURN SDLK_RETURN +#define SDL_SCANCODE_ESCAPE SDLK_ESCAPE +#define SDL_SCANCODE_SPACE SDLK_SPACE +#define SDL_SCANCODE_BACKSPACE SDLK_BACKSPACE +#define SDL_SCANCODE_UP SDLK_UP +#define SDL_SCANCODE_LEFT SDLK_LEFT +#define SDL_SCANCODE_DOWN SDLK_DOWN +#define SDL_SCANCODE_RIGHT SDLK_RIGHT +#define SDL_SCANCODE_LALT SDLK_LALT +#define SDL_SCANCODE_RALT SDLK_RALT +#define SDL_SCANCODE_LCTRL SDLK_LCTRL +#define SDL_SCANCODE_RCTRL SDLK_RCTRL +#define SDL_SCANCODE_LSHIFT SDLK_LSHIFT +#define SDL_SCANCODE_RSHIFT SDLK_RSHIFT +#define SDL_SCANCODE_LGUI SDLK_LMETA +#define SDL_SCANCODE_RGUI SDLK_RMETA +#define SDL_SCANCODE_INSERT SDLK_INSERT +#define SDL_SCANCODE_DELETE SDLK_DELETE +#define SDL_SCANCODE_PAGEDOWN SDLK_PAGEDOWN +#define SDL_SCANCODE_PAGEUP SDLK_PAGEUP +#define SDL_SCANCODE_HOME SDLK_HOME +#define SDL_SCANCODE_END SDLK_END +#define SDL_SCANCODE_KP_1 SDLK_KP1 +#define SDL_SCANCODE_KP_2 SDLK_KP2 +#define SDL_SCANCODE_KP_3 SDLK_KP3 +#define SDL_SCANCODE_KP_4 SDLK_KP4 +#define SDL_SCANCODE_KP_5 SDLK_KP5 +#define SDL_SCANCODE_KP_6 SDLK_KP6 +#define SDL_SCANCODE_KP_7 SDLK_KP7 +#define SDL_SCANCODE_KP_8 SDLK_KP8 +#define SDL_SCANCODE_KP_9 SDLK_KP9 +#define SDL_SCANCODE_KP_0 SDLK_KP0 +#define SDL_SCANCODE_KP_PERIOD SDLK_KP_PERIOD +#define SDL_SCANCODE_KP_ENTER SDLK_KP_ENTER +#define SDL_SCANCODE_KP_PLUS SDLK_KP_PLUS +#define SDL_SCANCODE_KP_MINUS SDLK_KP_MINUS +#define SDL_SCANCODE_KP_DIVIDE SDLK_KP_DIVIDE +#define SDL_SCANCODE_KP_MULTIPLY SDLK_KP_MULTIPLY +#define SDL_SCANCODE_NUMLOCKCLEAR SDLK_NUMLOCK +#define SDL_SCANCODE_CAPSLOCK SDLK_CAPSLOCK +#define SDL_SCANCODE_SLASH SDLK_SLASH +#define SDL_SCANCODE_PERIOD SDLK_PERIOD +#define SDL_SCANCODE_SEMICOLON SDLK_SEMICOLON +#define SDL_SCANCODE_APOSTROPHE SDLK_QUOTE +#define SDL_SCANCODE_COMMA SDLK_COMMA +#define SDL_SCANCODE_PRINTSCREEN SDLK_PRINT +#define SDL_SCANCODE_UNKNOWN SDLK_UNKNOWN +#define SDL_GetScancodeName( x ) "unknown" + +/* +============= +SDLash_KeyEvent + +============= +*/ +static void SDLash_KeyEvent( SDL_KeyboardEvent key ) +{ + int down = key.state != SDL_RELEASED; + int keynum = key.keysym.sym; + + if( SDL_IsTextInputActive( ) && down ) + { + // this is how engine understands ctrl+c, ctrl+v and other hotkeys + if( cls.key_dest != key_game && FBitSet( SDL_GetModState(), KMOD_CTRL )) + { + if( keynum >= SDL_SCANCODE_A && keynum <= SDL_SCANCODE_Z ) + { + keynum = keynum - SDL_SCANCODE_A + 1; + CL_CharEvent( keynum ); + } + + return; + } + + if( keynum >= SDLK_KP0 && keynum <= SDLK_KP9 ) + keynum -= SDLK_KP0 + '0'; + + if( isprint( keynum )) + { + if( FBitSet( SDL_GetModState(), KMOD_SHIFT )) + keynum = Key_ToUpper( keynum ); + + CL_CharEvent( keynum ); + } + } + +#define DECLARE_KEY_RANGE( min, max, repl ) \ + if( keynum >= (min) && keynum <= (max) ) \ + { \ + keynum = keynum - (min) + (repl); \ + } + + DECLARE_KEY_RANGE( SDL_SCANCODE_A, SDL_SCANCODE_Z, 'a' ) + else DECLARE_KEY_RANGE( SDL_SCANCODE_1, SDL_SCANCODE_9, '1' ) + else DECLARE_KEY_RANGE( SDL_SCANCODE_F1, SDL_SCANCODE_F12, K_F1 ) + else + { + qboolean numLock = FBitSet( SDL_GetModState(), KMOD_NUM ); + + switch( keynum ) + { + case SDL_SCANCODE_GRAVE: keynum = '`'; break; + case SDL_SCANCODE_0: keynum = '0'; break; + case SDL_SCANCODE_BACKSLASH: keynum = '\\'; break; + case SDL_SCANCODE_LEFTBRACKET: keynum = '['; break; + case SDL_SCANCODE_RIGHTBRACKET: keynum = ']'; break; + case SDL_SCANCODE_EQUALS: keynum = '='; break; + case SDL_SCANCODE_MINUS: keynum = '-'; break; + case SDL_SCANCODE_TAB: keynum = K_TAB; break; + case SDL_SCANCODE_RETURN: keynum = K_ENTER; break; + case SDL_SCANCODE_ESCAPE: keynum = K_ESCAPE; break; + case SDL_SCANCODE_SPACE: keynum = K_SPACE; break; + case SDL_SCANCODE_BACKSPACE: keynum = K_BACKSPACE; break; + case SDL_SCANCODE_UP: keynum = K_UPARROW; break; + case SDL_SCANCODE_LEFT: keynum = K_LEFTARROW; break; + case SDL_SCANCODE_DOWN: keynum = K_DOWNARROW; break; + case SDL_SCANCODE_RIGHT: keynum = K_RIGHTARROW; break; + case SDL_SCANCODE_LALT: + case SDL_SCANCODE_RALT: keynum = K_ALT; break; + case SDL_SCANCODE_LCTRL: + case SDL_SCANCODE_RCTRL: keynum = K_CTRL; break; + case SDL_SCANCODE_LSHIFT: + case SDL_SCANCODE_RSHIFT: keynum = K_SHIFT; break; + case SDL_SCANCODE_LGUI: + case SDL_SCANCODE_RGUI: keynum = K_WIN; break; + case SDL_SCANCODE_INSERT: keynum = K_INS; break; + case SDL_SCANCODE_DELETE: keynum = K_DEL; break; + case SDL_SCANCODE_PAGEDOWN: keynum = K_PGDN; break; + case SDL_SCANCODE_PAGEUP: keynum = K_PGUP; break; + case SDL_SCANCODE_HOME: keynum = K_HOME; break; + case SDL_SCANCODE_END: keynum = K_END; break; + case SDL_SCANCODE_KP_1: keynum = numLock ? '1' : K_KP_END; break; + case SDL_SCANCODE_KP_2: keynum = numLock ? '2' : K_KP_DOWNARROW; break; + case SDL_SCANCODE_KP_3: keynum = numLock ? '3' : K_KP_PGDN; break; + case SDL_SCANCODE_KP_4: keynum = numLock ? '4' : K_KP_LEFTARROW; break; + case SDL_SCANCODE_KP_5: keynum = numLock ? '5' : K_KP_5; break; + case SDL_SCANCODE_KP_6: keynum = numLock ? '6' : K_KP_RIGHTARROW; break; + case SDL_SCANCODE_KP_7: keynum = numLock ? '7' : K_KP_HOME; break; + case SDL_SCANCODE_KP_8: keynum = numLock ? '8' : K_KP_UPARROW; break; + case SDL_SCANCODE_KP_9: keynum = numLock ? '9' : K_KP_PGUP; break; + case SDL_SCANCODE_KP_0: keynum = numLock ? '0' : K_KP_INS; break; + case SDL_SCANCODE_KP_PERIOD: keynum = K_KP_DEL; break; + case SDL_SCANCODE_KP_ENTER: keynum = K_KP_ENTER; break; + case SDL_SCANCODE_KP_PLUS: keynum = K_KP_PLUS; break; + case SDL_SCANCODE_KP_MINUS: keynum = K_KP_MINUS; break; + case SDL_SCANCODE_KP_DIVIDE: keynum = K_KP_SLASH; break; + case SDL_SCANCODE_KP_MULTIPLY: keynum = '*'; break; + case SDL_SCANCODE_NUMLOCKCLEAR: keynum = K_KP_NUMLOCK; break; + case SDL_SCANCODE_CAPSLOCK: keynum = K_CAPSLOCK; break; + case SDL_SCANCODE_SLASH: keynum = '/'; break; + case SDL_SCANCODE_PERIOD: keynum = '.'; break; + case SDL_SCANCODE_SEMICOLON: keynum = ';'; break; + case SDL_SCANCODE_APOSTROPHE: keynum = '\''; break; + case SDL_SCANCODE_COMMA: keynum = ','; break; + case SDL_SCANCODE_PRINTSCREEN: + { + host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME; + break; + } + case SDL_SCANCODE_UNKNOWN: + { + if( down ) Con_Reportf( "%s: Unknown scancode\n", __func__ ); + return; + } + default: + if( down ) Con_Reportf( "%s: Unknown key: %s = %i\n", __func__, SDL_GetScancodeName( keynum ), keynum ); + return; + } + } + +#undef DECLARE_KEY_RANGE + + Key_Event( keynum, down ); +} + +/* +============= +SDLash_MouseEvent + +============= +*/ +static void SDLash_MouseEvent( SDL_MouseButtonEvent button ) +{ + int down; + + if( button.state == SDL_RELEASED ) + down = 0; + else + down = 1; + + switch( button.button ) + { + case SDL_BUTTON_LEFT: + IN_MouseEvent( 0, down ); + break; + case SDL_BUTTON_RIGHT: + IN_MouseEvent( 1, down ); + break; + case SDL_BUTTON_MIDDLE: + IN_MouseEvent( 2, down ); + break; + case SDL_BUTTON_X1: + IN_MouseEvent( 3, down ); + break; + case SDL_BUTTON_X2: + IN_MouseEvent( 4, down ); + break; + case SDL_BUTTON_WHEELUP: + IN_MWheelEvent( -1 ); + break; + case SDL_BUTTON_WHEELDOWN: + IN_MWheelEvent( 1 ); + break; + default: + Con_Printf( "Unknown mouse button ID: %d\n", button.button ); + } +} + +static void SDLash_ActiveEvent( int gain ) +{ + if( gain ) + { + host.status = HOST_FRAME; + if( cls.key_dest == key_game ) + IN_ActivateMouse( ); + + host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME; + if( vid_fullscreen.value == WINDOW_MODE_FULLSCREEN ) + VID_SetMode(); + } + else + { + host.status = HOST_NOFOCUS; + + if( cls.key_dest == key_game ) + { + Key_ClearStates(); + IN_DeactivateMouse(); + } + + host.force_draw_version_time = host.realtime + 2.0; + VID_RestoreScreenResolution(); + } +} + +/* +============= +SDLash_EventFilter + +============= +*/ +static void SDLash_EventHandler( SDL_Event *event ) +{ + switch ( event->type ) + { + /* Mouse events */ + case SDL_MOUSEMOTION: + if( host.mouse_visible ) + SDL_GetRelativeMouseState( NULL, NULL ); + break; + + case SDL_MOUSEBUTTONUP: + case SDL_MOUSEBUTTONDOWN: + SDLash_MouseEvent( event->button ); + break; + + /* Keyboard events */ + case SDL_KEYDOWN: + case SDL_KEYUP: + SDLash_KeyEvent( event->key ); + break; + + case SDL_QUIT: + Sys_Quit( "caught SDL_QUIT" ); + break; + case SDL_VIDEORESIZE: + VID_SaveWindowSize( event->resize.w, event->resize.h, false ); + break; + case SDL_ACTIVEEVENT: + SDLash_ActiveEvent( event->active.gain ); + break; + } +} + +/* +============= +SDLash_RunEvents + +============= +*/ +void Platform_RunEvents( void ) +{ + SDL_Event event; + + while( host.status != HOST_CRASHED && !host.shutdown_issued && SDL_PollEvent( &event ) ) + SDLash_EventHandler( &event ); +} + +/* +======================== +Platform_PreCreateMove + +this should disable mouse look on client when m_ignore enabled +TODO: kill mouse in win32 clients too +======================== +*/ +void Platform_PreCreateMove( void ) +{ + if( m_ignore.value ) + { + SDL_GetRelativeMouseState( NULL, NULL ); + SDL_ShowCursor( SDL_TRUE ); + } +} diff --git a/engine/platform/sdl1/in_sdl1.c b/engine/platform/sdl1/in_sdl1.c new file mode 100644 index 00000000..30d5e5d0 --- /dev/null +++ b/engine/platform/sdl1/in_sdl1.c @@ -0,0 +1,156 @@ +/* +in_sdl1.c - SDL input component +Copyright (C) 2018-2025 a1batross + +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 3 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. +*/ +#include + +#include "common.h" +#include "keydefs.h" +#include "input.h" +#include "client.h" +#include "vgui_draw.h" +#include "platform_sdl1.h" +#include "sound.h" +#include "vid_common.h" + +#define SDL_WarpMouseInWindow( win, x, y ) SDL_WarpMouse( ( x ), ( y ) ) + +/* +============= +Platform_GetMousePos + +============= +*/ +void GAME_EXPORT Platform_GetMousePos( int *x, int *y ) +{ + SDL_GetMouseState( x, y ); + + if( x && window_width.value && window_width.value != refState.width ) + { + float factor = refState.width / window_width.value; + *x = *x * factor; + } + + if( y && window_height.value && window_height.value != refState.height ) + { + float factor = refState.height / window_height.value; + *y = *y * factor; + } +} + +/* +============= +Platform_SetMousePos + +============ +*/ +void GAME_EXPORT Platform_SetMousePos( int x, int y ) +{ + SDL_WarpMouseInWindow( host.hWnd, x, y ); +} + +/* +======================== +Platform_MouseMove + +======================== +*/ +void Platform_MouseMove( float *x, float *y ) +{ + int m_x, m_y; + SDL_GetRelativeMouseState( &m_x, &m_y ); + *x = (float)m_x; + *y = (float)m_y; +} + +/* +============= +Platform_GetClipobardText + +============= +*/ +int Platform_GetClipboardText( char *buffer, size_t size ) +{ + buffer[0] = 0; + return 0; +} + +/* +============= +Platform_SetClipobardText + +============= +*/ +void Platform_SetClipboardText( const char *buffer ) +{ +} + +/* +============= +Platform_EnableTextInput + +============= +*/ +void Platform_EnableTextInput( qboolean enable ) +{ +} + + +/* +======================== +Platform_SetCursorType + +======================== +*/ +void Platform_SetCursorType( VGUI_DefaultCursor type ) +{ + qboolean visible; + + switch( type ) + { + case dc_user: + case dc_none: + visible = false; + break; + default: + visible = true; + break; + } + + // never disable cursor in touch emulation mode + if( !visible && Touch_WantVisibleCursor( )) + return; + + host.mouse_visible = visible; + VGui_UpdateInternalCursorState( type ); + + if( host.mouse_visible ) + { + SDL_ShowCursor( true ); + } + else + { + SDL_ShowCursor( false ); + } +} + +/* +======================== +Platform_GetKeyModifiers + +======================== +*/ +key_modifier_t Platform_GetKeyModifiers( void ) +{ + return KeyModifier_None; +} diff --git a/engine/platform/sdl1/joy_sdl1.c b/engine/platform/sdl1/joy_sdl1.c new file mode 100644 index 00000000..5856e7db --- /dev/null +++ b/engine/platform/sdl1/joy_sdl1.c @@ -0,0 +1,36 @@ +/* +joy_sdl1.c - SDL gamepads +Copyright (C) 2018-2025 a1batross + +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 3 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. +*/ +#include "platform.h" + +void Platform_CalibrateGamepadGyro( void ) +{ +} + +void Platform_Vibrate2( float time, int val1, int val2, uint flags ) +{ +} + +void Platform_Vibrate( float time, char flags ) +{ +} + +int Platform_JoyInit( void ) +{ + return 0; +} + +void Platform_JoyShutdown( void ) +{ +} diff --git a/engine/platform/sdl1/platform_sdl1.h b/engine/platform/sdl1/platform_sdl1.h new file mode 100644 index 00000000..0ed0f03c --- /dev/null +++ b/engine/platform/sdl1/platform_sdl1.h @@ -0,0 +1,37 @@ +/* +platform_sdl1.h - SDL backend internal header +Copyright (C) 2015-2018 a1batross + +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 3 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. +*/ + +#pragma once +#ifndef KEYWRAPPER_H +#define KEYWRAPPER_H +#ifdef XASH_SDL + +#include "platform.h" + +// window management +void VID_RestoreScreenResolution( void ); +qboolean VID_CreateWindow( int width, int height, window_mode_t window_mode ); +void VID_DestroyWindow( void ); +void GL_InitExtensions( void ); +qboolean GL_DeleteContext( void ); +void VID_SaveWindowSize( int width, int height, qboolean maximized ); + +// +// joy_sdl.c +// +void SDLash_HandleGameControllerEvent( SDL_Event *ev ); + +#endif // XASH_SDL +#endif // KEYWRAPPER_H diff --git a/engine/platform/sdl1/s_sdl1.c b/engine/platform/sdl1/s_sdl1.c new file mode 100644 index 00000000..86180d9a --- /dev/null +++ b/engine/platform/sdl1/s_sdl1.c @@ -0,0 +1,268 @@ +/* +s_sdl1.c - sound hardware output +Copyright (C) 2009 Uncle Mike +Copyright (C) 2025 Xash3D FWGS contributors + +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 3 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. +*/ + +#include "common.h" +#include "platform.h" +#if XASH_SOUND == SOUND_SDL + +#include "sound.h" +#include "voice.h" + +#include +#include + +#define SAMPLE_16BIT_SHIFT 1 +#define SECONDARY_BUFFER_SIZE 0x10000 + +#define SDL_GetCurrentAudioDriver() "legacysdl" +#define SDL_OpenAudioDevice( a, b, c, d, e ) SDL_OpenAudio( ( c ), ( d ) ) +#define SDL_CloseAudioDevice( a ) SDL_CloseAudio() +#define SDL_PauseAudioDevice( a, b ) SDL_PauseAudio( ( b ) ) +#define SDL_LockAudioDevice( x ) SDL_LockAudio() +#define SDL_UnlockAudioDevice( x ) SDL_UnlockAudio() +#define SDLash_IsAudioError( x ) (( x ) != 0) + +/* +======================================================================= +Global variables. Must be visible to window-procedure function +so it can unlock and free the data block after it has been played. +======================================================================= +*/ +static int sdl_dev; +static char sdl_backend_name[32]; + +static void SDL_SoundCallback( void *userdata, Uint8 *stream, int len ) +{ + const int size = dma.samples << 1; + int pos; + int wrapped; + + if( !dma.buffer ) + { + memset( stream, 0, len ); + return; + } + + pos = dma.samplepos << 1; + if( pos >= size ) + pos = dma.samplepos = 0; + + wrapped = pos + len - size; + + if( wrapped < 0 ) + { + memcpy( stream, dma.buffer + pos, len ); + dma.samplepos += len >> 1; + } + else + { + int remaining = size - pos; + + memcpy( stream, dma.buffer + pos, remaining ); + memcpy( stream + remaining, dma.buffer, wrapped ); + dma.samplepos = wrapped >> 1; + } + + if( dma.samplepos >= size ) + dma.samplepos = 0; +} + +/* +================== +SNDDMA_Init + +Try to find a sound device to mix for. +Returns false if nothing is found. +================== +*/ +qboolean SNDDMA_Init( void ) +{ + SDL_AudioSpec desired, obtained; + int samplecount; + const char *driver = NULL; + + // reinitialize SDL with our driver just in case + if( SDL_WasInit( SDL_INIT_AUDIO )) + SDL_QuitSubSystem( SDL_INIT_AUDIO ); + + if( SDL_InitSubSystem( SDL_INIT_AUDIO )) + { + Con_Reportf( S_ERROR "Audio: SDL: %s \n", SDL_GetError( ) ); + return false; + } + + memset( &desired, 0, sizeof( desired ) ); + desired.freq = SOUND_DMA_SPEED; + desired.format = AUDIO_S16LSB; + desired.samples = 1024; + desired.channels = 2; + desired.callback = SDL_SoundCallback; + + sdl_dev = SDL_OpenAudioDevice( NULL, 0, &desired, &obtained, 0 ); + + if( SDLash_IsAudioError( sdl_dev )) + { + Con_Printf( "Couldn't open SDL audio: %s\n", SDL_GetError( ) ); + return false; + } + + if( obtained.format != AUDIO_S16LSB ) + { + Con_Printf( "SDL audio format %d unsupported.\n", obtained.format ); + goto fail; + } + + if( obtained.channels != 1 && obtained.channels != 2 ) + { + Con_Printf( "SDL audio channels %d unsupported.\n", obtained.channels ); + goto fail; + } + + dma.format.speed = obtained.freq; + dma.format.channels = obtained.channels; + dma.format.width = 2; + samplecount = s_samplecount.value; + if( !samplecount ) + samplecount = 0x8000; + dma.samples = samplecount * obtained.channels; + dma.buffer = Mem_Calloc( sndpool, dma.samples * 2 ); + dma.samplepos = 0; + + Con_Printf( "Using SDL audio driver: %s @ %d Hz\n", SDL_GetCurrentAudioDriver( ), obtained.freq ); + Q_snprintf( sdl_backend_name, sizeof( sdl_backend_name ), "SDL" ); + dma.initialized = true; + dma.backendName = sdl_backend_name; + + SNDDMA_Activate( true ); + + return true; + +fail: + SNDDMA_Shutdown( ); + return false; +} + + +/* +============== +SNDDMA_BeginPainting + +Makes sure dma.buffer is valid +=============== +*/ +void SNDDMA_BeginPainting( void ) +{ + SDL_LockAudioDevice( sdl_dev ); +} + +/* +============== +SNDDMA_Submit + +Send sound to device if buffer isn't really the dma buffer +Also unlocks the dsound buffer +=============== +*/ +void SNDDMA_Submit( void ) +{ + SDL_UnlockAudioDevice( sdl_dev ); +} + +/* +============== +SNDDMA_Shutdown + +Reset the sound device for exiting +=============== +*/ +void SNDDMA_Shutdown( void ) +{ + Con_Printf( "Shutting down audio.\n" ); + dma.initialized = false; + + if( sdl_dev ) + { + SNDDMA_Activate( false ); + + SDL_CloseAudioDevice( sdl_dev ); + } + + if( SDL_WasInit( SDL_INIT_AUDIO )) + SDL_QuitSubSystem( SDL_INIT_AUDIO ); + + if( dma.buffer ) + { + Mem_Free( dma.buffer ); + dma.buffer = NULL; + } +} + +/* +=========== +SNDDMA_Activate +Called when the main window gains or loses focus. +The window have been destroyed and recreated +between a deactivate and an activate. +=========== +*/ +void SNDDMA_Activate( qboolean active ) +{ + if( !dma.initialized ) + return; + + SDL_PauseAudioDevice( sdl_dev, !active ); +} + +/* +=========== +VoiceCapture_Init +=========== +*/ +qboolean VoiceCapture_Init( void ) +{ + return false; +} + +/* +=========== +VoiceCapture_Activate +=========== +*/ +qboolean VoiceCapture_Activate( qboolean activate ) +{ + return false; +} + +/* +=========== +VoiceCapture_Lock +=========== +*/ +qboolean VoiceCapture_Lock( qboolean lock ) +{ + return false; +} + +/* +========== +VoiceCapture_Shutdown +========== +*/ +void VoiceCapture_Shutdown( void ) +{ +} + +#endif // XASH_SOUND == SOUND_SDL diff --git a/engine/platform/sdl1/sys_sdl1.c b/engine/platform/sdl1/sys_sdl1.c new file mode 100644 index 00000000..32451bb4 --- /dev/null +++ b/engine/platform/sdl1/sys_sdl1.c @@ -0,0 +1,61 @@ +/* +sys_sdl1.c - SDL1 system utils +Copyright (C) 2018 a1batross + +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 3 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. +*/ + +#include +#include "platform.h" +#include "platform_sdl1.h" + +#if XASH_TIMER == TIMER_SDL +double Platform_DoubleTime( void ) +{ + static Uint64 g_PerformanceFrequency; + static Uint64 g_ClockStart; + Uint64 CurrentTime; + + if( !g_PerformanceFrequency ) + { + g_PerformanceFrequency = SDL_GetPerformanceFrequency(); + g_ClockStart = SDL_GetPerformanceCounter(); + } + CurrentTime = SDL_GetPerformanceCounter(); + return (double)( CurrentTime - g_ClockStart ) / (double)( g_PerformanceFrequency ); +} + +void Platform_Sleep( int msec ) +{ + SDL_Delay( msec ); +} +#endif // XASH_TIMER == TIMER_SDL + +#if XASH_MESSAGEBOX == MSGBOX_SDL +void Platform_MessageBox( const char *title, const char *message, qboolean parentMainWindow ) +{ + SDL_ShowSimpleMessageBox( SDL_MESSAGEBOX_ERROR, title, message, parentMainWindow ? host.hWnd : NULL ); +} +#endif // XASH_MESSAGEBOX == MSGBOX_SDL + +void SDLash_Init( const char *basedir ) +{ + if( SDL_Init( SDL_INIT_TIMER | SDL_INIT_VIDEO )) + { + Sys_Warn( "SDL_Init failed: %s", SDL_GetError() ); + host.type = HOST_DEDICATED; + } +} + +void SDLash_Shutdown( void ) +{ + SDL_Quit(); +} diff --git a/engine/platform/sdl1/vid_sdl1.c b/engine/platform/sdl1/vid_sdl1.c new file mode 100644 index 00000000..8aa7473b --- /dev/null +++ b/engine/platform/sdl1/vid_sdl1.c @@ -0,0 +1,525 @@ +/* +vid_sdl1.c - SDL vid component +Copyright (C) 2018 a1batross + +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 3 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. +*/ +#include +#include "common.h" +#include "client.h" +#include "vid_common.h" +#include "platform_sdl1.h" + +static vidmode_t *vidmodes = NULL; +static int num_vidmodes = 0; +static void GL_SetupAttributes( void ); +struct +{ + int prev_width, prev_height; +} sdlState = { 640, 480 }; + +struct +{ + int width, height; + SDL_Surface *surf; + SDL_Surface *win; +} sw; + +qboolean SW_CreateBuffer( int width, int height, uint *stride, uint *bpp, uint *r, uint *g, uint *b ) +{ + sw.width = width; + sw.height = height; + sw.win = SDL_GetVideoSurface(); + + // sdl will create renderer if hw framebuffer unavailiable, so cannot fallback here + // if it is failed, it is not possible to draw with SDL in REF_SOFTWARE mode + if( !sw.win ) + { + Sys_Warn( "failed to initialize software output, try running with -glblit flag" ); + return false; + } + + *bpp = sw.win->format->BytesPerPixel; + *r = sw.win->format->Rmask; + *g = sw.win->format->Gmask; + *b = sw.win->format->Bmask; + *stride = sw.win->pitch / sw.win->format->BytesPerPixel; + + return true; +} + +void *SW_LockBuffer( void ) +{ + sw.win = SDL_GetVideoSurface(); + + // prevent buffer overrun + if( !sw.win || sw.win->w < sw.width || sw.win->h < sw.height ) + return NULL; + + // real window pixels (x11 shm region, dma buffer, etc) + // or SDL_Renderer texture if not supported + SDL_LockSurface( sw.win ); + return sw.win->pixels; +} + +void SW_UnlockBuffer( void ) +{ + // already blitted + SDL_UnlockSurface( sw.win ); + + SDL_Flip( host.hWnd ); +} + +int R_MaxVideoModes( void ) +{ + return num_vidmodes; +} + +vidmode_t *R_GetVideoMode( int num ) +{ + if( !vidmodes || num < 0 || num >= R_MaxVideoModes() ) + { + return NULL; + } + + return vidmodes + num; +} + +static void R_InitVideoModes( void ) +{ + char buf[MAX_VA_STRING]; + SDL_Rect **modes; + int len = 0, i = 0, j; + + modes = SDL_ListModes( NULL, SDL_FULLSCREEN ); + + if( !modes || modes == (void*)-1 ) + return; + + for( len = 0; modes[len]; len++ ); + + vidmodes = Mem_Malloc( host.mempool, len * sizeof( vidmode_t ) ); + + // from smallest to largest + for( ; i < len; i++ ) + { + SDL_Rect *mode = modes[len - i - 1]; + + for( j = 0; j < num_vidmodes; j++ ) + { + if( mode->w == vidmodes[j].width && + mode->h == vidmodes[j].height ) + { + break; + } + } + if( j != num_vidmodes ) + continue; + + vidmodes[num_vidmodes].width = mode->w; + vidmodes[num_vidmodes].height = mode->h; + Q_snprintf( buf, sizeof( buf ), "%ix%i", mode->w, mode->h ); + vidmodes[num_vidmodes].desc = copystring( buf ); + + num_vidmodes++; + } +} + +static void R_FreeVideoModes( void ) +{ + int i; + + if( !vidmodes ) + return; + + for( i = 0; i < num_vidmodes; i++ ) + Mem_Free( (char*)vidmodes[i].desc ); + Mem_Free( vidmodes ); + + vidmodes = NULL; +} + +/* +================= +GL_GetProcAddress +================= +*/ +void *GL_GetProcAddress( const char *name ) +{ + void *func = SDL_GL_GetProcAddress( name ); + + if( !func ) + Con_Reportf( S_ERROR "%s failed for %s\n", __func__, name ); + + return func; +} + +/* +=============== +GL_UpdateSwapInterval +=============== +*/ +void GL_UpdateSwapInterval( void ) +{ +} + +/* +================= +GL_DeleteContext + +always return false +================= +*/ +qboolean GL_DeleteContext( void ) +{ + return false; +} + +/* +================= +GL_CreateContext +================= +*/ +static qboolean GL_CreateContext( void ) +{ + return true; +} + +/* +================= +GL_UpdateContext +================= +*/ +static qboolean GL_UpdateContext( void ) +{ + return true; +} + +void VID_SaveWindowSize( int width, int height, qboolean maximized ) +{ + int render_w = width, render_h = height; + + VID_SetDisplayTransform( &render_w, &render_h ); + R_SaveVideoMode( width, height, render_w, render_h, maximized ); +} + +static qboolean VID_SetScreenResolution( int width, int height, window_mode_t window_mode ) +{ + VID_SaveWindowSize( width, height, true ); + return true; +} + +void VID_RestoreScreenResolution( void ) +{ +} + +static qboolean VID_CreateWindowWithSafeGL( const char *wndname, int xpos, int ypos, int w, int h, uint32_t flags ) +{ + while( glw_state.safe >= SAFE_NO && glw_state.safe < SAFE_LAST ) + { + host.hWnd = sw.surf = SDL_SetVideoMode( w, h, 16, flags ); + // we have window, exit loop + if( host.hWnd ) + break; + + Con_Reportf( S_ERROR "%s: couldn't create '%s' with safegl level %d: %s\n", __func__, wndname, glw_state.safe, SDL_GetError()); + + glw_state.safe++; + + if( !gl_msaa_samples.value && glw_state.safe == SAFE_NOMSAA ) + glw_state.safe++; // no need to skip msaa, if we already disabled it + + GL_SetupAttributes(); // re-choose attributes + + // try again create window + } + + // window creation has failed... + if( glw_state.safe >= SAFE_LAST ) + return false; + + return true; +} + +static qboolean RectFitsInDisplay( const SDL_Rect *rect, const SDL_Rect *display ) +{ + return rect->x >= display->x + && rect->y >= display->y + && rect->x + rect->w <= display->x + display->w + && rect->y + rect->h <= display->y + display->h; +} +// Function to check if the rectangle fits in any display +static qboolean RectFitsInAnyDisplay( const SDL_Rect *rect, const SDL_Rect *display_rects, int num_displays ) +{ + for( int i = 0; i < num_displays; i++ ) + { + if( RectFitsInDisplay( rect, &display_rects[i] )) + return true; // Rectangle fits in this display + } + return false; // Rectangle does not fit in any display +} + +/* +================= +VID_CreateWindow +================= +*/ +qboolean VID_CreateWindow( int width, int height, window_mode_t window_mode ) +{ + string wndname; + Uint32 flags = 0; + + Q_strncpy( wndname, GI->title, sizeof( wndname )); + + if( window_mode != WINDOW_MODE_WINDOWED ) + SetBits( flags, SDL_FULLSCREEN|SDL_HWSURFACE ); + + if( !glw_state.software ) + SetBits( flags, SDL_OPENGL ); + + if( !VID_CreateWindowWithSafeGL( wndname, 0, 0, width, height, flags )) + return false; + + VID_SaveWindowSize( width, height, false ); + + return true; +} + +/* +================= +VID_DestroyWindow +================= +*/ +void VID_DestroyWindow( void ) +{ + GL_DeleteContext(); + + VID_RestoreScreenResolution(); + if( host.hWnd ) + host.hWnd = NULL; + + if( refState.fullScreen ) + refState.fullScreen = false; +} + +/* +================== +GL_SetupAttributes +================== +*/ +static void GL_SetupAttributes( void ) +{ + ref.dllFuncs.GL_SetupAttributes( glw_state.safe ); +} + +void GL_SwapBuffers( void ) +{ + SDL_Flip( host.hWnd ); +} + +int GL_SetAttribute( int attr, int val ) +{ + switch( attr ) + { +#define MAP_REF_API_ATTRIBUTE_TO_SDL( name ) case REF_##name: return SDL_GL_SetAttribute( SDL_##name, val ); + MAP_REF_API_ATTRIBUTE_TO_SDL( GL_RED_SIZE ); + MAP_REF_API_ATTRIBUTE_TO_SDL( GL_GREEN_SIZE ); + MAP_REF_API_ATTRIBUTE_TO_SDL( GL_BLUE_SIZE ); + MAP_REF_API_ATTRIBUTE_TO_SDL( GL_ALPHA_SIZE ); + MAP_REF_API_ATTRIBUTE_TO_SDL( GL_DOUBLEBUFFER ); + MAP_REF_API_ATTRIBUTE_TO_SDL( GL_DEPTH_SIZE ); + MAP_REF_API_ATTRIBUTE_TO_SDL( GL_STENCIL_SIZE ); + MAP_REF_API_ATTRIBUTE_TO_SDL( GL_MULTISAMPLEBUFFERS ); + MAP_REF_API_ATTRIBUTE_TO_SDL( GL_MULTISAMPLESAMPLES ); + MAP_REF_API_ATTRIBUTE_TO_SDL( GL_ACCELERATED_VISUAL ); +#undef MAP_REF_API_ATTRIBUTE_TO_SDL + } + + return -1; +} + +int GL_GetAttribute( int attr, int *val ) +{ + switch( attr ) + { +#define MAP_REF_API_ATTRIBUTE_TO_SDL( name ) case REF_##name: return SDL_GL_GetAttribute( SDL_##name, val ); + MAP_REF_API_ATTRIBUTE_TO_SDL( GL_RED_SIZE ); + MAP_REF_API_ATTRIBUTE_TO_SDL( GL_GREEN_SIZE ); + MAP_REF_API_ATTRIBUTE_TO_SDL( GL_BLUE_SIZE ); + MAP_REF_API_ATTRIBUTE_TO_SDL( GL_ALPHA_SIZE ); + MAP_REF_API_ATTRIBUTE_TO_SDL( GL_DOUBLEBUFFER ); + MAP_REF_API_ATTRIBUTE_TO_SDL( GL_DEPTH_SIZE ); + MAP_REF_API_ATTRIBUTE_TO_SDL( GL_STENCIL_SIZE ); + MAP_REF_API_ATTRIBUTE_TO_SDL( GL_MULTISAMPLEBUFFERS ); + MAP_REF_API_ATTRIBUTE_TO_SDL( GL_MULTISAMPLESAMPLES ); + MAP_REF_API_ATTRIBUTE_TO_SDL( GL_ACCELERATED_VISUAL ); +#undef MAP_REF_API_ATTRIBUTE_TO_SDL + } + + return 0; +} + +/* +================== +R_Init_Video +================== +*/ +qboolean R_Init_Video( const int type ) +{ + string safe; + qboolean retval; + + refState.desktopBitsPixel = 16; + + switch( type ) + { + case REF_SOFTWARE: + glw_state.software = true; + break; + case REF_GL: + if( !glw_state.safe && Sys_GetParmFromCmdLine( "-safegl", safe ) ) + glw_state.safe = bound( SAFE_NO, Q_atoi( safe ), SAFE_DONTCARE ); + + // refdll can request some attributes + GL_SetupAttributes( ); + + if( SDL_GL_LoadLibrary( NULL ) < 0 ) + { + Con_Reportf( S_ERROR "Couldn't initialize OpenGL: %s\n", SDL_GetError()); + return false; + } + break; + default: + Host_Error( "Can't initialize unknown context type %d!\n", type ); + break; + } + + if( !(retval = VID_SetMode()) ) + { + return retval; + } + + switch( type ) + { + case REF_GL: + // refdll also can check extensions + ref.dllFuncs.GL_InitExtensions(); + break; + case REF_SOFTWARE: + default: + break; + } + + R_InitVideoModes(); + + host.renderinfo_changed = false; + + return true; +} + +rserr_t R_ChangeDisplaySettings( int width, int height, window_mode_t window_mode ) +{ + refState.fullScreen = window_mode != WINDOW_MODE_WINDOWED; + Con_Reportf( "%s: Setting video mode to %dx%d %s\n", __func__, width, height, refState.fullScreen ? "fullscreen" : "windowed" ); + + if( !host.hWnd ) + { + if( !VID_CreateWindow( width, height, window_mode )) + return rserr_invalid_mode; + } + else if( refState.fullScreen ) + { + if( !VID_SetScreenResolution( width, height, window_mode )) + return rserr_invalid_fullscreen; + } + else + { + VID_RestoreScreenResolution(); + VID_SaveWindowSize( width, height, true ); + } + + return rserr_ok; +} + +/* +================== +VID_SetMode + +Set the described video mode +================== +*/ +qboolean VID_SetMode( void ) +{ + int iScreenWidth, iScreenHeight; + rserr_t err; + window_mode_t window_mode; + + iScreenWidth = Cvar_VariableInteger( "width" ); + iScreenHeight = Cvar_VariableInteger( "height" ); + + if( iScreenWidth < VID_MIN_WIDTH || + iScreenHeight < VID_MIN_HEIGHT ) // trying to get resolution automatically by default + { + iScreenWidth = 320; + iScreenHeight = 240; + } + + window_mode = bound( 0, vid_fullscreen.value, WINDOW_MODE_COUNT - 1 ); + SetBits( gl_vsync.flags, FCVAR_CHANGED ); + + if(( err = R_ChangeDisplaySettings( iScreenWidth, iScreenHeight, window_mode )) == rserr_ok ) + { + sdlState.prev_width = iScreenWidth; + sdlState.prev_height = iScreenHeight; + } + else + { + if( err == rserr_invalid_fullscreen ) + { + Cvar_DirectSet( &vid_fullscreen, "0" ); + Con_Reportf( S_ERROR "%s: fullscreen unavailable in this mode\n", __func__ ); + Sys_Warn( "fullscreen unavailable in this mode!" ); + if(( err = R_ChangeDisplaySettings( iScreenWidth, iScreenHeight, WINDOW_MODE_WINDOWED )) == rserr_ok ) + return true; + } + else if( err == rserr_invalid_mode ) + { + Con_Reportf( S_ERROR "%s: invalid mode\n", __func__ ); + Sys_Warn( "invalid mode, engine will run in %dx%d", sdlState.prev_width, sdlState.prev_height ); + } + + // try setting it back to something safe + if(( err = R_ChangeDisplaySettings( sdlState.prev_width, sdlState.prev_height, WINDOW_MODE_WINDOWED )) != rserr_ok ) + { + Con_Reportf( S_ERROR "%s: could not revert to safe mode\n", __func__ ); + Sys_Warn( "could not revert to safe mode!" ); + return false; + } + } + + return true; +} + +/* +================== +R_Free_Video +================== +*/ +void R_Free_Video( void ) +{ + GL_DeleteContext (); + + VID_DestroyWindow (); + + R_FreeVideoModes(); + + ref.dllFuncs.GL_ClearExtensions(); +}