This commit is contained in:
Simon 2021-07-27 21:02:47 +01:00
commit b5b44d5f3f
69 changed files with 10405 additions and 702 deletions

2
.gitignore vendored
View file

@ -12,7 +12,7 @@ $RECYCLE.BIN/
*.lnk
# build files
*.o
# *.o
*.d
#binaries

View file

@ -158,6 +158,35 @@ if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
set( PROFILE 0 CACHE BOOL "Enable profiling with gprof for Debug and RelWithDebInfo build types." )
endif()
# Fast math flags, required by some subprojects
set( ZD_FASTMATH_FLAG "" )
if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
set( ZD_FASTMATH_FLAG "-ffast-math -ffp-contract=fast" )
elseif( MSVC )
set( ZD_FASTMATH_FLAG "/fp:fast" )
endif()
macro( use_fast_math )
set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ZD_FASTMATH_FLAG}" )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ZD_FASTMATH_FLAG}" )
endmacro()
include( CheckFunctionExists )
macro( require_stricmp )
CHECK_FUNCTION_EXISTS( stricmp STRICMP_EXISTS )
if( NOT STRICMP_EXISTS )
add_definitions( -Dstricmp=strcasecmp )
endif()
endmacro()
macro( require_strnicmp )
CHECK_FUNCTION_EXISTS( strnicmp STRNICMP_EXISTS )
if( NOT STRNICMP_EXISTS )
add_definitions( -Dstrnicmp=strncasecmp )
endif()
endmacro()
option( NO_OPENAL "Disable OpenAL sound support" OFF )
find_package( BZip2 )
@ -219,9 +248,9 @@ if( MSVC )
# Avoid CRT DLL dependancies in release builds, optionally generate assembly output for checking crash locations.
option( ZDOOM_GENERATE_ASM "Generate assembly output." OFF )
if( ZDOOM_GENERATE_ASM )
set( REL_C_FLAGS "/MT /Oy /Oi /FAcs" )
set( REL_C_FLAGS "/MT /Oy /Oi /FAcs /GS-" )
else()
set( REL_C_FLAGS "/MT /Oy /Oi" )
set( REL_C_FLAGS "/MT /Oy /Oi /GS-" )
endif()
@ -305,21 +334,13 @@ mark_as_advanced( FORCE_INTERNAL_GME )
option(FORCE_INTERNAL_ASMJIT "Use internal asmjit" ON)
mark_as_advanced( FORCE_INTERNAL_ASMJIT )
# Fast math flags, required by some subprojects
set( ZD_FASTMATH_FLAG "" )
if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
set( ZD_FASTMATH_FLAG "-ffast-math -ffp-contract=fast" )
elseif( MSVC )
set( ZD_FASTMATH_FLAG "/fp:fast" )
endif()
if( ZLIB_FOUND AND NOT FORCE_INTERNAL_ZLIB )
message( STATUS "Using system zlib, includes found at ${ZLIB_INCLUDE_DIR}" )
else()
message( STATUS "Using internal zlib" )
set( SKIP_INSTALL_ALL TRUE ) # Avoid installing zlib alongside zdoom
add_subdirectory( zlib )
set( ZLIB_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/zlib )
add_subdirectory( libraries/zlib )
set( ZLIB_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib )
set( ZLIB_LIBRARIES z )
set( ZLIB_LIBRARY z )
endif()
@ -343,8 +364,8 @@ if( ${HAVE_VM_JIT} )
else()
message( STATUS "Using internal asmjit" )
set( SKIP_INSTALL_ALL TRUE ) # Avoid installing asmjit alongside zdoom
add_subdirectory( asmjit )
set( ASMJIT_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/asmjit )
add_subdirectory( libraries/asmjit )
set( ASMJIT_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libraries/asmjit )
set( ASMJIT_LIBRARIES asmjit )
set( ASMJIT_LIBRARY asmjit )
endif()
@ -354,8 +375,8 @@ if( JPEG_FOUND AND NOT FORCE_INTERNAL_JPEG )
message( STATUS "Using system jpeg library, includes found at ${JPEG_INCLUDE_DIR}" )
else()
message( STATUS "Using internal jpeg library" )
add_subdirectory( jpeg )
set( JPEG_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/jpeg )
add_subdirectory( libraries/jpeg )
set( JPEG_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libraries/jpeg )
set( JPEG_LIBRARIES jpeg )
set( JPEG_LIBRARY jpeg )
endif()
@ -364,8 +385,8 @@ if( BZIP2_FOUND AND NOT FORCE_INTERNAL_BZIP2 )
message( STATUS "Using system bzip2 library, includes found at ${BZIP2_INCLUDE_DIR}" )
else()
message( STATUS "Using internal bzip2 library" )
add_subdirectory( bzip2 )
set( BZIP2_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/bzip2" )
add_subdirectory( libraries/bzip2 )
set( BZIP2_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/bzip2" )
set( BZIP2_LIBRARIES bz2 )
set( BZIP2_LIBRARY bz2 )
endif()
@ -377,12 +398,19 @@ else()
# Use MAME as it's balanced emulator: well-accurate, but doesn't eats lot of CPU
# Nuked OPN2 is very accurate emulator, but it eats too much CPU for the workflow
set( GME_YM2612_EMU "MAME" )
add_subdirectory( game-music-emu )
set( GME_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/game-music-emu" )
add_subdirectory( libraries/game-music-emu )
set( GME_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/game-music-emu" )
set( GME_LIBRARIES gme )
endif()
set( LZMA_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/lzma/C" )
set( LZMA_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/lzma/C" )
set( ADL_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/adlmidi" )
set( OPN_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/opnmidi" )
set( TIMIDITYPP_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/timidityplus" )
set( TIMIDITY_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/timidity" )
set( WILDMIDI_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/wildmidi" )
set( OPLSYNTH_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/oplsynth" )
set( ZMUSIC_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zmusic" )
if( NOT CMAKE_CROSSCOMPILING )
if( NOT CROSS_EXPORTS )
@ -400,10 +428,22 @@ install(DIRECTORY docs/
DESTINATION ${INSTALL_DOCS_PATH}
COMPONENT "Documentation")
add_subdirectory( lzma )
option( DYN_FLUIDSYNTH "Dynamically load fluidsynth" ON )
option( DYN_OPENAL "Dynamically load OpenAL" ON )
option( DYN_SNDFILE "Dynamically load libsndfile" ON )
option( DYN_MPG123 "Dynamically load libmpg123" ON )
add_subdirectory( libraries/lzma )
add_subdirectory( tools )
add_subdirectory( dumb )
add_subdirectory( gdtoa )
add_subdirectory( libraries/dumb )
add_subdirectory( libraries/gdtoa )
add_subdirectory( libraries/adlmidi )
add_subdirectory( libraries/opnmidi )
add_subdirectory( libraries/timidity )
add_subdirectory( libraries/timidityplus )
add_subdirectory( libraries/wildmidi )
add_subdirectory( libraries/oplsynth )
add_subdirectory( libraries/zmusic )
add_subdirectory( wadsrc )
add_subdirectory( wadsrc_bm )
add_subdirectory( wadsrc_lights )

View file

@ -32,7 +32,7 @@ LOCAL_SRC_FILES = \
C/LzFind.c \
C/Lzma2Dec.c \
C/LzmaDec.c \
C/LzmaEnc.c \
C/LzmaEnc.c \
include $(BUILD_STATIC_LIBRARY)

View file

@ -5,7 +5,7 @@ LOCAL_PATH := $(call my-dir)/../libraries/zlib
include $(CLEAR_VARS)
LOCAL_MODULE := zlib_lz
LOCAL_MODULE := zlib_lz
LOCAL_CFLAGS = -Wall

View file

@ -23,11 +23,6 @@ if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
endif()
endif()
option( DYN_FLUIDSYNTH "Dynamically load fluidsynth" ON )
option( DYN_OPENAL "Dynamically load OpenAL" ON )
option( DYN_SNDFILE "Dynamically load libsndfile" ON )
option( DYN_MPG123 "Dynamically load libmpg123" ON )
if( APPLE )
option( OSX_COCOA_BACKEND "Use native Cocoa backend instead of SDL" ON )
endif()
@ -221,27 +216,8 @@ endif()
if( NO_OPENAL )
add_definitions( -DNO_OPENAL=1 )
set(MPG123_FOUND NO)
set(SNDFILE_FOUND NO)
else()
# Search for libSndFile
if ( NOT DYN_SNDFILE )
find_package( SndFile )
endif()
# Search for libmpg123
if ( NOT DYN_MPG123 )
find_package( MPG123 )
endif()
endif()
# Search for FluidSynth
find_package( FluidSynth )
# Decide on SSE setup
set( SSE_MATTERS NO )
@ -448,15 +424,8 @@ if( NOT STRUPR_EXISTS )
add_definitions( -DNEED_STRUPR=1 )
endif()
CHECK_FUNCTION_EXISTS( stricmp STRICMP_EXISTS )
if( NOT STRICMP_EXISTS )
add_definitions( -Dstricmp=strcasecmp )
endif()
CHECK_FUNCTION_EXISTS( strnicmp STRNICMP_EXISTS )
if( NOT STRNICMP_EXISTS )
add_definitions( -Dstrnicmp=strncasecmp )
endif()
require_stricmp()
require_strnicmp()
if( NOT MSVC )
add_definitions( -D__forceinline=inline )
@ -480,15 +449,14 @@ endif()
# Update gitinfo.h
add_custom_target( revision_check ALL
COMMAND updaterevision src/gitinfo.h
COMMAND "${CMAKE_COMMAND}" -P "${CMAKE_SOURCE_DIR}/tools/updaterevision/UpdateRevision.cmake" src/gitinfo.h
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
DEPENDS updaterevision )
)
# Libraries ZDoom needs
message( STATUS "Fluid synth libs: ${FLUIDSYNTH_LIBRARIES}" )
set( ZDOOM_LIBS ${ZDOOM_LIBS} "${ZLIB_LIBRARIES}" "${JPEG_LIBRARIES}" "${BZIP2_LIBRARIES}" "${GME_LIBRARIES}" "${CMAKE_DL_LIBS}" )
include_directories( "${ZLIB_INCLUDE_DIR}" "${BZIP2_INCLUDE_DIR}" "${LZMA_INCLUDE_DIR}" "${JPEG_INCLUDE_DIR}" "${GME_INCLUDE_DIR}" )
include_directories( "${ZLIB_INCLUDE_DIR}" "${BZIP2_INCLUDE_DIR}" "${LZMA_INCLUDE_DIR}" "${JPEG_INCLUDE_DIR}" "${ZMUSIC_INCLUDE_DIR}" )
if( ${HAVE_VM_JIT} )
add_definitions( -DHAVE_VM_JIT )
@ -496,30 +464,12 @@ if( ${HAVE_VM_JIT} )
set( ZDOOM_LIBS ${ZDOOM_LIBS} "${ASMJIT_LIBRARIES}")
endif()
if( SNDFILE_FOUND )
set( ZDOOM_LIBS ${ZDOOM_LIBS} "${SNDFILE_LIBRARIES}" )
include_directories( "${SNDFILE_INCLUDE_DIRS}" )
endif()
if( MPG123_FOUND )
set( ZDOOM_LIBS ${ZDOOM_LIBS} "${MPG123_LIBRARIES}" )
include_directories( "${MPG123_INCLUDE_DIR}" )
endif()
if( NOT DYN_FLUIDSYNTH )
if( FLUIDSYNTH_FOUND )
set( ZDOOM_LIBS ${ZDOOM_LIBS} "${FLUIDSYNTH_LIBRARIES}" )
include_directories( "${FLUIDSYNTH_INCLUDE_DIR}" )
endif()
endif()
# Start defining source files for ZDoom
set( PLAT_WIN32_SOURCES
sound/mididevices/music_win_mididevice.cpp
win32/fb_d3d9.cpp
win32/fb_d3d9_wipe.cpp
win32/fb_ddraw.cpp
win32/hardware.cpp
win32/helperthread.cpp
win32/i_cd.cpp
win32/i_crash.cpp
win32/i_input.cpp
win32/i_keyboard.cpp
@ -531,11 +481,12 @@ set( PLAT_WIN32_SOURCES
win32/i_system.cpp
win32/i_specialpaths.cpp
win32/st_start.cpp
win32/st_start_util.cpp
win32/win32gliface.cpp
win32/win32video.cpp )
set( PLAT_POSIX_SOURCES
posix/i_cd.cpp
posix/i_steam.cpp )
posix/i_steam.cpp
posix/i_system_posix.cpp )
set( PLAT_SDL_SOURCES
posix/sdl/crashcatcher.c
posix/sdl/hardware.cpp
@ -558,7 +509,6 @@ set( PLAT_COCOA_SOURCES
posix/cocoa/i_input.mm
posix/cocoa/i_joystick.cpp
posix/cocoa/i_main.mm
posix/cocoa/i_main_except.cpp
posix/cocoa/i_system.mm
posix/cocoa/i_video.mm
posix/cocoa/st_console.mm
@ -642,37 +592,12 @@ add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/sc_man_scanner.h
include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
if( DYN_SNDFILE)
add_definitions( -DHAVE_SNDFILE -DDYN_SNDFILE )
elseif( SNDFILE_FOUND )
add_definitions( -DHAVE_SNDFILE )
endif()
if( DYN_MPG123)
add_definitions( -DHAVE_MPG123 -DDYN_MPG123 )
elseif( MPG123_FOUND )
add_definitions( -DHAVE_MPG123 )
endif()
if( DYN_FLUIDSYNTH )
add_definitions( -DHAVE_FLUIDSYNTH -DDYN_FLUIDSYNTH )
elseif( FLUIDSYNTH_FOUND )
add_definitions( -DHAVE_FLUIDSYNTH )
endif()
option( SEND_ANON_STATS "Enable sending of anonymous hardware statistics" ON )
if( NOT SEND_ANON_STATS )
add_definitions( -DNO_SEND_STATS )
endif()
# Disable ADLMIDI's and OPNMIDI's MIDI Sequencer
add_definitions(-DADLMIDI_DISABLE_MIDI_SEQUENCER)
add_definitions(-DOPNMIDI_DISABLE_MIDI_SEQUENCER)
# Disable OPNMIDI's experimental yet emulator (using of it has some issues and missing notes in playback)
add_definitions(-DOPNMIDI_DISABLE_GX_EMULATOR)
# Project files should be aware of the header files. We can GLOB these since
# there's generally a new cpp for every header so this file will get changed
file( GLOB HEADER_FILES
@ -682,10 +607,9 @@ file( GLOB HEADER_FILES
g_inventory/*.h
intermission/*.h
menu/*.h
sound/adlmidi/*.h*
sound/opnmidi/*.h*
sound/oplsynth/*.h
sound/oplsynth/dosbox/*.h
sound/*.h
sound/backend/*.h*
sound/music/*.h*
posix/*.h
posix/cocoa/*.h
posix/sdl/*.h
@ -695,7 +619,6 @@ file( GLOB HEADER_FILES
rapidjson/*.h
resourcefiles/*.h
sfmt/*.h
sound/*.h
textures/*.h
scripting/*.h
scripting/backend/*.h
@ -703,12 +626,7 @@ file( GLOB HEADER_FILES
scripting/zscript/*.h
scripting/vm/*.h
sound/midisources/*.h
sound/oplsynth/*.h
sound/oplsynth/dosbox/*.h
sound/thirdparty/*.h
sound/timidity/*.h
sound/timiditypp/*.h
sound/wildmidi/*.h
xlat/*.h
swrenderer/*.h
swrenderer/drawers/*.h
@ -848,13 +766,8 @@ set( VM_JIT_SOURCES
set( FASTMATH_SOURCES
swrenderer/r_all.cpp
polyrenderer/poly_all.cpp
sound/oplsynth/opl_mus_player.cpp
sound/mpg123_decoder.cpp
sound/music_midi_base.cpp
sound/oalsound.cpp
sound/sndfile_decoder.cpp
sound/timiditypp/fft4g.cpp
sound/timiditypp/reverb.cpp
sound/music/music_midi_base.cpp
sound/backend/oalsound.cpp
gl/utility/gl_clock.cpp
gl/renderer/gl_2ddrawer.cpp
gl/hqnx/init.cpp
@ -885,32 +798,6 @@ set( FASTMATH_SOURCES
gl/models/gl_models.cpp
r_data/models/models.cpp
r_data/matrix.cpp
sound/adlmidi/adldata.cpp
sound/adlmidi/adlmidi.cpp
sound/adlmidi/adlmidi_load.cpp
sound/adlmidi/adlmidi_midiplay.cpp
sound/adlmidi/adlmidi_opl3.cpp
sound/adlmidi/adlmidi_private.cpp
sound/adlmidi/chips/dosbox/dbopl.cpp
sound/adlmidi/chips/dosbox_opl3.cpp
sound/adlmidi/chips/nuked/nukedopl3_174.c
sound/adlmidi/chips/nuked/nukedopl3.c
sound/adlmidi/chips/nuked_opl3.cpp
sound/adlmidi/chips/nuked_opl3_v174.cpp
sound/adlmidi/wopl/wopl_file.c
sound/opnmidi/chips/gens_opn2.cpp
sound/opnmidi/chips/gens/Ym2612_Emu.cpp
sound/opnmidi/chips/mame/mame_ym2612fm.c
sound/opnmidi/chips/mame_opn2.cpp
sound/opnmidi/chips/nuked_opn2.cpp
sound/opnmidi/chips/nuked/ym3438.c
sound/opnmidi/opnmidi.cpp
sound/opnmidi/opnmidi_load.cpp
sound/opnmidi/opnmidi_midiplay.cpp
sound/opnmidi/opnmidi_opn2.cpp
sound/opnmidi/opnmidi_private.cpp
sound/opnmidi/wopn/wopn_file.c
)
set (PCH_SOURCES
@ -964,7 +851,6 @@ set (PCH_SOURCES
gi.cpp
gitinfo.cpp
hu_scores.cpp
i_module.cpp
i_net.cpp
i_time.cpp
info.cpp
@ -1037,12 +923,14 @@ set (PCH_SOURCES
r_utility.cpp
r_sky.cpp
r_videoscale.cpp
s_advsound.cpp
s_environment.cpp
s_playlist.cpp
s_sndseq.cpp
s_sound.cpp
sound/s_advsound.cpp
sound/s_environment.cpp
sound/s_reverbedit.cpp
sound/s_sndseq.cpp
sound/s_doomsound.cpp
sound/s_sound.cpp
sound/s_music.cpp
s_playlist.cpp
serializer.cpp
sc_man.cpp
scriptutil.cpp
@ -1223,73 +1111,11 @@ set (PCH_SOURCES
scripting/zscript/zcc_compile.cpp
scripting/zscript/zcc_parser.cpp
sfmt/SFMT.cpp
sound/i_music.cpp
sound/i_sound.cpp
sound/i_soundfont.cpp
sound/mididevices/music_adlmidi_mididevice.cpp
sound/mididevices/music_opldumper_mididevice.cpp
sound/mididevices/music_opl_mididevice.cpp
sound/mididevices/music_opnmidi_mididevice.cpp
sound/mididevices/music_timiditypp_mididevice.cpp
sound/mididevices/music_fluidsynth_mididevice.cpp
sound/mididevices/music_softsynth_mididevice.cpp
sound/mididevices/music_timidity_mididevice.cpp
sound/mididevices/music_wildmidi_mididevice.cpp
sound/mididevices/music_wavewriter_mididevice.cpp
sound/midisources/midisource.cpp
sound/midisources/midisource_mus.cpp
sound/midisources/midisource_smf.cpp
sound/midisources/midisource_hmi.cpp
sound/midisources/midisource_xmi.cpp
sound/musicformats/music_cd.cpp
sound/musicformats/music_dumb.cpp
sound/musicformats/music_gme.cpp
sound/musicformats/music_libsndfile.cpp
sound/musicformats/music_midistream.cpp
sound/musicformats/music_opl.cpp
sound/musicformats/music_stream.cpp
sound/musicformats/music_xa.cpp
sound/oplsynth/fmopl.cpp
sound/oplsynth/musicblock.cpp
sound/oplsynth/oplio.cpp
sound/oplsynth/dosbox/opl.cpp
sound/oplsynth/OPL3.cpp
sound/oplsynth/nukedopl3.cpp
sound/timidity/common.cpp
sound/timidity/instrum.cpp
sound/timidity/instrum_dls.cpp
sound/timidity/instrum_font.cpp
sound/timidity/instrum_sf2.cpp
sound/timidity/mix.cpp
sound/timidity/playmidi.cpp
sound/timidity/resample.cpp
sound/timidity/timidity.cpp
sound/timiditypp/common.cpp
sound/timiditypp/configfile.cpp
sound/timiditypp/effect.cpp
sound/timiditypp/filter.cpp
sound/timiditypp/freq.cpp
sound/timiditypp/instrum.cpp
sound/timiditypp/mblock.cpp
sound/timiditypp/mix.cpp
sound/timiditypp/playmidi.cpp
sound/timiditypp/quantity.cpp
sound/timiditypp/readmidic.cpp
sound/timiditypp/recache.cpp
sound/timiditypp/resample.cpp
sound/timiditypp/sbkconv.cpp
sound/timiditypp/sffile.cpp
sound/timiditypp/sfitem.cpp
sound/timiditypp/smplfile.cpp
sound/timiditypp/sndfont.cpp
sound/timiditypp/tables.cpp
sound/wildmidi/file_io.cpp
sound/wildmidi/gus_pat.cpp
sound/wildmidi/reverb.cpp
sound/wildmidi/wildmidi_lib.cpp
sound/wildmidi/wm_error.cpp
sound/music/i_music.cpp
sound/music/i_soundfont.cpp
sound/backend/i_sound.cpp
sound/music/music_config.cpp
events.cpp
atterm.cpp
GuillotineBinPack.cpp
SkylineBinPack.cpp
)
@ -1312,6 +1138,7 @@ add_executable( zdoom WIN32 MACOSX_BUNDLE
x86.cpp
strnatcmp.c
zstring.cpp
dictionary.cpp
math/asin.c
math/atan.c
math/const.c
@ -1354,23 +1181,21 @@ if( UNIX )
endif()
endif()
target_link_libraries( zdoom ${ZDOOM_LIBS} gdtoa dumb lzma )
target_link_libraries( zdoom ${ZDOOM_LIBS} gdtoa dumb lzma adl opn timidity timidityplus wildmidi oplsynth zmusic )
include_directories( .
g_statusbar
g_shared
g_inventory
sound
sound/music
sound/backend
textures
sound/oplsynth
sound/timidity
sound/wildmidi
xlat
scripting
scripting/vm
../gdtoa
../dumb/include
${CMAKE_BINARY_DIR}/gdtoa
../libraries/gdtoa
${CMAKE_BINARY_DIR}/libraries/gdtoa
${SYSTEM_SOURCES_DIR} )
add_dependencies( zdoom revision_check )
@ -1448,7 +1273,8 @@ endif()
if( APPLE )
set_target_properties(zdoom PROPERTIES
LINK_FLAGS "-framework Cocoa -framework IOKit -framework OpenGL"
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/posix/osx/zdoom-info.plist" )
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/posix/osx/zdoom-info.plist"
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "" )
endif()
@ -1462,15 +1288,7 @@ install(TARGETS zdoom
COMPONENT "Game executable")
source_group("Audio Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sound/.+")
source_group("Audio Files\\ADL MIDI" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sound/adlmidi/.+")
source_group("Audio Files\\OPN MIDI" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sound/opnmidi/.+")
source_group("Audio Files\\OPL Synth" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sound/oplsynth/.+")
source_group("Audio Files\\OPL Synth\\DOSBox" FILES sound/oplsynth/dosbox/opl.cpp sound/oplsynth/dosbox/opl.h)
source_group("Audio Files\\Timidity" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sound/timidity/.+")
source_group("Audio Files\\Timiditypp" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sound/timiditypp/.+")
source_group("Audio Files\\WildMidi" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sound/wildmidi/.+")
source_group("Audio Files\\MIDI Devices" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sound/mididevices/.+")
source_group("Audio Files\\MIDI Sources" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sound/midisources/.+")
source_group("Audio Files\\Backend" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sound/backend/.+")
source_group("Audio Files\\Music formats" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sound/musicformats/.+")
source_group("Audio Files\\Third-party" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sound/thirdparty/.+")
source_group("External\\Math" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/math/.+")

View file

@ -25,6 +25,8 @@
**
**/
#include "version.h"
#include "gl/system/gl_system.h"
#include "gl/shaders/gl_shader.h"
#include "gl/dynlights/gl_lightbuffer.h"
@ -32,13 +34,21 @@
#include "gl/system/gl_interface.h"
#include "gl/utility//gl_clock.h"
static const int INITIAL_BUFFER_SIZE = 320000; // This means 80000 lights per frame and 320000*16 bytes == 5.12 MB.
static const int ELEMENTS_PER_LIGHT = 4; // each light needs 4 vec4's.
static const int ELEMENT_SIZE = (4*sizeof(float));
CUSTOM_CVAR (Int, gl_light_buffer_type, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
{
Printf("You must restart " GAMENAME " to switch the light buffer\n");
}
FLightBuffer::FLightBuffer()
{
mBufferSize = INITIAL_BUFFER_SIZE;
mByteSize = mBufferSize * sizeof(float);
int maxNumberOfLights = 80000;
mBufferSize = maxNumberOfLights * ELEMENTS_PER_LIGHT;
mByteSize = mBufferSize * ELEMENT_SIZE;
// Hack alert: On Intel's GL driver SSBO's perform quite worse than UBOs.
// We only want to disable using SSBOs for lights but not disable the feature entirely.
// Note that using an uniform buffer here will limit the number of lights per surface so it isn't done for NVidia and AMD.
@ -47,14 +57,15 @@ FLightBuffer::FLightBuffer()
mBufferType = GL_SHADER_STORAGE_BUFFER;
mBlockAlign = 0;
mBlockSize = mBufferSize;
mMaxUploadSize = mBlockSize;
}
else
{
mBufferType = GL_UNIFORM_BUFFER;
mBlockSize = gl.maxuniformblock / 16;
if (mBlockSize > 2048) mBlockSize = 2048; // we don't really need a larger buffer
mBlockAlign = mBlockSize / 2;
mBlockSize = gl.maxuniformblock / ELEMENT_SIZE;
mBlockAlign = gl.uniformblockalignment / ELEMENT_SIZE;
mMaxUploadSize = (mBlockSize - mBlockAlign);
mByteSize += gl.maxuniformblock; // to avoid mapping beyond the end of the buffer.
}
glGenBuffers(1, &mBufferId);
@ -72,7 +83,6 @@ FLightBuffer::FLightBuffer()
}
Clear();
mLastMappedIndex = UINT_MAX;
}
FLightBuffer::~FLightBuffer()
@ -84,97 +94,58 @@ FLightBuffer::~FLightBuffer()
void FLightBuffer::Clear()
{
mIndex = 0;
mLastMappedIndex = UINT_MAX;
mIndices.Clear();
mUploadIndex = 0;
}
int FLightBuffer::UploadLights(FDynLightData &data)
{
// All meaasurements here are in vec4's.
int size0 = data.arrays[0].Size()/4;
int size1 = data.arrays[1].Size()/4;
int size2 = data.arrays[2].Size()/4;
int totalsize = size0 + size1 + size2 + 1;
// pointless type casting because some compilers can't print enough warnings.
if (mBlockAlign > 0 && (unsigned int)totalsize + (mIndex % mBlockAlign) > mBlockSize)
if (totalsize > (int)mMaxUploadSize)
{
mIndex = ((mIndex + mBlockAlign) / mBlockAlign) * mBlockAlign;
// can't be rendered all at once.
if ((unsigned int)totalsize > mBlockSize)
int diff = totalsize - (int)mMaxUploadSize;
size2 -= diff;
if (size2 < 0)
{
int diff = totalsize - (int)mBlockSize;
size2 -= diff;
if (size2 < 0)
{
size1 += size2;
size2 = 0;
}
if (size1 < 0)
{
size0 += size1;
size1 = 0;
}
totalsize = size0 + size1 + size2 + 1;
size1 += size2;
size2 = 0;
}
if (size1 < 0)
{
size0 += size1;
size1 = 0;
}
totalsize = size0 + size1 + size2 + 1;
}
if (totalsize <= 1) return -1;
if (mIndex + totalsize > mBufferSize/4)
{
// reallocate the buffer with twice the size
unsigned int newbuffer;
// first unmap the old buffer
glBindBuffer(mBufferType, mBufferId);
glUnmapBuffer(mBufferType);
// create and bind the new buffer, bind the old one to a copy target (too bad that DSA is not yet supported well enough to omit this crap.)
glGenBuffers(1, &newbuffer);
glBindBufferBase(mBufferType, LIGHTBUF_BINDINGPOINT, newbuffer);
glBindBuffer(mBufferType, newbuffer); // Note: Some older AMD drivers don't do that in glBindBufferBase, as they should.
glBindBuffer(GL_COPY_READ_BUFFER, mBufferId);
// create the new buffer's storage (twice as large as the old one)
mBufferSize *= 2;
mByteSize *= 2;
if (gl.lightmethod == LM_DIRECT)
{
glBufferStorage(mBufferType, mByteSize, NULL, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
mBufferPointer = (float*)glMapBufferRange(mBufferType, 0, mByteSize, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
}
else
{
glBufferData(mBufferType, mByteSize, NULL, GL_DYNAMIC_DRAW);
mBufferPointer = (float*)glMapBufferRange(mBufferType, 0, mByteSize, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
}
// copy contents and delete the old buffer.
glCopyBufferSubData(GL_COPY_READ_BUFFER, mBufferType, 0, 0, mByteSize/2);
glBindBuffer(GL_COPY_READ_BUFFER, 0);
glDeleteBuffers(1, &mBufferId);
mBufferId = newbuffer;
}
float *copyptr;
assert(mBufferPointer != NULL);
if (mBufferPointer == NULL) return -1;
copyptr = mBufferPointer + mIndex * 4;
if (totalsize <= 1) return -1; // there are no lights
assert(mBufferPointer != nullptr);
if (mBufferPointer == nullptr) return -1;
unsigned thisindex = mIndex.fetch_add(totalsize);
float parmcnt[] = { 0, float(size0), float(size0 + size1), float(size0 + size1 + size2) };
memcpy(&copyptr[0], parmcnt, 4 * sizeof(float));
memcpy(&copyptr[4], &data.arrays[0][0], 4 * size0*sizeof(float));
memcpy(&copyptr[4 + 4*size0], &data.arrays[1][0], 4 * size1*sizeof(float));
memcpy(&copyptr[4 + 4*(size0 + size1)], &data.arrays[2][0], 4 * size2*sizeof(float));
unsigned int bufferindex = mIndex;
mIndex += totalsize;
draw_dlight += (totalsize-1) / 2;
return bufferindex;
if (thisindex + totalsize <= mBufferSize)
{
float *copyptr = mBufferPointer + thisindex*4;
memcpy(&copyptr[0], parmcnt, ELEMENT_SIZE);
memcpy(&copyptr[4], &data.arrays[0][0], size0 * ELEMENT_SIZE);
memcpy(&copyptr[4 + 4*size0], &data.arrays[1][0], size1 * ELEMENT_SIZE);
memcpy(&copyptr[4 + 4*(size0 + size1)], &data.arrays[2][0], size2 * ELEMENT_SIZE);
return thisindex;
}
else
{
return -1; // Buffer is full. Since it is being used live at the point of the upload we cannot do much here but to abort.
}
}
void FLightBuffer::Begin()
@ -200,14 +171,20 @@ int FLightBuffer::BindUBO(unsigned int index)
{
unsigned int offset = (index / mBlockAlign) * mBlockAlign;
if (offset != mLastMappedIndex)
{
// this will only get called if a uniform buffer is used. For a shader storage buffer we only need to bind the buffer once at the start to all shader programs
mLastMappedIndex = offset;
glBindBufferRange(GL_UNIFORM_BUFFER, LIGHTBUF_BINDINGPOINT, mBufferId, offset*16, mBlockSize*16); // we go from counting vec4's to counting bytes here.
glBindBufferRange(GL_UNIFORM_BUFFER, LIGHTBUF_BINDINGPOINT, mBufferId, offset * ELEMENT_SIZE, mBlockSize * ELEMENT_SIZE);
}
return (index - offset);
}
void FLightBuffer::BindBase()
{
glBindBufferBase(mBufferType, LIGHTBUF_BINDINGPOINT, mBufferId);
mLastMappedIndex = UINT_MAX;
}

View file

@ -2,6 +2,7 @@
#define __GL_LIGHTBUFFER_H
#include "tarray.h"
#include <atomic>
struct FDynLightData;
class FLightBuffer
@ -11,13 +12,13 @@ class FLightBuffer
float * mBufferPointer;
unsigned int mBufferType;
unsigned int mIndex;
unsigned int mUploadIndex;
std::atomic<unsigned int> mIndex;
unsigned int mLastMappedIndex;
unsigned int mBlockAlign;
unsigned int mBlockSize;
unsigned int mBufferSize;
unsigned int mByteSize;
unsigned int mMaxUploadSize;
public:
@ -28,6 +29,7 @@ public:
void Begin();
void Finish();
int BindUBO(unsigned int index);
void BindBase();
unsigned int GetBlockSize() const { return mBlockSize; }
unsigned int GetBufferType() const { return mBufferType; }
unsigned int GetIndexPtr() const { return mIndices.Size(); }

View file

@ -310,6 +310,7 @@ void GLSceneDrawer::RenderScene(int recursion)
gl_RenderState.SetCameraPos(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z);
GLRenderer->mLights->BindBase();
gl_RenderState.EnableFog(true);
gl_RenderState.BlendFunc(GL_ONE,GL_ZERO);
@ -838,9 +839,6 @@ void GLSceneDrawer::SetFixedColormap (player_t *player)
sector_t * GLSceneDrawer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen)
{
sector_t * lviewsector;
GLRenderer->mSceneClearColor[0] = 0.0f;
GLRenderer->mSceneClearColor[1] = 0.0f;
GLRenderer->mSceneClearColor[2] = 0.0f;
R_SetupFrame (r_viewpoint, r_viewwindow, camera);
SetViewArea();
@ -1007,6 +1005,21 @@ void FGLRenderer::RenderView (player_t* player)
// Render the view to a savegame picture
//
//===========================================================================
#ifdef __ANDROID__
uint8_t * gles_convertRGB(uint8_t * data, int width, int height)
{
uint8_t *src = data;
uint8_t *dst = data;
for (int i=0; i<width*height; i++) {
for (int j=0; j<3; j++)
*(dst++) = *(src++);
src++;
}
return dst;
}
#endif
void GLSceneDrawer::WriteSavePic (player_t *player, FileWriter *file, int width, int height)
{
@ -1040,9 +1053,16 @@ void GLSceneDrawer::WriteSavePic (player_t *player, FileWriter *file, int width,
GLRenderer->CopyToBackbuffer(&bounds, false);
glFlush();
#ifdef __MOBILE__ //Some androids do not like GL_RGB
uint8_t * scr = (uint8_t *)M_Malloc(width * height * 4);
glReadPixels(0,0,width, height,GL_RGBA,GL_UNSIGNED_BYTE,scr);
gles_convertRGB(scr,width,height);
M_CreatePNG (file, scr + ((height-1) * width * 3), NULL, SS_RGB, width, height, -width * 3, Gamma);
#else
uint8_t * scr = (uint8_t *)M_Malloc(width * height * 3);
glReadPixels(0,0,width, height,GL_RGB,GL_UNSIGNED_BYTE,scr);
M_CreatePNG (file, scr + ((height-1) * width * 3), NULL, SS_RGB, width, height, -width * 3, Gamma);
#endif
M_Free(scr);
}

View file

@ -78,12 +78,13 @@
//-----------------------------------------------------------------------------
//
// Shamelessly lifted from Doomsday (written by Jaakko Keränen)
// Shamelessly lifted from Doomsday (written by Jaakko Ker?en)
// also shamelessly lifted from ZDoomGL! ;)
//
//-----------------------------------------------------------------------------
CVAR(Float, skyoffset, 0, 0) // for testing
CVAR(Bool, gl_skydome, true, CVAR_GLOBALCONFIG|CVAR_ARCHIVE)
//-----------------------------------------------------------------------------
//
@ -227,8 +228,8 @@ void FSkyVertexBuffer::CreateDome()
mVertices[10].Set(-1.0f, 0.0f, -1.0f);
mVertices[11].Set(0.0f, 0.0f, 1.0f);
mColumns = 128;
mRows = 4;
mColumns = 32;
mRows = 2;
CreateSkyHemisphere(SKYHEMI_UPPER);
CreateSkyHemisphere(SKYHEMI_LOWER);
mPrimStart.Push(mVertices.Size());
@ -499,6 +500,15 @@ static void RenderBox(FTextureID texno, FMaterial * gltex, float x_offset, bool
//-----------------------------------------------------------------------------
void GLSkyPortal::DrawContents()
{
if (origin->texture[0])
{
PalEntry pe = origin->texture[0]->tex->GetSkyCapColor(false);
GLRenderer->mSceneClearColor[0] = pe.r / 255.f;
GLRenderer->mSceneClearColor[1] = pe.g / 255.f;
GLRenderer->mSceneClearColor[2] = pe.b / 255.f;
}
if (!gl_skydome) return;
bool drawBoth = false;
// We have no use for Doom lighting special handling here, so disable it for this function.

View file

@ -442,7 +442,10 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char *
unsigned int lightbuffertype = GLRenderer->mLights->GetBufferType();
unsigned int lightbuffersize = GLRenderer->mLights->GetBlockSize();
#ifdef __MOBILE__
vp_comb.Format(ES_VERSION_STR"\n#define NUM_UBO_LIGHTS %d\n", lightbuffersize);
if (gl_light_buffer_type == 0)
vp_comb.Format(ES_VERSION_STR"\n#define NUM_UBO_LIGHTS %d\n", lightbuffersize);
else
vp_comb.Format(ES_VERSION_STR"\n#define SHADER_STORAGE_LIGHTS\n");
#else
if (lightbuffertype == GL_UNIFORM_BUFFER)
{

View file

@ -29,6 +29,7 @@ EXTERN_CVAR (Bool, gl_light_sprites);
EXTERN_CVAR (Bool, gl_light_particles);
EXTERN_CVAR (Bool, gl_light_shadowmap);
EXTERN_CVAR (Int, gl_shadowmap_quality);
EXTERN_CVAR (Int, gl_light_buffer_type);
EXTERN_CVAR(Int, gl_fogmode)
EXTERN_CVAR(Int, gl_lightmode)

View file

@ -212,6 +212,8 @@ void gl_LoadExtensions()
gl.glslversion = 3.3;
gl.flags |= RFL_NO_CLIP_PLANES;
gl.flags |= RFL_UINT_IDX;
if (gl_light_buffer_type == 1)
gl.flags |= RFL_SHADER_STORAGE_BUFFER;
}
#else

View file

@ -61,7 +61,7 @@ public:
if (!memcmp(t, Array[i], sizeof(T))) return Array[i];
}
T * newo=TheFreeList.GetNew();
memset(newo, 0, sizeof(T));
*newo=*t;
Array.Push(newo);
return newo;

View file

@ -0,0 +1,77 @@
/*
** i_common.h
**
**---------------------------------------------------------------------------
** Copyright 2012-2015 Alexey Lysiuk
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#ifndef COCOA_I_COMMON_INCLUDED
#define COCOA_I_COMMON_INCLUDED
#import <AppKit/AppKit.h>
struct RenderBufferOptions
{
float pixelScale;
float shiftX;
float shiftY;
float width;
float height;
bool dirty;
};
extern RenderBufferOptions rbOpts;
// Version of AppKit framework we are interested in
// The following values are needed to build with earlier SDKs
#define AppKit10_7 1138
#define AppKit10_8 1187
#define AppKit10_9 1265
@interface NSWindow(ExitAppOnClose)
- (void)exitAppOnClose;
@end
void I_ProcessEvent(NSEvent* event);
void I_ProcessJoysticks();
NSSize I_GetContentViewSize(const NSWindow* window);
void I_SetMainWindowVisible(bool visible);
void I_SetNativeMouse(bool wantNative);
#endif // COCOA_I_COMMON_INCLUDED

View file

@ -0,0 +1,789 @@
/*
** i_input.mm
**
**---------------------------------------------------------------------------
** Copyright 2012-2015 Alexey Lysiuk
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "i_common.h"
#import <Carbon/Carbon.h>
#include "c_console.h"
#include "c_cvars.h"
#include "c_dispatch.h"
#include "d_event.h"
#include "d_gui.h"
#include "dikeys.h"
#include "doomdef.h"
#include "doomstat.h"
#include "v_video.h"
#include "events.h"
EXTERN_CVAR(Int, m_use_mouse)
CVAR(Bool, use_mouse, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR(Bool, m_noprescale, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR(Bool, m_filter, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR(Bool, k_allowfullscreentoggle, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CUSTOM_CVAR(Int, mouse_capturemode, 1, CVAR_GLOBALCONFIG | CVAR_ARCHIVE)
{
if (self < 0)
{
self = 0;
}
else if (self > 2)
{
self = 2;
}
}
extern int paused, chatmodeon;
extern constate_e ConsoleState;
bool GUICapture;
namespace
{
// TODO: remove this magic!
size_t s_skipMouseMoves;
// ---------------------------------------------------------------------------
void CheckGUICapture()
{
bool wantCapture = (MENU_Off == menuactive)
? (c_down == ConsoleState || c_falling == ConsoleState || chatmodeon)
: (MENU_On == menuactive || MENU_OnNoPause == menuactive);
// [ZZ] check active event handlers that want the UI processing
if (!wantCapture && E_CheckUiProcessors())
{
wantCapture = true;
}
if (wantCapture != GUICapture)
{
GUICapture = wantCapture;
ResetButtonStates();
}
}
void SetCursorPosition(const NSPoint position)
{
NSWindow* window = [NSApp keyWindow];
if (nil == window)
{
return;
}
const NSRect displayRect = [[window screen] frame];
const CGPoint eventPoint = CGPointMake(position.x, displayRect.size.height - position.y);
CGEventSourceRef eventSource = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
if (NULL != eventSource)
{
CGEventRef mouseMoveEvent = CGEventCreateMouseEvent(eventSource,
kCGEventMouseMoved, eventPoint, kCGMouseButtonLeft);
if (NULL != mouseMoveEvent)
{
CGEventPost(kCGHIDEventTap, mouseMoveEvent);
CFRelease(mouseMoveEvent);
}
CFRelease(eventSource);
}
// TODO: remove this magic!
s_skipMouseMoves = 2;
}
void CenterCursor()
{
NSWindow* window = [NSApp keyWindow];
if (nil == window)
{
return;
}
const NSRect displayRect = [[window screen] frame];
const NSRect windowRect = [window frame];
const NSPoint centerPoint = { NSMidX(windowRect), NSMidY(windowRect) };
SetCursorPosition(centerPoint);
}
bool IsInGame()
{
switch (mouse_capturemode)
{
default:
case 0:
return gamestate == GS_LEVEL;
case 1:
return gamestate == GS_LEVEL
|| gamestate == GS_INTERMISSION
|| gamestate == GS_FINALE;
case 2:
return true;
}
}
void CheckNativeMouse()
{
const bool windowed = (NULL == screen) || !screen->IsFullscreen();
bool wantNative;
if (windowed)
{
if (![NSApp isActive] || !use_mouse)
{
wantNative = true;
}
else if (MENU_WaitKey == menuactive)
{
wantNative = false;
}
else
{
wantNative = (!m_use_mouse || MENU_WaitKey != menuactive)
&& (!IsInGame() || GUICapture || paused || demoplayback);
}
}
else
{
// ungrab mouse when in the menu with mouse control on.
wantNative = m_use_mouse
&& (MENU_On == menuactive || MENU_OnNoPause == menuactive);
}
if (!wantNative && E_CheckRequireMouse())
wantNative = true;
I_SetNativeMouse(wantNative);
}
} // unnamed namespace
void I_GetEvent()
{
[[NSRunLoop currentRunLoop] limitDateForMode:NSDefaultRunLoopMode];
}
void I_StartTic()
{
CheckGUICapture();
CheckNativeMouse();
I_ProcessJoysticks();
I_GetEvent();
}
void I_StartFrame()
{
}
void I_SetMouseCapture()
{
}
void I_ReleaseMouseCapture()
{
}
void I_SetNativeMouse(bool wantNative)
{
static bool nativeMouse = true;
static NSPoint mouseLocation;
if (wantNative != nativeMouse)
{
nativeMouse = wantNative;
if (!wantNative)
{
mouseLocation = [NSEvent mouseLocation];
CenterCursor();
}
CGAssociateMouseAndMouseCursorPosition(wantNative);
if (wantNative)
{
SetCursorPosition(mouseLocation);
[NSCursor unhide];
}
else
{
[NSCursor hide];
}
}
}
// ---------------------------------------------------------------------------
namespace
{
const size_t KEY_COUNT = 128;
// See Carbon -> HIToolbox -> Events.h for kVK_ constants
const uint8_t KEYCODE_TO_DIK[KEY_COUNT] =
{
DIK_A, DIK_S, DIK_D, DIK_F, DIK_H, DIK_G, DIK_Z, DIK_X, // 0x00 - 0x07
DIK_C, DIK_V, 0, DIK_B, DIK_Q, DIK_W, DIK_E, DIK_R, // 0x08 - 0x0F
DIK_Y, DIK_T, DIK_1, DIK_2, DIK_3, DIK_4, DIK_6, DIK_5, // 0x10 - 0x17
DIK_EQUALS, DIK_9, DIK_7, DIK_MINUS, DIK_8, DIK_0, DIK_RBRACKET, DIK_O, // 0x18 - 0x1F
DIK_U, DIK_LBRACKET, DIK_I, DIK_P, DIK_RETURN, DIK_L, DIK_J, DIK_APOSTROPHE, // 0x20 - 0x27
DIK_K, DIK_SEMICOLON, DIK_BACKSLASH, DIK_COMMA, DIK_SLASH, DIK_N, DIK_M, DIK_PERIOD, // 0x28 - 0x2F
DIK_TAB, DIK_SPACE, DIK_GRAVE, DIK_BACK, 0, DIK_ESCAPE, 0, DIK_LWIN, // 0x30 - 0x37
DIK_LSHIFT, DIK_CAPITAL, DIK_LMENU, DIK_LCONTROL, DIK_RSHIFT, DIK_RMENU, DIK_RCONTROL, 0, // 0x38 - 0x3F
0, DIK_DECIMAL, 0, DIK_MULTIPLY, 0, DIK_ADD, 0, 0, // 0x40 - 0x47
DIK_VOLUMEUP, DIK_VOLUMEDOWN, DIK_MUTE, DIK_SLASH, DIK_NUMPADENTER, 0, DIK_SUBTRACT, 0, // 0x48 - 0x4F
0, DIK_NUMPAD_EQUALS, DIK_NUMPAD0, DIK_NUMPAD1, DIK_NUMPAD2, DIK_NUMPAD3, DIK_NUMPAD4, DIK_NUMPAD5, // 0x50 - 0x57
DIK_NUMPAD6, DIK_NUMPAD7, 0, DIK_NUMPAD8, DIK_NUMPAD9, 0, 0, 0, // 0x58 - 0x5F
DIK_F5, DIK_F6, DIK_F7, DIK_F3, DIK_F8, DIK_F9, 0, DIK_F11, // 0x60 - 0x67
0, DIK_F13, 0, DIK_F14, 0, DIK_F10, 0, DIK_F12, // 0x68 - 0x6F
0, DIK_F15, 0, DIK_HOME, 0, DIK_DELETE, DIK_F4, DIK_END, // 0x70 - 0x77
DIK_F2, 0, DIK_F1, DIK_LEFT, DIK_RIGHT, DIK_DOWN, DIK_UP, 0, // 0x78 - 0x7F
};
const uint8_t KEYCODE_TO_ASCII[KEY_COUNT] =
{
'a', 's', 'd', 'f', 'h', 'g', 'z', 'x', // 0x00 - 0x07
'c', 'v', 0, 'b', 'q', 'w', 'e', 'r', // 0x08 - 0x0F
'y', 't', '1', '2', '3', '4', '6', '5', // 0x10 - 0x17
'=', '9', '7', '-', '8', '0', ']', 'o', // 0x18 - 0x1F
'u', '[', 'i', 'p', 13, 'l', 'j', '\'', // 0x20 - 0x27
'k', ';', '\\', ',', '/', 'n', 'm', '.', // 0x28 - 0x2F
9, ' ', '`', 12, 0, 27, 0, 0, // 0x30 - 0x37
0, 0, 0, 0, 0, 0, 0, 0, // 0x38 - 0x3F
0, 0, 0, 0, 0, 0, 0, 0, // 0x40 - 0x47
0, 0, 0, 0, 0, 0, 0, 0, // 0x48 - 0x4F
0, 0, 0, 0, 0, 0, 0, 0, // 0x50 - 0x57
0, 0, 0, 0, 0, 0, 0, 0, // 0x58 - 0x5F
0, 0, 0, 0, 0, 0, 0, 0, // 0x60 - 0x67
0, 0, 0, 0, 0, 0, 0, 0, // 0x68 - 0x6F
0, 0, 0, 0, 0, 0, 0, 0, // 0x70 - 0x77
0, 0, 0, 0, 0, 0, 0, 0, // 0x78 - 0x7F
};
uint8_t ModifierToDIK(const uint32_t modifier)
{
switch (modifier)
{
case NSAlphaShiftKeyMask: return DIK_CAPITAL;
case NSShiftKeyMask: return DIK_LSHIFT;
case NSControlKeyMask: return DIK_LCONTROL;
case NSAlternateKeyMask: return DIK_LMENU;
case NSCommandKeyMask: return DIK_LWIN;
}
return 0;
}
int16_t ModifierFlagsToGUIKeyModifiers(NSEvent* theEvent)
{
const NSUInteger modifiers([theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask);
return ((modifiers & NSShiftKeyMask ) ? GKM_SHIFT : 0)
| ((modifiers & NSControlKeyMask ) ? GKM_CTRL : 0)
| ((modifiers & NSAlternateKeyMask) ? GKM_ALT : 0)
| ((modifiers & NSCommandKeyMask ) ? GKM_META : 0);
}
bool ShouldGenerateGUICharEvent(NSEvent* theEvent)
{
const NSUInteger modifiers([theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask);
return !(modifiers & NSControlKeyMask)
&& !(modifiers & NSAlternateKeyMask)
&& !(modifiers & NSCommandKeyMask)
&& !(modifiers & NSFunctionKeyMask);
}
NSStringEncoding GetEncodingForUnicodeCharacter(const unichar character)
{
if (character >= L'\u0100' && character <= L'\u024F')
{
return NSWindowsCP1250StringEncoding; // Central and Eastern Europe
}
else if (character >= L'\u0370' && character <= L'\u03FF')
{
return NSWindowsCP1253StringEncoding; // Greek
}
else if (character >= L'\u0400' && character <= L'\u04FF')
{
return NSWindowsCP1251StringEncoding; // Cyrillic
}
// TODO: add handling for other characters
// TODO: Turkish should use NSWindowsCP1254StringEncoding
return NSWindowsCP1252StringEncoding;
}
unsigned char GetCharacterFromNSEvent(NSEvent* theEvent, unichar *realchar)
{
const NSString* unicodeCharacters = [theEvent characters];
if (0 == [unicodeCharacters length])
{
return '\0';
}
const unichar unicodeCharacter = [unicodeCharacters characterAtIndex:0];
const NSStringEncoding encoding = GetEncodingForUnicodeCharacter(unicodeCharacter);
unsigned char character = '\0';
if (NSWindowsCP1252StringEncoding == encoding)
{
// TODO: make sure that the following is always correct
character = unicodeCharacter & 0xFF;
}
else
{
const NSData* const characters =
[[theEvent characters] dataUsingEncoding:encoding];
character = [characters length] > 0
? *static_cast<const unsigned char*>([characters bytes])
: '\0';
}
*realchar = unicodeCharacter;
return character;
}
void ProcessKeyboardEventInMenu(NSEvent* theEvent)
{
event_t event = {};
unichar realchar;
event.type = EV_GUI_Event;
event.subtype = NSKeyDown == [theEvent type] ? EV_GUI_KeyDown : EV_GUI_KeyUp;
event.data2 = GetCharacterFromNSEvent(theEvent, &realchar);
event.data3 = ModifierFlagsToGUIKeyModifiers(theEvent);
if (EV_GUI_KeyDown == event.subtype && [theEvent isARepeat])
{
event.subtype = EV_GUI_KeyRepeat;
}
const unsigned short keyCode = [theEvent keyCode];
switch (keyCode)
{
case kVK_Return: event.data1 = GK_RETURN; break;
case kVK_PageUp: event.data1 = GK_PGUP; break;
case kVK_PageDown: event.data1 = GK_PGDN; break;
case kVK_End: event.data1 = GK_END; break;
case kVK_Home: event.data1 = GK_HOME; break;
case kVK_LeftArrow: event.data1 = GK_LEFT; break;
case kVK_RightArrow: event.data1 = GK_RIGHT; break;
case kVK_UpArrow: event.data1 = GK_UP; break;
case kVK_DownArrow: event.data1 = GK_DOWN; break;
case kVK_Delete: event.data1 = GK_BACKSPACE; break;
case kVK_ForwardDelete: event.data1 = GK_DEL; break;
case kVK_Escape: event.data1 = GK_ESCAPE; break;
case kVK_F1: event.data1 = GK_F1; break;
case kVK_F2: event.data1 = GK_F2; break;
case kVK_F3: event.data1 = GK_F3; break;
case kVK_F4: event.data1 = GK_F4; break;
case kVK_F5: event.data1 = GK_F5; break;
case kVK_F6: event.data1 = GK_F6; break;
case kVK_F7: event.data1 = GK_F7; break;
case kVK_F8: event.data1 = GK_F8; break;
case kVK_F9: event.data1 = GK_F9; break;
case kVK_F10: event.data1 = GK_F10; break;
case kVK_F11: event.data1 = GK_F11; break;
case kVK_F12: event.data1 = GK_F12; break;
case kVK_F13: event.data1 = GK_SYSRQ; break;
default:
event.data1 = KEYCODE_TO_ASCII[keyCode];
break;
}
if (event.data1 < 128)
{
event.data1 = toupper(event.data1);
D_PostEvent(&event);
}
if (!iscntrl(event.data2)
&& EV_GUI_KeyUp != event.subtype
&& ShouldGenerateGUICharEvent(theEvent))
{
event.subtype = EV_GUI_Char;
event.data1 = realchar;
event.data2 = event.data3 & GKM_ALT;
D_PostEvent(&event);
}
}
void NSEventToGameMousePosition(NSEvent* inEvent, event_t* outEvent)
{
const NSWindow* window = [inEvent window];
const NSView* view = [window contentView];
const NSPoint screenPos = [NSEvent mouseLocation];
const NSRect screenRect = NSMakeRect(screenPos.x, screenPos.y, 0, 0);
const NSRect windowRect = [window convertRectFromScreen:screenRect];
const NSPoint viewPos = [view convertPointToBacking:windowRect.origin];
const CGFloat frameHeight = I_GetContentViewSize(window).height;
outEvent->data1 = static_cast<int16_t>( viewPos.x);
outEvent->data2 = static_cast<int16_t>(frameHeight - viewPos.y);
// Compensate letterbox adjustment done by cross-platform code
// More elegant solution is a bit problematic due to HiDPI/Retina support
outEvent->data2 += (int(frameHeight) - screen->VideoHeight) / 2;
screen->ScaleCoordsFromWindow(outEvent->data1, outEvent->data2);
}
void ProcessMouseMoveInMenu(NSEvent* theEvent)
{
event_t event = {};
event.type = EV_GUI_Event;
event.subtype = EV_GUI_MouseMove;
event.data3 = ModifierFlagsToGUIKeyModifiers(theEvent);
NSEventToGameMousePosition(theEvent, &event);
D_PostEvent(&event);
}
void ProcessMouseMoveInGame(NSEvent* theEvent)
{
int x([theEvent deltaX]);
int y(-[theEvent deltaY]);
if (0 == x && 0 == y)
{
return;
}
if (!m_noprescale)
{
x *= 3;
y *= 2;
}
event_t event = {};
static int lastX = 0, lastY = 0;
if (m_filter)
{
event.x = (x + lastX) / 2;
event.y = (y + lastY) / 2;
}
else
{
event.x = x;
event.y = y;
}
lastX = x;
lastY = y;
if (0 != event.x || 0 != event.y)
{
event.type = EV_Mouse;
D_PostEvent(&event);
}
}
void ProcessKeyboardEvent(NSEvent* theEvent)
{
const unsigned short keyCode = [theEvent keyCode];
if (keyCode >= KEY_COUNT)
{
assert(!"Unknown keycode");
return;
}
const bool isARepeat = [theEvent isARepeat];
if (k_allowfullscreentoggle
&& (kVK_ANSI_F == keyCode)
&& (NSCommandKeyMask & [theEvent modifierFlags])
&& (NSKeyDown == [theEvent type])
&& !isARepeat)
{
ToggleFullscreen = !ToggleFullscreen;
return;
}
if (GUICapture)
{
ProcessKeyboardEventInMenu(theEvent);
}
else if (!isARepeat)
{
event_t event = {};
event.type = NSKeyDown == [theEvent type] ? EV_KeyDown : EV_KeyUp;
event.data1 = KEYCODE_TO_DIK[ keyCode ];
if (0 != event.data1)
{
event.data2 = KEYCODE_TO_ASCII[ keyCode ];
D_PostEvent(&event);
}
}
}
void ProcessKeyboardFlagsEvent(NSEvent* theEvent)
{
if (GUICapture)
{
// Ignore events from modifier keys in menu/console/chat
return;
}
static const uint32_t FLAGS_MASK =
NSDeviceIndependentModifierFlagsMask & ~NSNumericPadKeyMask;
const uint32_t modifiers = [theEvent modifierFlags] & FLAGS_MASK;
static uint32_t oldModifiers = 0;
const uint32_t deltaModifiers = modifiers ^ oldModifiers;
if (0 == deltaModifiers)
{
return;
}
event_t event = {};
event.type = modifiers > oldModifiers ? EV_KeyDown : EV_KeyUp;
event.data1 = ModifierToDIK(deltaModifiers);
oldModifiers = modifiers;
if (DIK_CAPITAL == event.data1)
{
// Caps Lock is a modifier key which generates one event per state change
// but not per actual key press or release. So treat any event as key down
event.type = EV_KeyDown;
}
D_PostEvent(&event);
}
void ProcessMouseMoveEvent(NSEvent* theEvent)
{
if (!use_mouse)
{
return;
}
if (s_skipMouseMoves > 0)
{
--s_skipMouseMoves;
return;
}
if (GUICapture)
{
ProcessMouseMoveInMenu(theEvent);
}
else
{
ProcessMouseMoveInGame(theEvent);
}
}
void ProcessMouseButtonEvent(NSEvent* theEvent)
{
if (!use_mouse)
{
return;
}
event_t event = {};
const NSEventType cocoaEventType = [theEvent type];
if (GUICapture)
{
event.type = EV_GUI_Event;
event.data3 = ModifierFlagsToGUIKeyModifiers(theEvent);
switch (cocoaEventType)
{
case NSLeftMouseDown: event.subtype = EV_GUI_LButtonDown; break;
case NSRightMouseDown: event.subtype = EV_GUI_RButtonDown; break;
case NSOtherMouseDown: event.subtype = EV_GUI_MButtonDown; break;
case NSLeftMouseUp: event.subtype = EV_GUI_LButtonUp; break;
case NSRightMouseUp: event.subtype = EV_GUI_RButtonUp; break;
case NSOtherMouseUp: event.subtype = EV_GUI_MButtonUp; break;
default: break;
}
NSEventToGameMousePosition(theEvent, &event);
D_PostEvent(&event);
}
else
{
switch (cocoaEventType)
{
case NSLeftMouseDown:
case NSRightMouseDown:
case NSOtherMouseDown:
event.type = EV_KeyDown;
break;
case NSLeftMouseUp:
case NSRightMouseUp:
case NSOtherMouseUp:
event.type = EV_KeyUp;
break;
default:
break;
}
event.data1 = MIN(KEY_MOUSE1 + [theEvent buttonNumber], NSInteger(KEY_MOUSE8));
D_PostEvent(&event);
}
}
void ProcessMouseWheelEvent(NSEvent* theEvent)
{
if (!use_mouse)
{
return;
}
const int16_t modifiers = ModifierFlagsToGUIKeyModifiers(theEvent);
const CGFloat delta = (modifiers & GKM_SHIFT)
? [theEvent deltaX]
: [theEvent deltaY];
const bool isZeroDelta = fabs(delta) < 1.0E-5;
if (isZeroDelta && GUICapture)
{
return;
}
event_t event = {};
if (GUICapture)
{
event.type = EV_GUI_Event;
event.subtype = delta > 0.0f ? EV_GUI_WheelUp : EV_GUI_WheelDown;
event.data3 = modifiers;
}
else
{
event.type = isZeroDelta ? EV_KeyUp : EV_KeyDown;
event.data1 = delta > 0.0f ? KEY_MWHEELUP : KEY_MWHEELDOWN;
}
D_PostEvent(&event);
}
} // unnamed namespace
void I_ProcessEvent(NSEvent* event)
{
const NSEventType eventType = [event type];
switch (eventType)
{
case NSMouseMoved:
ProcessMouseMoveEvent(event);
break;
case NSLeftMouseDown:
case NSLeftMouseUp:
case NSRightMouseDown:
case NSRightMouseUp:
case NSOtherMouseDown:
case NSOtherMouseUp:
ProcessMouseButtonEvent(event);
break;
case NSLeftMouseDragged:
case NSRightMouseDragged:
case NSOtherMouseDragged:
ProcessMouseButtonEvent(event);
ProcessMouseMoveEvent(event);
break;
case NSScrollWheel:
ProcessMouseWheelEvent(event);
break;
case NSKeyDown:
case NSKeyUp:
ProcessKeyboardEvent(event);
break;
case NSFlagsChanged:
ProcessKeyboardFlagsEvent(event);
break;
default:
break;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,507 @@
/*
** i_main.mm
**
**---------------------------------------------------------------------------
** Copyright 2012-2018 Alexey Lysiuk
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "i_common.h"
#include "s_sound.h"
#include <sys/sysctl.h>
#include <unistd.h>
#include "c_console.h"
#include "c_cvars.h"
#include "cmdlib.h"
#include "d_main.h"
#include "doomerrors.h"
#include "i_system.h"
#include "m_argv.h"
#include "s_sound.h"
#include "st_console.h"
#include "version.h"
#include "doomerrors.h"
#include "s_music.h"
#define ZD_UNUSED(VARIABLE) ((void)(VARIABLE))
// ---------------------------------------------------------------------------
CVAR (Bool, i_soundinbackground, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
EXTERN_CVAR(Int, vid_defwidth )
EXTERN_CVAR(Int, vid_defheight)
EXTERN_CVAR(Bool, vid_vsync )
EXTERN_CVAR(Bool, fullscreen )
// ---------------------------------------------------------------------------
void Mac_I_FatalError(const char* const message)
{
I_SetMainWindowVisible(false);
S_StopMusic(true);
FConsoleWindow::GetInstance().ShowFatalError(message);
}
void I_DetectOS()
{
SInt32 majorVersion = 0;
Gestalt(gestaltSystemVersionMajor, &majorVersion);
SInt32 minorVersion = 0;
Gestalt(gestaltSystemVersionMinor, &minorVersion);
SInt32 bugFixVersion = 0;
Gestalt(gestaltSystemVersionBugFix, &bugFixVersion);
const char* name = "Unknown version";
if (10 == majorVersion) switch (minorVersion)
{
case 4: name = "Mac OS X Tiger"; break;
case 5: name = "Mac OS X Leopard"; break;
case 6: name = "Mac OS X Snow Leopard"; break;
case 7: name = "Mac OS X Lion"; break;
case 8: name = "OS X Mountain Lion"; break;
case 9: name = "OS X Mavericks"; break;
case 10: name = "OS X Yosemite"; break;
case 11: name = "OS X El Capitan"; break;
case 12: name = "macOS Sierra"; break;
case 13: name = "macOS High Sierra"; break;
case 14: name = "macOS Mojave"; break;
case 15: name = "macOS Catalina"; break;
}
char release[16] = "unknown";
size_t size = sizeof release - 1;
sysctlbyname("kern.osversion", release, &size, nullptr, 0);
const char* const architecture =
#ifdef __i386__
"32-bit Intel";
#elif defined __x86_64__
"64-bit Intel";
#else
"Unknown";
#endif
Printf("OS: %s %d.%d.%d (%s) %s\n", name,
int(majorVersion), int(minorVersion), int(bugFixVersion),
release, architecture);
}
FArgs* Args; // command line arguments
namespace
{
TArray<FString> s_argv;
int DoMain(int argc, char** argv)
{
printf(GAMENAME" %s - %s - Cocoa version\nCompiled on %s\n\n",
GetVersionString(), GetGitTime(), __DATE__);
seteuid(getuid());
// Set LC_NUMERIC environment variable in case some library decides to
// clear the setlocale call at least this will be correct.
// Note that the LANG environment variable is overridden by LC_*
setenv("LC_NUMERIC", "C", 1);
setlocale(LC_ALL, "C");
// Set reasonable default values for video settings
const NSSize screenSize = [[NSScreen mainScreen] frame].size;
vid_defwidth = static_cast<int>(screenSize.width);
vid_defheight = static_cast<int>(screenSize.height);
vid_vsync = true;
fullscreen = true;
Args = new FArgs(argc, argv);
NSString* exePath = [[NSBundle mainBundle] executablePath];
progdir = [[exePath stringByDeletingLastPathComponent] UTF8String];
progdir += "/";
auto ret = D_DoomMain();
FConsoleWindow::DeleteInstance();
return ret;
}
} // unnamed namespace
// ---------------------------------------------------------------------------
@interface ApplicationController : NSResponder<NSApplicationDelegate>
{
}
- (void)keyDown:(NSEvent*)theEvent;
- (void)keyUp:(NSEvent*)theEvent;
- (void)applicationDidBecomeActive:(NSNotification*)aNotification;
- (void)applicationWillResignActive:(NSNotification*)aNotification;
- (void)applicationDidFinishLaunching:(NSNotification*)aNotification;
- (BOOL)application:(NSApplication*)theApplication openFile:(NSString*)filename;
- (void)processEvents:(NSTimer*)timer;
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
- (void)sendExitEvent:(id)sender;
@end
ApplicationController* appCtrl;
@implementation ApplicationController
- (void)keyDown:(NSEvent*)theEvent
{
// Empty but present to avoid playing of 'beep' alert sound
ZD_UNUSED(theEvent);
}
- (void)keyUp:(NSEvent*)theEvent
{
// Empty but present to avoid playing of 'beep' alert sound
ZD_UNUSED(theEvent);
}
extern bool AppActive;
- (void)applicationDidBecomeActive:(NSNotification*)aNotification
{
ZD_UNUSED(aNotification);
S_SetSoundPaused(1);
AppActive = true;
}
- (void)applicationWillResignActive:(NSNotification*)aNotification
{
ZD_UNUSED(aNotification);
S_SetSoundPaused(i_soundinbackground);
AppActive = false;
}
- (void)applicationDidFinishLaunching:(NSNotification*)aNotification
{
// When starting from command line with real executable path, e.g. ZDoom.app/Contents/MacOS/ZDoom
// application remains deactivated for an unknown reason.
// The following call resolves this issue
[NSApp activateIgnoringOtherApps:YES];
// Setup timer for custom event loop
NSTimer* timer = [NSTimer timerWithTimeInterval:0
target:self
selector:@selector(processEvents:)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer
forMode:NSDefaultRunLoopMode];
FConsoleWindow::CreateInstance();
const size_t argc = s_argv.Size();
TArray<char*> argv(argc + 1, true);
for (size_t i = 0; i < argc; ++i)
{
argv[i] = s_argv[i].LockBuffer();
}
argv[argc] = nullptr;
exit(DoMain(argc, &argv[0]));
}
- (BOOL)application:(NSApplication*)theApplication openFile:(NSString*)filename
{
ZD_UNUSED(theApplication);
// Some parameters from command line are passed to this function
// These parameters need to be skipped to avoid duplication
// Note: SDL has different approach to fix this issue, see the same method in SDLMain.m
const char* const charFileName = [filename UTF8String];
for (size_t i = 0, count = s_argv.Size(); i < count; ++i)
{
if (0 == strcmp(s_argv[i], charFileName))
{
return FALSE;
}
}
bool iwad = false;
if (const char* const extPos = strrchr(charFileName, '.'))
{
iwad = 0 == stricmp(extPos, ".iwad")
|| 0 == stricmp(extPos, ".ipk3")
|| 0 == stricmp(extPos, ".ipk7");
}
s_argv.Push(iwad ? "-iwad" : "-file");
s_argv.Push(charFileName);
return TRUE;
}
- (void)processEvents:(NSTimer*)timer
{
ZD_UNUSED(timer);
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
while (true)
{
NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
untilDate:[NSDate dateWithTimeIntervalSinceNow:0]
inMode:NSDefaultRunLoopMode
dequeue:YES];
if (nil == event)
{
break;
}
I_ProcessEvent(event);
[NSApp sendEvent:event];
}
[NSApp updateWindows];
[pool release];
}
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
{
[self sendExitEvent:sender];
return NSTerminateLater;
}
- (void)sendExitEvent:(id)sender
{
throw CExitEvent(0);
}
@end
// ---------------------------------------------------------------------------
namespace
{
NSMenuItem* CreateApplicationMenu()
{
NSMenu* menu = [NSMenu new];
[menu addItemWithTitle:[@"About " stringByAppendingString:@GAMENAME]
action:@selector(orderFrontStandardAboutPanel:)
keyEquivalent:@""];
[menu addItem:[NSMenuItem separatorItem]];
[menu addItemWithTitle:[@"Hide " stringByAppendingString:@GAMENAME]
action:@selector(hide:)
keyEquivalent:@"h"];
[[menu addItemWithTitle:@"Hide Others"
action:@selector(hideOtherApplications:)
keyEquivalent:@"h"]
setKeyEquivalentModifierMask:NSAlternateKeyMask | NSCommandKeyMask];
[menu addItemWithTitle:@"Show All"
action:@selector(unhideAllApplications:)
keyEquivalent:@""];
[menu addItem:[NSMenuItem separatorItem]];
[menu addItemWithTitle:[@"Quit " stringByAppendingString:@GAMENAME]
action:@selector(sendExitEvent:)
keyEquivalent:@"q"];
NSMenuItem* menuItem = [NSMenuItem new];
[menuItem setSubmenu:menu];
if ([NSApp respondsToSelector:@selector(setAppleMenu:)])
{
[NSApp performSelector:@selector(setAppleMenu:) withObject:menu];
}
return menuItem;
}
NSMenuItem* CreateEditMenu()
{
NSMenu* menu = [[NSMenu alloc] initWithTitle:@"Edit"];
[menu addItemWithTitle:@"Undo"
action:@selector(undo:)
keyEquivalent:@"z"];
[menu addItemWithTitle:@"Redo"
action:@selector(redo:)
keyEquivalent:@"Z"];
[menu addItem:[NSMenuItem separatorItem]];
[menu addItemWithTitle:@"Cut"
action:@selector(cut:)
keyEquivalent:@"x"];
[menu addItemWithTitle:@"Copy"
action:@selector(copy:)
keyEquivalent:@"c"];
[menu addItemWithTitle:@"Paste"
action:@selector(paste:)
keyEquivalent:@"v"];
[menu addItemWithTitle:@"Delete"
action:@selector(delete:)
keyEquivalent:@""];
[menu addItemWithTitle:@"Select All"
action:@selector(selectAll:)
keyEquivalent:@"a"];
NSMenuItem* menuItem = [NSMenuItem new];
[menuItem setSubmenu:menu];
return menuItem;
}
NSMenuItem* CreateWindowMenu()
{
NSMenu* menu = [[NSMenu alloc] initWithTitle:@"Window"];
[NSApp setWindowsMenu:menu];
[menu addItemWithTitle:@"Minimize"
action:@selector(performMiniaturize:)
keyEquivalent:@"m"];
[menu addItemWithTitle:@"Zoom"
action:@selector(performZoom:)
keyEquivalent:@""];
[menu addItem:[NSMenuItem separatorItem]];
[menu addItemWithTitle:@"Bring All to Front"
action:@selector(arrangeInFront:)
keyEquivalent:@""];
NSMenuItem* menuItem = [NSMenuItem new];
[menuItem setSubmenu:menu];
return menuItem;
}
void CreateMenu()
{
NSMenu* menuBar = [NSMenu new];
[menuBar addItem:CreateApplicationMenu()];
[menuBar addItem:CreateEditMenu()];
[menuBar addItem:CreateWindowMenu()];
[NSApp setMainMenu:menuBar];
}
void ReleaseApplicationController()
{
if (NULL != appCtrl)
{
[NSApp setDelegate:nil];
[NSApp deactivate];
[appCtrl release];
appCtrl = NULL;
}
}
} // unnamed namespace
int main(int argc, char** argv)
{
for (int i = 0; i < argc; ++i)
{
const char* const argument = argv[i];
#if _DEBUG
if (0 == strcmp(argument, "-wait_for_debugger"))
{
NSAlert* alert = [[NSAlert alloc] init];
[alert setMessageText:@GAMENAME];
[alert setInformativeText:@"Waiting for debugger..."];
[alert addButtonWithTitle:@"Continue"];
[alert runModal];
}
#endif // _DEBUG
s_argv.Push(argument);
}
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
[NSApplication sharedApplication];
// The following code isn't mandatory,
// but it enables to run the application without a bundle
if ([NSApp respondsToSelector:@selector(setActivationPolicy:)])
{
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
}
CreateMenu();
atexit(ReleaseApplicationController);
appCtrl = [ApplicationController new];
[NSApp setDelegate:appCtrl];
[NSApp run];
[pool release];
return EXIT_SUCCESS;
}

View file

@ -0,0 +1,177 @@
/*
** i_system.mm
**
**---------------------------------------------------------------------------
** Copyright 2012-2018 Alexey Lysiuk
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "i_common.h"
#include <sys/sysctl.h>
#include "i_system.h"
#include "st_console.h"
#include "v_text.h"
double PerfToSec, PerfToMillisec;
void CalculateCPUSpeed()
{
long long frequency;
size_t size = sizeof frequency;
if (0 == sysctlbyname("machdep.tsc.frequency", &frequency, &size, nullptr, 0) && 0 != frequency)
{
PerfToSec = 1.0 / frequency;
PerfToMillisec = 1000.0 / frequency;
if (!batchrun)
{
Printf("CPU speed: %.0f MHz\n", 0.001 / PerfToMillisec);
}
}
}
void I_SetIWADInfo()
{
FConsoleWindow::GetInstance().SetTitleText();
}
void I_DebugPrint(const char *cp)
{
NSLog(@"%s", cp);
}
void I_PrintStr(const char* const message)
{
FConsoleWindow::GetInstance().AddText(message);
// Strip out any color escape sequences before writing to output
char* const copy = new char[strlen(message) + 1];
const char* srcp = message;
char* dstp = copy;
while ('\0' != *srcp)
{
if (TEXTCOLOR_ESCAPE == *srcp)
{
if ('\0' != srcp[1])
{
srcp += 2;
}
else
{
break;
}
}
else if (0x1d == *srcp || 0x1f == *srcp) // Opening and closing bar character
{
*dstp++ = '-';
++srcp;
}
else if (0x1e == *srcp) // Middle bar character
{
*dstp++ = '=';
++srcp;
}
else
{
*dstp++ = *srcp++;
}
}
*dstp = '\0';
fputs(copy, stdout);
delete[] copy;
fflush(stdout);
}
void Mac_I_FatalError(const char* const message);
void I_ShowFatalError(const char *message)
{
Mac_I_FatalError(message);
}
int I_PickIWad(WadStuff* const wads, const int numwads, const bool showwin, const int defaultiwad)
{
if (!showwin)
{
return defaultiwad;
}
I_SetMainWindowVisible(false);
extern int I_PickIWad_Cocoa(WadStuff*, int, bool, int);
const int result = I_PickIWad_Cocoa(wads, numwads, showwin, defaultiwad);
I_SetMainWindowVisible(true);
return result;
}
void I_PutInClipboard(const char* const string)
{
NSPasteboard* const pasteBoard = [NSPasteboard generalPasteboard];
NSString* const stringType = NSStringPboardType;
NSArray* const types = [NSArray arrayWithObjects:stringType, nil];
NSString* const content = [NSString stringWithUTF8String:string];
[pasteBoard declareTypes:types
owner:nil];
[pasteBoard setString:content
forType:stringType];
}
FString I_GetFromClipboard(bool returnNothing)
{
if (returnNothing)
{
return FString();
}
NSPasteboard* const pasteBoard = [NSPasteboard generalPasteboard];
NSString* const value = [pasteBoard stringForType:NSStringPboardType];
return FString([value UTF8String]);
}
unsigned int I_MakeRNGSeed()
{
return static_cast<unsigned int>(arc4random());
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,90 @@
/*
** sdlglvideo.h
**
**---------------------------------------------------------------------------
** Copyright 2012-2014 Alexey Lysiuk
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
// IMPORTANT NOTE!
// This file was intentially named sdlglvideo.h but it has nothing with SDL
// The name was selected to avoid spreding of changes over the project
// The same applies to SDLGLFB class
// See gl/system/gl_framebuffer.h for details about its usage
#ifndef COCOA_SDLGLVIDEO_H_INCLUDED
#define COCOA_SDLGLVIDEO_H_INCLUDED
#include "v_video.h"
#include "gl/shaders/gl_shader.h"
#include "gl/textures/gl_hwtexture.h"
class SDLGLFB : public DFrameBuffer
{
public:
// This must have the same parameters as the Windows version, even if they are not used!
SDLGLFB(void *hMonitor, int width, int height, int, int, bool fullscreen, bool bgra);
~SDLGLFB();
virtual bool Lock(bool buffered = true);
virtual void Unlock();
virtual bool IsLocked();
virtual bool IsFullscreen();
virtual void SetVSync(bool vsync);
int GetClientWidth();
int GetClientHeight();
virtual int GetTrueHeight() { return GetClientHeight(); }
protected:
int m_Lock;
bool UpdatePending;
static const uint32_t GAMMA_CHANNEL_SIZE = 256;
static const uint32_t GAMMA_CHANNEL_COUNT = 3;
static const uint32_t GAMMA_TABLE_SIZE = GAMMA_CHANNEL_SIZE * GAMMA_CHANNEL_COUNT;
bool m_supportsGamma;
uint16_t m_originalGamma[GAMMA_TABLE_SIZE];
SDLGLFB();
void InitializeState();
void SwapBuffers();
void SetGammaTable(uint16_t* table);
void ResetGammaTable();
};
#endif // COCOA_SDLGLVIDEO_H_INCLUDED

View file

@ -0,0 +1,96 @@
/*
** st_console.h
**
**---------------------------------------------------------------------------
** Copyright 2015 Alexey Lysiuk
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#ifndef COCOA_ST_CONSOLE_INCLUDED
#define COCOA_ST_CONSOLE_INCLUDED
@class NSButton;
@class NSProgressIndicator;
@class NSScrollView;
@class NSTextField;
@class NSTextView;
@class NSView;
@class NSWindow;
struct PalEntry;
class FConsoleWindow
{
public:
static FConsoleWindow& GetInstance();
static void CreateInstance();
static void DeleteInstance();
void Show(bool visible);
void ShowFatalError(const char* message);
void AddText(const char* message);
void SetTitleText();
void SetProgressBar(bool visible);
// FStartupScreen functionality
void Progress(int current, int maximum);
void NetInit(const char* message, int playerCount);
void NetProgress(int count);
void NetDone();
private:
NSWindow* m_window;
NSTextView* m_textView;
NSScrollView* m_scrollView;
NSProgressIndicator* m_progressBar;
NSView* m_netView;
NSTextField* m_netMessageText;
NSTextField* m_netCountText;
NSProgressIndicator* m_netProgressBar;
NSButton* m_netAbortButton;
unsigned int m_characterCount;
int m_netCurPos;
int m_netMaxPos;
FConsoleWindow();
void ExpandTextView(float height);
void AddText(const PalEntry& color, const char* message);
void ScrollTextToBottom();
};
#endif // COCOA_ST_CONSOLE_INCLUDED

View file

@ -0,0 +1,533 @@
/*
** st_console.mm
**
**---------------------------------------------------------------------------
** Copyright 2015 Alexey Lysiuk
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "i_common.h"
#include "d_main.h"
#include "i_system.h"
#include "st_console.h"
#include "v_text.h"
#include "version.h"
#include "i_time.h"
static NSColor* RGB(const uint8_t red, const uint8_t green, const uint8_t blue)
{
return [NSColor colorWithCalibratedRed:red / 255.0f
green:green / 255.0f
blue:blue / 255.0f
alpha:1.0f];
}
static NSColor* RGB(const PalEntry& color)
{
return RGB(color.r, color.g, color.b);
}
static NSColor* RGB(const uint32_t color)
{
return RGB(PalEntry(color));
}
static const CGFloat PROGRESS_BAR_HEIGHT = 18.0f;
static const CGFloat NET_VIEW_HEIGHT = 88.0f;
FConsoleWindow::FConsoleWindow()
: m_window([NSWindow alloc])
, m_textView([NSTextView alloc])
, m_scrollView([NSScrollView alloc])
, m_progressBar(nil)
, m_netView(nil)
, m_netMessageText(nil)
, m_netCountText(nil)
, m_netProgressBar(nil)
, m_netAbortButton(nil)
, m_characterCount(0)
, m_netCurPos(0)
, m_netMaxPos(0)
{
const CGFloat initialWidth = 512.0f;
const CGFloat initialHeight = 384.0f;
const NSRect initialRect = NSMakeRect(0.0f, 0.0f, initialWidth, initialHeight);
[m_textView initWithFrame:initialRect];
[m_textView setEditable:NO];
[m_textView setBackgroundColor:RGB(70, 70, 70)];
[m_textView setMinSize:NSMakeSize(0.0f, initialHeight)];
[m_textView setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
[m_textView setVerticallyResizable:YES];
[m_textView setHorizontallyResizable:NO];
[m_textView setAutoresizingMask:NSViewWidthSizable];
NSTextContainer* const textContainer = [m_textView textContainer];
[textContainer setContainerSize:NSMakeSize(initialWidth, FLT_MAX)];
[textContainer setWidthTracksTextView:YES];
[m_scrollView initWithFrame:initialRect];
[m_scrollView setBorderType:NSNoBorder];
[m_scrollView setHasVerticalScroller:YES];
[m_scrollView setHasHorizontalScroller:NO];
[m_scrollView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
[m_scrollView setDocumentView:m_textView];
NSString* const title = [NSString stringWithFormat:@"%s %s - Console", GAMESIG, GetVersionString()];
[m_window initWithContentRect:initialRect
styleMask:NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask
backing:NSBackingStoreBuffered
defer:NO];
[m_window setMinSize:[m_window frame].size];
[m_window setShowsResizeIndicator:NO];
[m_window setTitle:title];
[m_window center];
[m_window exitAppOnClose];
// Do not allow fullscreen mode for this window
[m_window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary];
[[m_window contentView] addSubview:m_scrollView];
[m_window makeKeyAndOrderFront:nil];
}
static FConsoleWindow* s_instance;
void FConsoleWindow::CreateInstance()
{
assert(NULL == s_instance);
s_instance = new FConsoleWindow;
}
void FConsoleWindow::DeleteInstance()
{
assert(NULL != s_instance);
delete s_instance;
s_instance = NULL;
}
FConsoleWindow& FConsoleWindow::GetInstance()
{
assert(NULL != s_instance);
return *s_instance;
}
void FConsoleWindow::Show(const bool visible)
{
if (visible)
{
[m_window orderFront:nil];
}
else
{
[m_window orderOut:nil];
}
}
void FConsoleWindow::ShowFatalError(const char* const message)
{
SetProgressBar(false);
NetDone();
const CGFloat textViewWidth = [m_scrollView frame].size.width;
ExpandTextView(-32.0f);
NSButton* quitButton = [[NSButton alloc] initWithFrame:NSMakeRect(textViewWidth - 76.0f, 0.0f, 72.0f, 30.0f)];
[quitButton setAutoresizingMask:NSViewMinXMargin];
[quitButton setBezelStyle:NSRoundedBezelStyle];
[quitButton setTitle:@"Quit"];
[quitButton setKeyEquivalent:@"\r"];
[quitButton setTarget:NSApp];
[quitButton setAction:@selector(stopModal)];
NSView* quitPanel = [[NSView alloc] initWithFrame:NSMakeRect(0.0f, 0.0f, textViewWidth, 32.0f)];
[quitPanel setAutoresizingMask:NSViewWidthSizable];
[quitPanel addSubview:quitButton];
[[m_window contentView] addSubview:quitPanel];
[m_window orderFront:nil];
AddText(PalEntry(255, 0, 0), "\nExecution could not continue.\n");
AddText(PalEntry(255, 255, 170), message);
AddText("\n");
ScrollTextToBottom();
[NSApp runModalForWindow:m_window];
}
static const unsigned int THIRTY_FPS = 33; // milliseconds per update
template <typename Function, unsigned int interval = THIRTY_FPS>
struct TimedUpdater
{
explicit TimedUpdater(const Function& function)
{
const unsigned int currentTime = I_msTime();
if (currentTime - m_previousTime > interval)
{
m_previousTime = currentTime;
function();
[[NSRunLoop currentRunLoop] limitDateForMode:NSDefaultRunLoopMode];
}
}
static unsigned int m_previousTime;
};
template <typename Function, unsigned int interval>
unsigned int TimedUpdater<Function, interval>::m_previousTime;
template <typename Function, unsigned int interval = THIRTY_FPS>
static void UpdateTimed(const Function& function)
{
TimedUpdater<Function, interval> dummy(function);
}
void FConsoleWindow::AddText(const char* message)
{
PalEntry color(223, 223, 223);
char buffer[1024] = {};
size_t pos = 0;
bool reset = false;
while (*message != '\0')
{
if ((TEXTCOLOR_ESCAPE == *message && 0 != pos)
|| (pos == sizeof buffer - 1)
|| reset)
{
buffer[pos] = '\0';
pos = 0;
reset = false;
AddText(color, buffer);
}
if (TEXTCOLOR_ESCAPE == *message)
{
const uint8_t* colorID = reinterpret_cast<const uint8_t*>(message) + 1;
if ('\0' == *colorID)
{
break;
}
const EColorRange range = V_ParseFontColor(colorID, CR_UNTRANSLATED, CR_YELLOW);
if (range != CR_UNDEFINED)
{
color = V_LogColorFromColorRange(range);
}
message += 2;
}
else if (0x1d == *message || 0x1f == *message) // Opening and closing bar characters
{
buffer[pos++] = '-';
++message;
}
else if (0x1e == *message) // Middle bar character
{
buffer[pos++] = '=';
++message;
}
else
{
buffer[pos++] = *message++;
}
}
if (0 != pos)
{
buffer[pos] = '\0';
AddText(color, buffer);
}
if ([m_window isVisible])
{
UpdateTimed([&]()
{
[m_textView scrollRangeToVisible:NSMakeRange(m_characterCount, 0)];
});
}
}
void FConsoleWindow::AddText(const PalEntry& color, const char* const message)
{
NSString* const text = [NSString stringWithCString:message
encoding:NSISOLatin1StringEncoding];
NSDictionary* const attributes = [NSDictionary dictionaryWithObjectsAndKeys:
[NSFont systemFontOfSize:14.0f], NSFontAttributeName,
RGB(color), NSForegroundColorAttributeName,
nil];
NSAttributedString* const formattedText =
[[NSAttributedString alloc] initWithString:text
attributes:attributes];
[[m_textView textStorage] appendAttributedString:formattedText];
m_characterCount += [text length];
}
void FConsoleWindow::ScrollTextToBottom()
{
[m_textView scrollRangeToVisible:NSMakeRange(m_characterCount, 0)];
[[NSRunLoop currentRunLoop] limitDateForMode:NSDefaultRunLoopMode];
}
void FConsoleWindow::SetTitleText()
{
static const CGFloat TITLE_TEXT_HEIGHT = 32.0f;
NSRect textViewFrame = [m_scrollView frame];
textViewFrame.size.height -= TITLE_TEXT_HEIGHT;
[m_scrollView setFrame:textViewFrame];
const NSRect titleTextRect = NSMakeRect(
0.0f,
textViewFrame.origin.y + textViewFrame.size.height,
textViewFrame.size.width,
TITLE_TEXT_HEIGHT);
// Temporary solution for the same foreground and background colors
// It's used in graphical startup screen, with Hexen style in particular
// Native OS X backend doesn't implement this yet
if (DoomStartupInfo.FgColor == DoomStartupInfo.BkColor)
{
DoomStartupInfo.FgColor = ~DoomStartupInfo.FgColor;
}
NSTextField* titleText = [[NSTextField alloc] initWithFrame:titleTextRect];
[titleText setStringValue:[NSString stringWithCString:DoomStartupInfo.Name
encoding:NSISOLatin1StringEncoding]];
[titleText setAlignment:NSCenterTextAlignment];
[titleText setTextColor:RGB(DoomStartupInfo.FgColor)];
[titleText setBackgroundColor:RGB(DoomStartupInfo.BkColor)];
[titleText setFont:[NSFont fontWithName:@"Trebuchet MS Bold" size:18.0f]];
[titleText setAutoresizingMask:NSViewWidthSizable | NSViewMinYMargin];
[titleText setSelectable:NO];
[titleText setBordered:NO];
[[m_window contentView] addSubview:titleText];
}
void FConsoleWindow::SetProgressBar(const bool visible)
{
if ( (!visible && nil == m_progressBar)
|| (visible && nil != m_progressBar))
{
return;
}
if (visible)
{
ExpandTextView(-PROGRESS_BAR_HEIGHT);
static const CGFloat PROGRESS_BAR_X = 2.0f;
const NSRect PROGRESS_BAR_RECT = NSMakeRect(
PROGRESS_BAR_X, 0.0f,
[m_window frame].size.width - PROGRESS_BAR_X * 2, 16.0f);
m_progressBar = [[NSProgressIndicator alloc] initWithFrame:PROGRESS_BAR_RECT];
[m_progressBar setIndeterminate:NO];
[m_progressBar setAutoresizingMask:NSViewWidthSizable];
[[m_window contentView] addSubview:m_progressBar];
}
else
{
ExpandTextView(PROGRESS_BAR_HEIGHT);
[m_progressBar removeFromSuperview];
[m_progressBar release];
m_progressBar = nil;
}
}
void FConsoleWindow::ExpandTextView(const float height)
{
NSRect textFrame = [m_scrollView frame];
textFrame.origin.y -= height;
textFrame.size.height += height;
[m_scrollView setFrame:textFrame];
}
void FConsoleWindow::Progress(const int current, const int maximum)
{
if (nil == m_progressBar)
{
return;
}
UpdateTimed([&]()
{
[m_progressBar setMaxValue:maximum];
[m_progressBar setDoubleValue:current];
});
}
void FConsoleWindow::NetInit(const char* const message, const int playerCount)
{
if (nil == m_netView)
{
SetProgressBar(false);
ExpandTextView(-NET_VIEW_HEIGHT);
// Message like 'Waiting for players' or 'Contacting host'
m_netMessageText = [[NSTextField alloc] initWithFrame:NSMakeRect(12.0f, 64.0f, 400.0f, 16.0f)];
[m_netMessageText setAutoresizingMask:NSViewWidthSizable];
[m_netMessageText setDrawsBackground:NO];
[m_netMessageText setSelectable:NO];
[m_netMessageText setBordered:NO];
// Text with connected/total players count
m_netCountText = [[NSTextField alloc] initWithFrame:NSMakeRect(428.0f, 64.0f, 72.0f, 16.0f)];
[m_netCountText setAutoresizingMask:NSViewMinXMargin];
[m_netCountText setAlignment:NSRightTextAlignment];
[m_netCountText setDrawsBackground:NO];
[m_netCountText setSelectable:NO];
[m_netCountText setBordered:NO];
// Connection progress
m_netProgressBar = [[NSProgressIndicator alloc] initWithFrame:NSMakeRect(12.0f, 40.0f, 488.0f, 16.0f)];
[m_netProgressBar setAutoresizingMask:NSViewWidthSizable];
[m_netProgressBar setMaxValue:playerCount];
if (0 == playerCount)
{
// Joining game
[m_netProgressBar setIndeterminate:YES];
[m_netProgressBar startAnimation:nil];
}
else
{
// Hosting game
[m_netProgressBar setIndeterminate:NO];
}
// Cancel network game button
m_netAbortButton = [[NSButton alloc] initWithFrame:NSMakeRect(432.0f, 8.0f, 72.0f, 28.0f)];
[m_netAbortButton setAutoresizingMask:NSViewMinXMargin];
[m_netAbortButton setBezelStyle:NSRoundedBezelStyle];
[m_netAbortButton setTitle:@"Cancel"];
[m_netAbortButton setKeyEquivalent:@"\r"];
[m_netAbortButton setTarget:[NSApp delegate]];
[m_netAbortButton setAction:@selector(sendExitEvent:)];
// Panel for controls above
m_netView = [[NSView alloc] initWithFrame:NSMakeRect(0.0f, 0.0f, 512.0f, NET_VIEW_HEIGHT)];
[m_netView setAutoresizingMask:NSViewWidthSizable];
[m_netView addSubview:m_netMessageText];
[m_netView addSubview:m_netCountText];
[m_netView addSubview:m_netProgressBar];
[m_netView addSubview:m_netAbortButton];
NSRect windowRect = [m_window frame];
windowRect.origin.y -= NET_VIEW_HEIGHT;
windowRect.size.height += NET_VIEW_HEIGHT;
[m_window setFrame:windowRect display:YES];
[[m_window contentView] addSubview:m_netView];
ScrollTextToBottom();
}
[m_netMessageText setStringValue:[NSString stringWithUTF8String:message]];
m_netCurPos = 0;
m_netMaxPos = playerCount;
NetProgress(1); // You always know about yourself
}
void FConsoleWindow::NetProgress(const int count)
{
if (0 == count)
{
++m_netCurPos;
}
else
{
m_netCurPos = count;
}
if (nil == m_netView)
{
return;
}
if (m_netMaxPos > 1)
{
[m_netCountText setStringValue:[NSString stringWithFormat:@"%d / %d", m_netCurPos, m_netMaxPos]];
[m_netProgressBar setDoubleValue:MIN(m_netCurPos, m_netMaxPos)];
}
}
void FConsoleWindow::NetDone()
{
if (nil != m_netView)
{
ExpandTextView(NET_VIEW_HEIGHT);
[m_netView removeFromSuperview];
[m_netView release];
m_netView = nil;
// Released by m_netView
m_netMessageText = nil;
m_netCountText = nil;
m_netProgressBar = nil;
m_netAbortButton = nil;
}
}

View file

@ -0,0 +1,175 @@
/*
** st_start.mm
**
**---------------------------------------------------------------------------
** Copyright 2015 Alexey Lysiuk
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include <unistd.h>
#import <Foundation/NSRunLoop.h>
#include "c_cvars.h"
#include "doomtype.h"
#include "st_console.h"
#include "st_start.h"
#include "doomerrors.h"
FStartupScreen *StartScreen;
CUSTOM_CVAR(Int, showendoom, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (self < 0)
{
self = 0;
}
else if (self > 2)
{
self = 2;
}
}
// ---------------------------------------------------------------------------
FBasicStartupScreen::FBasicStartupScreen(int maxProgress, bool showBar)
: FStartupScreen(maxProgress)
{
FConsoleWindow& consoleWindow = FConsoleWindow::GetInstance();
consoleWindow.SetProgressBar(true);
#if 0
// Testing code, please do not remove
consoleWindow.AddText("----------------------------------------------------------------\n");
consoleWindow.AddText("1234567890 !@#$%^&*() ,<.>/?;:'\" [{]}\\| `~-_=+ "
"This is very very very long message needed to trigger word wrapping...\n\n");
consoleWindow.AddText("Multiline...\n\tmessage...\n\t\twith...\n\t\t\ttabs.\n\n");
consoleWindow.AddText(TEXTCOLOR_BRICK "TEXTCOLOR_BRICK\n" TEXTCOLOR_TAN "TEXTCOLOR_TAN\n");
consoleWindow.AddText(TEXTCOLOR_GRAY "TEXTCOLOR_GRAY & TEXTCOLOR_GREY\n");
consoleWindow.AddText(TEXTCOLOR_GREEN "TEXTCOLOR_GREEN\n" TEXTCOLOR_BROWN "TEXTCOLOR_BROWN\n");
consoleWindow.AddText(TEXTCOLOR_GOLD "TEXTCOLOR_GOLD\n" TEXTCOLOR_RED "TEXTCOLOR_RED\n");
consoleWindow.AddText(TEXTCOLOR_BLUE "TEXTCOLOR_BLUE\n" TEXTCOLOR_ORANGE "TEXTCOLOR_ORANGE\n");
consoleWindow.AddText(TEXTCOLOR_WHITE "TEXTCOLOR_WHITE\n" TEXTCOLOR_YELLOW "TEXTCOLOR_YELLOW\n");
consoleWindow.AddText(TEXTCOLOR_UNTRANSLATED "TEXTCOLOR_UNTRANSLATED\n");
consoleWindow.AddText(TEXTCOLOR_BLACK "TEXTCOLOR_BLACK\n" TEXTCOLOR_LIGHTBLUE "TEXTCOLOR_LIGHTBLUE\n");
consoleWindow.AddText(TEXTCOLOR_CREAM "TEXTCOLOR_CREAM\n" TEXTCOLOR_OLIVE "TEXTCOLOR_OLIVE\n");
consoleWindow.AddText(TEXTCOLOR_DARKGREEN "TEXTCOLOR_DARKGREEN\n" TEXTCOLOR_DARKRED "TEXTCOLOR_DARKRED\n");
consoleWindow.AddText(TEXTCOLOR_DARKBROWN "TEXTCOLOR_DARKBROWN\n" TEXTCOLOR_PURPLE "TEXTCOLOR_PURPLE\n");
consoleWindow.AddText(TEXTCOLOR_DARKGRAY "TEXTCOLOR_DARKGRAY\n" TEXTCOLOR_CYAN "TEXTCOLOR_CYAN\n");
consoleWindow.AddText(TEXTCOLOR_ICE "TEXTCOLOR_ICE\n" TEXTCOLOR_FIRE "TEXTCOLOR_FIRE\n");
consoleWindow.AddText(TEXTCOLOR_SAPPHIRE "TEXTCOLOR_SAPPHIRE\n" TEXTCOLOR_TEAL "TEXTCOLOR_TEAL\n");
consoleWindow.AddText(TEXTCOLOR_NORMAL "TEXTCOLOR_NORMAL\n" TEXTCOLOR_BOLD "TEXTCOLOR_BOLD\n");
consoleWindow.AddText(TEXTCOLOR_CHAT "TEXTCOLOR_CHAT\n" TEXTCOLOR_TEAMCHAT "TEXTCOLOR_TEAMCHAT\n");
consoleWindow.AddText("----------------------------------------------------------------\n");
#endif // _DEBUG
}
FBasicStartupScreen::~FBasicStartupScreen()
{
FConsoleWindow::GetInstance().SetProgressBar(false);
}
void FBasicStartupScreen::Progress()
{
if (CurPos < MaxPos)
{
++CurPos;
}
FConsoleWindow::GetInstance().Progress(CurPos, MaxPos);
}
void FBasicStartupScreen::NetInit(const char* const message, const int playerCount)
{
FConsoleWindow::GetInstance().NetInit(message, playerCount);
}
void FBasicStartupScreen::NetProgress(const int count)
{
FConsoleWindow::GetInstance().NetProgress(count);
}
void FBasicStartupScreen::NetMessage(const char* const format, ...)
{
va_list args;
va_start(args, format);
FString message;
message.VFormat(format, args);
va_end(args);
Printf("%s\n", message.GetChars());
}
void FBasicStartupScreen::NetDone()
{
FConsoleWindow::GetInstance().NetDone();
}
bool FBasicStartupScreen::NetLoop(bool (*timerCallback)(void*), void* const userData)
{
while (true)
{
if (timerCallback(userData))
{
break;
}
[[NSRunLoop currentRunLoop] limitDateForMode:NSDefaultRunLoopMode];
// Do not poll to often
usleep(50000);
}
return true;
}
// ---------------------------------------------------------------------------
FStartupScreen *FStartupScreen::CreateInstance(const int maxProgress)
{
return new FBasicStartupScreen(maxProgress, true);
}
// ---------------------------------------------------------------------------
void ST_Endoom()
{
throw CExitEvent(0);
}

View file

@ -0,0 +1,156 @@
//-----------------------------------------------------------------------------
//
// Copyright 1993-1996 id Software
// Copyright 1999-2016 Randy Heit
//
// 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.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//-----------------------------------------------------------------------------
//
#include <fnmatch.h>
#ifdef __APPLE__
#include <AvailabilityMacros.h>
#endif // __APPLE__
#include "cmdlib.h"
#include "d_protocol.h"
#include "i_system.h"
#include "gameconfigfile.h"
#include "x86.h"
void I_Tactile(int /*on*/, int /*off*/, int /*total*/)
{
}
static ticcmd_t emptycmd;
ticcmd_t *I_BaseTiccmd()
{
return &emptycmd;
}
//
// I_Init
//
void I_Init()
{
extern void CalculateCPUSpeed();
CheckCPUID(&CPU);
CalculateCPUSpeed();
DumpCPUInfo(&CPU);
}
bool I_WriteIniFailed()
{
printf("The config file %s could not be saved:\n%s\n", GameConfig->GetPathName(), strerror(errno));
return false; // return true to retry
}
static const char *pattern;
#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED < 1080
static int matchfile(struct dirent *ent)
#else
static int matchfile(const struct dirent *ent)
#endif
{
return fnmatch(pattern, ent->d_name, FNM_NOESCAPE) == 0;
}
void *I_FindFirst(const char *const filespec, findstate_t *const fileinfo)
{
FString dir;
const char *const slash = strrchr(filespec, '/');
if (slash)
{
pattern = slash + 1;
dir = FString(filespec, slash - filespec + 1);
fileinfo->path = dir;
}
else
{
pattern = filespec;
dir = ".";
}
fileinfo->current = 0;
fileinfo->count = scandir(dir.GetChars(), &fileinfo->namelist, matchfile, alphasort);
if (fileinfo->count > 0)
{
return fileinfo;
}
return (void *)-1;
}
int I_FindNext(void *const handle, findstate_t *const fileinfo)
{
findstate_t *const state = static_cast<findstate_t *>(handle);
if (state->current < fileinfo->count)
{
return ++state->current < fileinfo->count ? 0 : -1;
}
return -1;
}
int I_FindClose(void *const handle)
{
findstate_t *const state = static_cast<findstate_t *>(handle);
if (handle != (void *)-1 && state->count > 0)
{
for (int i = 0; i < state->count; ++i)
{
free(state->namelist[i]);
}
free(state->namelist);
state->namelist = nullptr;
state->count = 0;
}
return 0;
}
int I_FindAttr(findstate_t *const fileinfo)
{
dirent *const ent = fileinfo->namelist[fileinfo->current];
const FString path = fileinfo->path + ent->d_name;
bool isdir;
if (DirEntryExists(path, &isdir))
{
return isdir ? FA_DIREC : 0;
}
return 0;
}
TArray<FString> I_GetGogPaths()
{
// GOG's Doom games are Windows only at the moment
return TArray<FString>();
}

View file

@ -0,0 +1,238 @@
/*
** i_specialpaths.mm
** Gets special system folders where data should be stored. (macOS version)
**
**---------------------------------------------------------------------------
** Copyright 2013-2016 Randy Heit
** Copyright 2016 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include <CoreServices/CoreServices.h>
#include "cmdlib.h"
#include "m_misc.h"
#include "version.h" // for GAMENAME
//===========================================================================
//
// M_GetAppDataPath macOS
//
// Returns the path for the AppData folder.
//
//===========================================================================
FString M_GetAppDataPath(bool create)
{
FString path;
char pathstr[PATH_MAX];
FSRef folder;
if (noErr == FSFindFolder(kUserDomain, kApplicationSupportFolderType, create ? kCreateFolder : 0, &folder) &&
noErr == FSRefMakePath(&folder, (UInt8*)pathstr, PATH_MAX))
{
path = pathstr;
}
else
{
path = progdir;
}
path += "/" GAMENAMELOWERCASE;
if (create) CreatePath(path);
return path;
}
//===========================================================================
//
// M_GetCachePath macOS
//
// Returns the path for cache GL nodes.
//
//===========================================================================
FString M_GetCachePath(bool create)
{
FString path;
char pathstr[PATH_MAX];
FSRef folder;
if (noErr == FSFindFolder(kUserDomain, kApplicationSupportFolderType, create ? kCreateFolder : 0, &folder) &&
noErr == FSRefMakePath(&folder, (UInt8*)pathstr, PATH_MAX))
{
path = pathstr;
}
else
{
path = progdir;
}
path += "/zdoom/cache";
if (create) CreatePath(path);
return path;
}
//===========================================================================
//
// M_GetAutoexecPath macOS
//
// Returns the expected location of autoexec.cfg.
//
//===========================================================================
FString M_GetAutoexecPath()
{
FString path;
char cpath[PATH_MAX];
FSRef folder;
if (noErr == FSFindFolder(kUserDomain, kDocumentsFolderType, kCreateFolder, &folder) &&
noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX))
{
path << cpath << "/" GAME_DIR "/autoexec.cfg";
}
return path;
}
//===========================================================================
//
// M_GetCajunPath macOS
//
// Returns the location of the Cajun Bot definitions.
//
//===========================================================================
FString M_GetCajunPath(const char *botfilename)
{
FString path;
// Just copies the Windows code. Should this be more Mac-specific?
path << progdir << "zcajun/" << botfilename;
if (!FileExists(path))
{
path = "";
}
return path;
}
//===========================================================================
//
// M_GetConfigPath macOS
//
// Returns the path to the config file. On Windows, this can vary for reading
// vs writing. i.e. If $PROGDIR/zdoom-<user>.ini does not exist, it will try
// to read from $PROGDIR/zdoom.ini, but it will never write to zdoom.ini.
//
//===========================================================================
FString M_GetConfigPath(bool for_reading)
{
char cpath[PATH_MAX];
FSRef folder;
if (noErr == FSFindFolder(kUserDomain, kPreferencesFolderType, kCreateFolder, &folder) &&
noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX))
{
FString path;
path << cpath << "/" GAMENAMELOWERCASE ".ini";
return path;
}
// Ungh.
return GAMENAMELOWERCASE ".ini";
}
//===========================================================================
//
// M_GetScreenshotsPath macOS
//
// Returns the path to the default screenshots directory.
//
//===========================================================================
FString M_GetScreenshotsPath()
{
FString path;
char cpath[PATH_MAX];
FSRef folder;
if (noErr == FSFindFolder(kUserDomain, kDocumentsFolderType, kCreateFolder, &folder) &&
noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX))
{
path << cpath << "/" GAME_DIR "/Screenshots/";
}
else
{
path = "~/";
}
return path;
}
//===========================================================================
//
// M_GetSavegamesPath macOS
//
// Returns the path to the default save games directory.
//
//===========================================================================
FString M_GetSavegamesPath()
{
FString path;
char cpath[PATH_MAX];
FSRef folder;
if (noErr == FSFindFolder(kUserDomain, kDocumentsFolderType, kCreateFolder, &folder) &&
noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX))
{
path << cpath << "/" GAME_DIR "/Savegames/";
}
return path;
}
//===========================================================================
//
// M_GetDocumentsPath Unix
//
// Returns the path to the default documents directory.
//
//===========================================================================
FString M_GetDocumentsPath()
{
FString path;
char cpath[PATH_MAX];
FSRef folder;
if (noErr == FSFindFolder(kUserDomain, kDocumentsFolderType, kCreateFolder, &folder) &&
noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX))
{
path << cpath << "/" GAME_DIR "/";
}
return path;
}

View file

@ -0,0 +1,471 @@
/*
** iwadpicker_cocoa.mm
**
** Implements Mac OS X native IWAD Picker.
**
**---------------------------------------------------------------------------
** Copyright 2010 Braden Obrzut
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "cmdlib.h"
#include "d_main.h"
#include "version.h"
#include "c_cvars.h"
#include "m_argv.h"
#include "m_misc.h"
#include "gameconfigfile.h"
#include "doomerrors.h"
#include <Cocoa/Cocoa.h>
#include <wordexp.h>
#include <unistd.h>
CVAR(String, osx_additional_parameters, "", CVAR_ARCHIVE | CVAR_NOSET | CVAR_GLOBALCONFIG);
enum
{
COLUMN_IWAD,
COLUMN_GAME,
NUM_COLUMNS
};
static const char* const tableHeaders[NUM_COLUMNS] = { "IWAD", "Game" };
// Class to convert the IWAD data into a form that Cocoa can use.
@interface IWADTableData : NSObject<NSTableViewDataSource>
{
NSMutableArray *data;
}
- (void)dealloc;
- (IWADTableData *)init:(WadStuff *) wads num:(int) numwads;
- (int)numberOfRowsInTableView:(NSTableView *)aTableView;
- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex;
@end
@implementation IWADTableData
- (void)dealloc
{
[data release];
[super dealloc];
}
- (IWADTableData *)init:(WadStuff *) wads num:(int) numwads
{
data = [[NSMutableArray alloc] initWithCapacity:numwads];
for(int i = 0;i < numwads;i++)
{
NSMutableDictionary *record = [[NSMutableDictionary alloc] initWithCapacity:NUM_COLUMNS];
const char* filename = strrchr(wads[i].Path, '/');
if(filename == NULL)
filename = wads[i].Path;
else
filename++;
[record setObject:[NSString stringWithUTF8String:filename] forKey:[NSString stringWithUTF8String:tableHeaders[COLUMN_IWAD]]];
[record setObject:[NSString stringWithUTF8String:wads[i].Name] forKey:[NSString stringWithUTF8String:tableHeaders[COLUMN_GAME]]];
[data addObject:record];
[record release];
}
return self;
}
- (int)numberOfRowsInTableView:(NSTableView *)aTableView
{
return [data count];
}
- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
{
NSParameterAssert(rowIndex >= 0 && (unsigned int) rowIndex < [data count]);
NSMutableDictionary *record = [data objectAtIndex:rowIndex];
return [record objectForKey:[aTableColumn identifier]];
}
@end
static NSDictionary* GetKnownFileTypes()
{
return [NSDictionary dictionaryWithObjectsAndKeys:
@"-file" , @"wad",
@"-file" , @"pk3",
@"-file" , @"zip",
@"-file" , @"pk7",
@"-file" , @"7z",
@"-deh" , @"deh",
@"-bex" , @"bex",
@"-exec" , @"cfg",
@"-playdemo", @"lmp",
nil];
}
static NSArray* GetKnownExtensions()
{
return [GetKnownFileTypes() allKeys];
}
@interface NSMutableString(AppendKnownFileType)
- (void)appendKnownFileType:(NSString *)filePath;
@end
@implementation NSMutableString(AppendKnownFileType)
- (void)appendKnownFileType:(NSString *)filePath
{
NSString* extension = [[filePath pathExtension] lowercaseString];
NSString* parameter = [GetKnownFileTypes() objectForKey:extension];
if (nil == parameter)
{
return;
}
[self appendFormat:@"%@ \"%@\" ", parameter, filePath];
}
@end
// So we can listen for button actions and such we need to have an Obj-C class.
@interface IWADPicker : NSObject
{
NSApplication *app;
NSWindow *window;
NSButton *okButton;
NSButton *cancelButton;
NSButton *browseButton;
NSTextField *parametersTextField;
bool cancelled;
}
- (void)buttonPressed:(id) sender;
- (void)browseButtonPressed:(id) sender;
- (void)doubleClicked:(id) sender;
- (void)makeLabel:(NSTextField *)label withString:(const char*) str;
- (int)pickIWad:(WadStuff *)wads num:(int) numwads showWindow:(bool) showwin defaultWad:(int) defaultiwad;
- (NSString*)commandLineParameters;
- (void)menuActionSent:(NSNotification*)notification;
@end
@implementation IWADPicker
- (void)buttonPressed:(id) sender
{
if(sender == cancelButton)
cancelled = true;
[window orderOut:self];
[app stopModal];
}
- (void)browseButtonPressed:(id) sender
{
NSOpenPanel* openPanel = [NSOpenPanel openPanel];
[openPanel setAllowsMultipleSelection:YES];
[openPanel setCanChooseFiles:YES];
[openPanel setCanChooseDirectories:YES];
[openPanel setResolvesAliases:YES];
[openPanel setAllowedFileTypes:GetKnownExtensions()];
if (NSOKButton == [openPanel runModal])
{
NSArray* files = [openPanel URLs];
NSMutableString* parameters = [NSMutableString string];
for (NSUInteger i = 0, ei = [files count]; i < ei; ++i)
{
NSString* filePath = [[files objectAtIndex:i] path];
BOOL isDirectory = false;
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath isDirectory:&isDirectory] && isDirectory)
{
[parameters appendFormat:@"-file \"%@\" ", filePath];
}
else
{
[parameters appendKnownFileType:filePath];
}
}
if ([parameters length] > 0)
{
NSString* newParameters = [parametersTextField stringValue];
if ([newParameters length] > 0
&& NO == [newParameters hasSuffix:@" "])
{
newParameters = [newParameters stringByAppendingString:@" "];
}
newParameters = [newParameters stringByAppendingString:parameters];
[parametersTextField setStringValue: newParameters];
}
}
}
- (void)doubleClicked:(id) sender
{
if ([sender clickedRow] >= 0)
{
[window orderOut:self];
[app stopModal];
}
}
// Apparently labels in Cocoa are uneditable text fields, so lets make this a
// little more automated.
- (void)makeLabel:(NSTextField *)label withString:(const char*) str
{
[label setStringValue:[NSString stringWithUTF8String:str]];
[label setBezeled:NO];
[label setDrawsBackground:NO];
[label setEditable:NO];
[label setSelectable:NO];
}
- (int)pickIWad:(WadStuff *)wads num:(int) numwads showWindow:(bool) showwin defaultWad:(int) defaultiwad
{
cancelled = false;
app = [NSApplication sharedApplication];
id windowTitle = [NSString stringWithFormat:@"%s %s", GAMENAME, GetVersionString()];
NSRect frame = NSMakeRect(0, 0, 440, 450);
window = [[NSWindow alloc] initWithContentRect:frame styleMask:NSTitledWindowMask backing:NSBackingStoreBuffered defer:NO];
[window setTitle:windowTitle];
NSTextField *description = [[NSTextField alloc] initWithFrame:NSMakeRect(18, 384, 402, 50)];
[self makeLabel:description withString:GAMENAME " found more than one IWAD\nSelect from the list below to determine which one to use:"];
[[window contentView] addSubview:description];
[description release];
NSScrollView *iwadScroller = [[NSScrollView alloc] initWithFrame:NSMakeRect(20, 135, 402, 256)];
NSTableView *iwadTable = [[NSTableView alloc] initWithFrame:[iwadScroller bounds]];
IWADTableData *tableData = [[IWADTableData alloc] init:wads num:numwads];
for(int i = 0;i < NUM_COLUMNS;i++)
{
NSTableColumn *column = [[NSTableColumn alloc] initWithIdentifier:[NSString stringWithUTF8String:tableHeaders[i]]];
[[column headerCell] setStringValue:[column identifier]];
if(i == 0)
[column setMaxWidth:110];
[column setEditable:NO];
[column setResizingMask:NSTableColumnAutoresizingMask];
[iwadTable addTableColumn:column];
[column release];
}
[iwadScroller setDocumentView:iwadTable];
[iwadScroller setHasVerticalScroller:YES];
[iwadTable setDataSource:tableData];
[iwadTable sizeToFit];
[iwadTable setDoubleAction:@selector(doubleClicked:)];
[iwadTable setTarget:self];
NSIndexSet *selection = [[NSIndexSet alloc] initWithIndex:defaultiwad];
[iwadTable selectRowIndexes:selection byExtendingSelection:NO];
[selection release];
[iwadTable scrollRowToVisible:defaultiwad];
[[window contentView] addSubview:iwadScroller];
[iwadTable release];
[iwadScroller release];
NSTextField *additionalParametersLabel = [[NSTextField alloc] initWithFrame:NSMakeRect(18, 108, 144, 17)];
[self makeLabel:additionalParametersLabel withString:"Additional Parameters:"];
[[window contentView] addSubview:additionalParametersLabel];
parametersTextField = [[NSTextField alloc] initWithFrame:NSMakeRect(20, 48, 402, 54)];
[parametersTextField setStringValue:[NSString stringWithUTF8String:osx_additional_parameters]];
[[window contentView] addSubview:parametersTextField];
// Doesn't look like the SDL version implements this so lets not show it.
/*NSButton *dontAsk = [[NSButton alloc] initWithFrame:NSMakeRect(18, 18, 178, 18)];
[dontAsk setTitle:[NSString stringWithCString:"Don't ask me this again"]];
[dontAsk setButtonType:NSSwitchButton];
[dontAsk setState:(showwin ? NSOffState : NSOnState)];
[[window contentView] addSubview:dontAsk];*/
okButton = [[NSButton alloc] initWithFrame:NSMakeRect(236, 8, 96, 32)];
[okButton setTitle:@"OK"];
[okButton setBezelStyle:NSRoundedBezelStyle];
[okButton setAction:@selector(buttonPressed:)];
[okButton setTarget:self];
[okButton setKeyEquivalent:@"\r"];
[[window contentView] addSubview:okButton];
cancelButton = [[NSButton alloc] initWithFrame:NSMakeRect(332, 8, 96, 32)];
[cancelButton setTitle:@"Cancel"];
[cancelButton setBezelStyle:NSRoundedBezelStyle];
[cancelButton setAction:@selector(buttonPressed:)];
[cancelButton setTarget:self];
[cancelButton setKeyEquivalent:@"\033"];
[[window contentView] addSubview:cancelButton];
browseButton = [[NSButton alloc] initWithFrame:NSMakeRect(14, 8, 96, 32)];
[browseButton setTitle:@"Browse..."];
[browseButton setBezelStyle:NSRoundedBezelStyle];
[browseButton setAction:@selector(browseButtonPressed:)];
[browseButton setTarget:self];
[[window contentView] addSubview:browseButton];
NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(menuActionSent:) name:NSMenuDidSendActionNotification object:nil];
[window center];
[app runModalForWindow:window];
[center removeObserver:self name:NSMenuDidSendActionNotification object:nil];
[window release];
[okButton release];
[cancelButton release];
[browseButton release];
return cancelled ? -1 : [iwadTable selectedRow];
}
- (NSString*)commandLineParameters
{
return [parametersTextField stringValue];
}
- (void)menuActionSent:(NSNotification*)notification
{
NSDictionary* userInfo = [notification userInfo];
NSMenuItem* menuItem = [userInfo valueForKey:@"MenuItem"];
if ( @selector(terminate:) == [menuItem action] )
{
throw CExitEvent(0);
}
}
@end
EXTERN_CVAR(String, defaultiwad)
static NSString* GetArchitectureString()
{
#ifdef __i386__
return @"i386";
#elif defined __x86_64__
return @"x86_64";
#endif
}
static void RestartWithParameters(const WadStuff& wad, NSString* parameters)
{
assert(nil != parameters);
defaultiwad = wad.Name;
GameConfig->DoGameSetup("Doom");
M_SaveDefaults(NULL);
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
@try
{
NSString* executablePath = [NSString stringWithUTF8String:Args->GetArg(0)];
NSMutableArray* const arguments = [[NSMutableArray alloc] init];
// The following value shoud be equal to NSAppKitVersionNumber10_5
// It's hard-coded in order to build with earlier SDKs
const bool canSelectArchitecture = NSAppKitVersionNumber >= 949;
if (canSelectArchitecture)
{
[arguments addObject:@"-arch"];
[arguments addObject:GetArchitectureString()];
[arguments addObject:executablePath];
executablePath = @"/usr/bin/arch";
}
[arguments addObject:@"-iwad"];
[arguments addObject:[NSString stringWithUTF8String:wad.Path]];
for (int i = 1, count = Args->NumArgs(); i < count; ++i)
{
NSString* currentParameter = [NSString stringWithUTF8String:Args->GetArg(i)];
[arguments addObject:currentParameter];
}
wordexp_t expansion = {};
if (0 == wordexp([parameters UTF8String], &expansion, 0))
{
for (size_t i = 0; i < expansion.we_wordc; ++i)
{
NSString* argumentString = [NSString stringWithCString:expansion.we_wordv[i]
encoding:NSUTF8StringEncoding];
[arguments addObject:argumentString];
}
wordfree(&expansion);
}
[NSTask launchedTaskWithLaunchPath:executablePath
arguments:arguments];
_exit(0); // to avoid atexit()'s functions
}
@catch (NSException* e)
{
NSLog(@"Cannot restart: %@", [e reason]);
}
[pool release];
}
// Simple wrapper so we can call this from outside.
int I_PickIWad_Cocoa (WadStuff *wads, int numwads, bool showwin, int defaultiwad)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
IWADPicker *picker = [IWADPicker alloc];
int ret = [picker pickIWad:wads num:numwads showWindow:showwin defaultWad:defaultiwad];
NSString* parametersToAppend = [picker commandLineParameters];
osx_additional_parameters = [parametersToAppend UTF8String];
if (ret >= 0)
{
if (0 != [parametersToAppend length])
{
RestartWithParameters(wads[ret], parametersToAppend);
}
}
[pool release];
return ret;
}

View file

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string>zdoom.icns</string>
<key>CFBundleIdentifier</key>
<string>org.drdteam.gzdoom</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>GZDoom</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>Development Version</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.action-games</string>
<key>LSMinimumSystemVersion</key>
<string>10.7</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeName</key>
<string>Doom Resource File</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleTypeExtensions</key>
<array>
<string>wad</string>
<string>pk3</string>
<string>zip</string>
<string>pk7</string>
<string>7z</string>
<string>iwad</string>
<string>ipk3</string>
<string>ipk7</string>
</array>
</dict>
</array>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true/>
</dict>
</plist>

View file

@ -0,0 +1,428 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/param.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#ifdef __linux__
#include <sys/prctl.h>
#ifndef PR_SET_PTRACER
#define PR_SET_PTRACER 0x59616d61
#endif
#elif defined (__APPLE__) || defined (__FreeBSD__) || defined(__OpenBSD__)
#include <signal.h>
#endif
static const char crash_switch[] = "--cc-handle-crash";
static const char fatal_err[] = "\n\n*** Fatal Error ***\n";
static const char pipe_err[] = "!!! Failed to create pipe\n";
static const char fork_err[] = "!!! Failed to fork debug process\n";
static const char exec_err[] = "!!! Failed to exec debug process\n";
static char argv0[PATH_MAX];
static char altstack[SIGSTKSZ];
static struct {
int signum;
pid_t pid;
int has_siginfo;
siginfo_t siginfo;
char buf[4096];
} crash_info;
static const struct {
const char *name;
int signum;
} signals[] = {
{ "Segmentation fault", SIGSEGV },
{ "Illegal instruction", SIGILL },
{ "FPU exception", SIGFPE },
{ "System BUS error", SIGBUS },
{ NULL, 0 }
};
static const struct {
int code;
const char *name;
} sigill_codes[] = {
#ifndef __FreeBSD__
{ ILL_ILLOPC, "Illegal opcode" },
{ ILL_ILLOPN, "Illegal operand" },
{ ILL_ILLADR, "Illegal addressing mode" },
{ ILL_ILLTRP, "Illegal trap" },
{ ILL_PRVOPC, "Privileged opcode" },
{ ILL_PRVREG, "Privileged register" },
{ ILL_COPROC, "Coprocessor error" },
{ ILL_BADSTK, "Internal stack error" },
#endif
{ 0, NULL }
};
static const struct {
int code;
const char *name;
} sigfpe_codes[] = {
{ FPE_INTDIV, "Integer divide by zero" },
{ FPE_INTOVF, "Integer overflow" },
{ FPE_FLTDIV, "Floating point divide by zero" },
{ FPE_FLTOVF, "Floating point overflow" },
{ FPE_FLTUND, "Floating point underflow" },
{ FPE_FLTRES, "Floating point inexact result" },
{ FPE_FLTINV, "Floating point invalid operation" },
{ FPE_FLTSUB, "Subscript out of range" },
{ 0, NULL }
};
static const struct {
int code;
const char *name;
} sigsegv_codes[] = {
#ifndef __FreeBSD__
{ SEGV_MAPERR, "Address not mapped to object" },
{ SEGV_ACCERR, "Invalid permissions for mapped object" },
#endif
{ 0, NULL }
};
static const struct {
int code;
const char *name;
} sigbus_codes[] = {
#ifndef __FreeBSD__
{ BUS_ADRALN, "Invalid address alignment" },
{ BUS_ADRERR, "Non-existent physical address" },
{ BUS_OBJERR, "Object specific hardware error" },
#endif
{ 0, NULL }
};
static int (*cc_user_info)(char*, char*);
static void gdb_info(pid_t pid)
{
char respfile[64];
char cmd_buf[128];
FILE *f;
int fd;
/* Create a temp file to put gdb commands into */
strcpy(respfile, "gdb-respfile-XXXXXX");
if((fd=mkstemp(respfile)) >= 0 && (f=fdopen(fd, "w")) != NULL)
{
fprintf(f, "attach %d\n"
"shell echo \"\"\n"
"shell echo \"* Loaded Libraries\"\n"
"info sharedlibrary\n"
"shell echo \"\"\n"
"shell echo \"* Threads\"\n"
"info threads\n"
"shell echo \"\"\n"
"shell echo \"* FPU Status\"\n"
"info float\n"
"shell echo \"\"\n"
"shell echo \"* Registers\"\n"
"info registers\n"
"shell echo \"\"\n"
"shell echo \"* Backtrace\"\n"
"thread apply all backtrace full\n"
"detach\n"
"quit\n", pid);
fclose(f);
/* Run gdb and print process info. */
snprintf(cmd_buf, sizeof(cmd_buf), "gdb --quiet --batch --command=%s", respfile);
printf("Executing: %s\n", cmd_buf);
fflush(stdout);
system(cmd_buf);
/* Clean up */
remove(respfile);
}
else
{
/* Error creating temp file */
if(fd >= 0)
{
close(fd);
remove(respfile);
}
printf("!!! Could not create gdb command file\n");
}
fflush(stdout);
}
static void sys_info(void)
{
#ifdef __unix__
system("echo \"System: `uname -a`\"");
putchar('\n');
fflush(stdout);
#endif
}
static size_t safe_write(int fd, const void *buf, size_t len)
{
size_t ret = 0;
while(ret < len)
{
ssize_t rem;
if((rem=write(fd, (const char*)buf+ret, len-ret)) == -1)
{
if(errno == EINTR)
continue;
break;
}
ret += rem;
}
return ret;
}
static void crash_catcher(int signum, siginfo_t *siginfo, void *context)
{
//ucontext_t *ucontext = (ucontext_t*)context;
pid_t dbg_pid;
int fd[2];
/* Make sure the effective uid is the real uid */
if(getuid() != geteuid())
{
raise(signum);
return;
}
safe_write(STDERR_FILENO, fatal_err, sizeof(fatal_err)-1);
if(pipe(fd) == -1)
{
safe_write(STDERR_FILENO, pipe_err, sizeof(pipe_err)-1);
raise(signum);
return;
}
crash_info.signum = signum;
crash_info.pid = getpid();
crash_info.has_siginfo = !!siginfo;
if(siginfo)
crash_info.siginfo = *siginfo;
if(cc_user_info)
cc_user_info(crash_info.buf, crash_info.buf+sizeof(crash_info.buf));
/* Fork off to start a crash handler */
switch((dbg_pid=fork()))
{
/* Error */
case -1:
safe_write(STDERR_FILENO, fork_err, sizeof(fork_err)-1);
raise(signum);
return;
case 0:
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
close(fd[1]);
execl(argv0, argv0, crash_switch, NULL);
safe_write(STDERR_FILENO, exec_err, sizeof(exec_err)-1);
_exit(1);
default:
#ifdef __linux__
prctl(PR_SET_PTRACER, dbg_pid, 0, 0, 0);
#endif
safe_write(fd[1], &crash_info, sizeof(crash_info));
close(fd[0]);
close(fd[1]);
/* Wait; we'll be killed when gdb is done */
do {
int status;
if(waitpid(dbg_pid, &status, 0) == dbg_pid &&
(WIFEXITED(status) || WIFSIGNALED(status)))
{
/* The debug process died before it could kill us */
raise(signum);
break;
}
} while(1);
}
}
static void crash_handler(const char *logfile)
{
const char *sigdesc = "";
int i;
if(fread(&crash_info, sizeof(crash_info), 1, stdin) != 1)
{
fprintf(stderr, "!!! Failed to retrieve info from crashed process\n");
exit(1);
}
/* Get the signal description */
for(i = 0;signals[i].name;++i)
{
if(signals[i].signum == crash_info.signum)
{
sigdesc = signals[i].name;
break;
}
}
if(crash_info.has_siginfo)
{
switch(crash_info.signum)
{
case SIGSEGV:
for(i = 0;sigsegv_codes[i].name;++i)
{
if(sigsegv_codes[i].code == crash_info.siginfo.si_code)
{
sigdesc = sigsegv_codes[i].name;
break;
}
}
break;
case SIGFPE:
for(i = 0;sigfpe_codes[i].name;++i)
{
if(sigfpe_codes[i].code == crash_info.siginfo.si_code)
{
sigdesc = sigfpe_codes[i].name;
break;
}
}
break;
case SIGILL:
for(i = 0;sigill_codes[i].name;++i)
{
if(sigill_codes[i].code == crash_info.siginfo.si_code)
{
sigdesc = sigill_codes[i].name;
break;
}
}
break;
case SIGBUS:
for(i = 0;sigbus_codes[i].name;++i)
{
if(sigbus_codes[i].code == crash_info.siginfo.si_code)
{
sigdesc = sigbus_codes[i].name;
break;
}
}
break;
}
}
fprintf(stderr, "%s (signal %i)\n", sigdesc, crash_info.signum);
if(crash_info.has_siginfo)
fprintf(stderr, "Address: %p\n", crash_info.siginfo.si_addr);
fputc('\n', stderr);
if(logfile)
{
/* Create crash log file and redirect shell output to it */
if(freopen(logfile, "wa", stdout) != stdout)
{
fprintf(stderr, "!!! Could not create %s following signal\n", logfile);
exit(1);
}
fprintf(stderr, "Generating %s and killing process %d, please wait... ", logfile, crash_info.pid);
printf("*** Fatal Error ***\n"
"%s (signal %i)\n", sigdesc, crash_info.signum);
if(crash_info.has_siginfo)
printf("Address: %p\n", crash_info.siginfo.si_addr);
fputc('\n', stdout);
fflush(stdout);
}
sys_info();
crash_info.buf[sizeof(crash_info.buf)-1] = '\0';
printf("%s\n", crash_info.buf);
fflush(stdout);
if(crash_info.pid > 0)
{
gdb_info(crash_info.pid);
kill(crash_info.pid, SIGKILL);
}
if(logfile)
{
const char *str;
char buf[512];
if((str=getenv("KDE_FULL_SESSION")) && strcmp(str, "true") == 0)
snprintf(buf, sizeof(buf), "kdialog --title \"Very Fatal Error\" --textbox \"%s\" 800 600", logfile);
else if((str=getenv("GNOME_DESKTOP_SESSION_ID")) && str[0] != '\0')
snprintf(buf, sizeof(buf), "gxmessage -buttons \"Okay:0\" -geometry 800x600 -title \"Very Fatal Error\" -center -file \"%s\"", logfile);
else
snprintf(buf, sizeof(buf), "xmessage -buttons \"Okay:0\" -center -file \"%s\"", logfile);
system(buf);
}
exit(0);
}
int cc_install_handlers(int argc, char **argv, int num_signals, int *signals, const char *logfile, int (*user_info)(char*, char*))
{
struct sigaction sa;
stack_t altss;
int retval;
if(argc == 2 && strcmp(argv[1], crash_switch) == 0)
crash_handler(logfile);
cc_user_info = user_info;
if(argv[0][0] == '/')
snprintf(argv0, sizeof(argv0), "%s", argv[0]);
else
{
getcwd(argv0, sizeof(argv0));
retval = strlen(argv0);
snprintf(argv0+retval, sizeof(argv0)-retval, "/%s", argv[0]);
}
/* Set an alternate signal stack so SIGSEGVs caused by stack overflows
* still run */
altss.ss_sp = altstack;
altss.ss_flags = 0;
altss.ss_size = sizeof(altstack);
sigaltstack(&altss, NULL);
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = crash_catcher;
sa.sa_flags = SA_RESETHAND | SA_NODEFER | SA_SIGINFO | SA_ONSTACK;
sigemptyset(&sa.sa_mask);
retval = 0;
while(num_signals--)
{
if((*signals != SIGSEGV && *signals != SIGILL && *signals != SIGFPE &&
*signals != SIGBUS) || sigaction(*signals, &sa, NULL) == -1)
{
*signals = 0;
retval = -1;
}
++signals;
}
return retval;
}

View file

@ -0,0 +1,395 @@
/*
** hardware.cpp
** Somewhat OS-independant interface to the screen, mouse, keyboard, and stick
**
**---------------------------------------------------------------------------
** Copyright 1998-2006 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include <SDL.h>
#include <signal.h>
#include <time.h>
#include "version.h"
#include "hardware.h"
#include "i_video.h"
#include "i_system.h"
#include "c_console.h"
#include "c_cvars.h"
#include "c_dispatch.h"
#include "sdlvideo.h"
#include "v_text.h"
#include "doomstat.h"
#include "m_argv.h"
#include "sdlglvideo.h"
#include "r_renderer.h"
#include "swrenderer/r_swrenderer.h"
EXTERN_CVAR (Bool, ticker)
EXTERN_CVAR (Bool, fullscreen)
EXTERN_CVAR (Bool, swtruecolor)
EXTERN_CVAR (Float, vid_winscale)
IVideo *Video;
extern int NewWidth, NewHeight, NewBits, DisplayBits;
bool V_DoModeSetup (int width, int height, int bits);
void I_RestartRenderer();
int currentrenderer;
// [ZDoomGL]
CUSTOM_CVAR (Int, vid_renderer, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
{
// 0: Software renderer
// 1: OpenGL renderer
if (self != currentrenderer)
{
switch (self)
{
case 0:
Printf("Switching to software renderer...\n");
break;
case 1:
Printf("Switching to OpenGL renderer...\n");
break;
default:
Printf("Unknown renderer (%d). Falling back to software renderer...\n", (int) vid_renderer);
self = 0; // make sure to actually switch to the software renderer
break;
}
Printf("You must restart " GAMENAME " to switch the renderer\n");
}
}
void I_ShutdownGraphics ()
{
if (screen)
{
DFrameBuffer *s = screen;
screen = NULL;
delete s;
}
if (Video)
delete Video, Video = NULL;
SDL_QuitSubSystem (SDL_INIT_VIDEO);
}
void I_InitGraphics ()
{
#ifdef __APPLE__
SDL_SetHint(SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES, "0");
#endif // __APPLE__
if (SDL_InitSubSystem (SDL_INIT_VIDEO) < 0)
{
I_FatalError ("Could not initialize SDL video:\n%s\n", SDL_GetError());
return;
}
Printf("Using video driver %s\n", SDL_GetCurrentVideoDriver());
UCVarValue val;
val.Bool = !!Args->CheckParm ("-devparm");
ticker.SetGenericRepDefault (val, CVAR_Bool);
//currentrenderer = vid_renderer;
Video = new SDLGLVideo(0);
if (Video == NULL)
I_FatalError ("Failed to initialize display");
Video->SetWindowedScale (vid_winscale);
}
void I_DeleteRenderer()
{
if (Renderer != NULL) delete Renderer;
}
void I_CreateRenderer()
{
currentrenderer = vid_renderer;
if (Renderer == NULL)
{
if (currentrenderer==1) Renderer = gl_CreateInterface();
else Renderer = new FSoftwareRenderer;
}
}
/** Remaining code is common to Win32 and Linux **/
// VIDEO WRAPPERS ---------------------------------------------------------
DFrameBuffer *I_SetMode (int &width, int &height, DFrameBuffer *old)
{
bool fs = false;
switch (Video->GetDisplayType ())
{
case DISPLAY_WindowOnly:
fs = false;
break;
case DISPLAY_FullscreenOnly:
fs = true;
break;
case DISPLAY_Both:
fs = fullscreen;
break;
}
DFrameBuffer *res = Video->CreateFrameBuffer (width, height, swtruecolor, fs, old);
/* Right now, CreateFrameBuffer cannot return NULL
if (res == NULL)
{
I_FatalError ("Mode %dx%d is unavailable\n", width, height);
}
*/
return res;
}
bool I_CheckResolution (int width, int height, int bits)
{
int twidth, theight;
Video->StartModeIterator (bits, screen ? screen->IsFullscreen() : fullscreen);
while (Video->NextMode (&twidth, &theight, NULL))
{
if (width == twidth && height == theight)
return true;
}
return false;
}
void I_ClosestResolution (int *width, int *height, int bits)
{
#ifdef __MOBILE__ // We want to always use the resolution of the device!
return;
#endif
int twidth, theight;
int cwidth = 0, cheight = 0;
int iteration;
uint32_t closest = 4294967295u;
for (iteration = 0; iteration < 2; iteration++)
{
Video->StartModeIterator (bits, screen ? screen->IsFullscreen() : fullscreen);
while (Video->NextMode (&twidth, &theight, NULL))
{
if (twidth == *width && theight == *height)
return;
if (iteration == 0 && (twidth < *width || theight < *height))
continue;
uint32_t dist = (twidth - *width) * (twidth - *width)
+ (theight - *height) * (theight - *height);
if (dist < closest)
{
closest = dist;
cwidth = twidth;
cheight = theight;
}
}
if (closest != 4294967295u)
{
*width = cwidth;
*height = cheight;
return;
}
}
}
//==========================================================================
//
// SetFPSLimit
//
// Initializes an event timer to fire at a rate of <limit>/sec. The video
// update will wait for this timer to trigger before updating.
//
// Pass 0 as the limit for unlimited.
// Pass a negative value for the limit to use the value of vid_maxfps.
//
//==========================================================================
EXTERN_CVAR(Int, vid_maxfps);
EXTERN_CVAR(Bool, cl_capfps);
#if !defined(__APPLE__) && !defined(__OpenBSD__)
Semaphore FPSLimitSemaphore;
static void FPSLimitNotify(sigval val)
{
SEMAPHORE_SIGNAL(FPSLimitSemaphore)
}
void I_SetFPSLimit(int limit)
{
static sigevent FPSLimitEvent;
static timer_t FPSLimitTimer;
static bool FPSLimitTimerEnabled = false;
static bool EventSetup = false;
if(!EventSetup)
{
EventSetup = true;
FPSLimitEvent.sigev_notify = SIGEV_THREAD;
FPSLimitEvent.sigev_signo = 0;
FPSLimitEvent.sigev_value.sival_int = 0;
FPSLimitEvent.sigev_notify_function = FPSLimitNotify;
FPSLimitEvent.sigev_notify_attributes = NULL;
SEMAPHORE_INIT(FPSLimitSemaphore, 0, 0)
}
if (limit < 0)
{
limit = vid_maxfps;
}
// Kill any leftover timer.
if (FPSLimitTimerEnabled)
{
timer_delete(FPSLimitTimer);
FPSLimitTimerEnabled = false;
}
if (limit == 0)
{ // no limit
DPrintf(DMSG_NOTIFY, "FPS timer disabled\n");
}
else
{
FPSLimitTimerEnabled = true;
if(timer_create(CLOCK_REALTIME, &FPSLimitEvent, &FPSLimitTimer) == -1)
Printf(DMSG_WARNING, "Failed to create FPS limitter event\n");
itimerspec period = { {0, 0}, {0, 0} };
period.it_value.tv_nsec = period.it_interval.tv_nsec = 1000000000 / limit;
if(timer_settime(FPSLimitTimer, 0, &period, NULL) == -1)
Printf(DMSG_WARNING, "Failed to set FPS limitter timer\n");
DPrintf(DMSG_NOTIFY, "FPS timer set to %u ms\n", (unsigned int) period.it_interval.tv_nsec / 1000000);
}
}
#else
// So Apple doesn't support POSIX timers and I can't find a good substitute short of
// having Objective-C Cocoa events or something like that.
void I_SetFPSLimit(int limit)
{
}
#endif
CUSTOM_CVAR (Int, vid_maxfps, 200, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (vid_maxfps < TICRATE && vid_maxfps != 0)
{
vid_maxfps = TICRATE;
}
else if (vid_maxfps > 1000)
{
vid_maxfps = 1000;
}
else if (cl_capfps == 0)
{
I_SetFPSLimit(vid_maxfps);
}
}
extern int NewWidth, NewHeight, NewBits, DisplayBits;
CUSTOM_CVAR(Bool, swtruecolor, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL)
{
// Strictly speaking this doesn't require a mode switch, but it is the easiest
// way to force a CreateFramebuffer call without a lot of refactoring.
if (currentrenderer == 0)
{
NewWidth = screen->VideoWidth;
NewHeight = screen->VideoHeight;
NewBits = DisplayBits;
setmodeneeded = true;
}
}
CUSTOM_CVAR (Bool, fullscreen, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL)
{
NewWidth = screen->VideoWidth;
NewHeight = screen->VideoHeight;
NewBits = DisplayBits;
setmodeneeded = true;
}
CUSTOM_CVAR (Float, vid_winscale, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
{
if (self < 1.f)
{
self = 1.f;
}
else if (Video)
{
Video->SetWindowedScale (self);
NewWidth = screen->VideoWidth;
NewHeight = screen->VideoHeight;
NewBits = DisplayBits;
setmodeneeded = true;
}
}
CCMD (vid_listmodes)
{
static const char *ratios[7] = { "", " - 16:9", " - 16:10", "", " - 5:4", "", " - 21:9" };
int width, height, bits;
bool letterbox;
if (Video == NULL)
{
return;
}
for (bits = 1; bits <= 32; bits++)
{
Video->StartModeIterator (bits, screen->IsFullscreen());
while (Video->NextMode (&width, &height, &letterbox))
{
bool thisMode = (width == DisplayWidth && height == DisplayHeight && bits == DisplayBits);
int ratio = CheckRatio (width, height);
Printf (thisMode ? PRINT_BOLD : PRINT_HIGH,
"%s%4d x%5d x%3d%s%s\n",
thisMode || !IsRatioWidescreen(ratio) ? "" : TEXTCOLOR_GOLD,
width, height, bits,
ratios[ratio],
thisMode || !letterbox ? "" : TEXTCOLOR_BROWN " LB"
);
}
}
}
CCMD (vid_currentmode)
{
Printf ("%dx%dx%d\n", DisplayWidth, DisplayHeight, DisplayBits);
}

View file

@ -0,0 +1,86 @@
/*
** i_gui.cpp
**
**---------------------------------------------------------------------------
** Copyright 2008 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include <string.h>
#include <SDL.h>
#include "bitmap.h"
#include "v_palette.h"
#include "textures.h"
bool I_SetCursor(FTexture *cursorpic)
{
static SDL_Cursor *cursor;
static SDL_Surface *cursorSurface;
if (cursorpic != NULL && cursorpic->UseType != ETextureType::Null)
{
// Must be no larger than 32x32.
if (cursorpic->GetWidth() > 32 || cursorpic->GetHeight() > 32)
{
return false;
}
if (cursorSurface == NULL)
cursorSurface = SDL_CreateRGBSurface (0, 32, 32, 32, MAKEARGB(0,255,0,0), MAKEARGB(0,0,255,0), MAKEARGB(0,0,0,255), MAKEARGB(255,0,0,0));
SDL_LockSurface(cursorSurface);
uint8_t buffer[32*32*4];
memset(buffer, 0, 32*32*4);
FBitmap bmp(buffer, 32*4, 32, 32);
cursorpic->CopyTrueColorPixels(&bmp, 0, 0);
memcpy(cursorSurface->pixels, bmp.GetPixels(), 32*32*4);
SDL_UnlockSurface(cursorSurface);
if (cursor)
SDL_FreeCursor (cursor);
cursor = SDL_CreateColorCursor (cursorSurface, 0, 0);
SDL_SetCursor (cursor);
}
else
{
if (cursor)
{
SDL_SetCursor (NULL);
SDL_FreeCursor (cursor);
cursor = NULL;
}
if (cursorSurface != NULL)
{
SDL_FreeSurface(cursorSurface);
cursorSurface = NULL;
}
}
return true;
}

View file

@ -0,0 +1,591 @@
/*
** i_input.cpp
**
**---------------------------------------------------------------------------
** Copyright 2005-2016 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include <SDL.h>
#include <ctype.h>
#include "doomtype.h"
#include "c_dispatch.h"
#include "doomdef.h"
#include "doomstat.h"
#include "m_argv.h"
#include "i_input.h"
#include "v_video.h"
#include "d_main.h"
#include "d_event.h"
#include "d_gui.h"
#include "c_console.h"
#include "c_cvars.h"
#include "i_system.h"
#include "c_dispatch.h"
#include "dikeys.h"
#include "templates.h"
#include "s_sound.h"
#include "events.h"
#include "utf8.h"
#include "doomerrors.h"
static void I_CheckGUICapture ();
static void I_CheckNativeMouse ();
bool GUICapture;
static bool NativeMouse = true;
extern int paused;
CVAR (Bool, use_mouse, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, m_noprescale, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, m_filter, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, i_soundinbackground, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
EXTERN_CVAR (Bool, fullscreen)
extern int WaitingForKey, chatmodeon;
extern constate_e ConsoleState;
static const SDL_Keycode DIKToKeySym[256] =
{
0, SDLK_ESCAPE, SDLK_1, SDLK_2, SDLK_3, SDLK_4, SDLK_5, SDLK_6,
SDLK_7, SDLK_8, SDLK_9, SDLK_0,SDLK_MINUS, SDLK_EQUALS, SDLK_BACKSPACE, SDLK_TAB,
SDLK_q, SDLK_w, SDLK_e, SDLK_r, SDLK_t, SDLK_y, SDLK_u, SDLK_i,
SDLK_o, SDLK_p, SDLK_LEFTBRACKET, SDLK_RIGHTBRACKET, SDLK_RETURN, SDLK_LCTRL, SDLK_a, SDLK_s,
SDLK_d, SDLK_f, SDLK_g, SDLK_h, SDLK_j, SDLK_k, SDLK_l, SDLK_SEMICOLON,
SDLK_QUOTE, SDLK_BACKQUOTE, SDLK_LSHIFT, SDLK_BACKSLASH, SDLK_z, SDLK_x, SDLK_c, SDLK_v,
SDLK_b, SDLK_n, SDLK_m, SDLK_COMMA, SDLK_PERIOD, SDLK_SLASH, SDLK_RSHIFT, SDLK_KP_MULTIPLY,
SDLK_LALT, SDLK_SPACE, SDLK_CAPSLOCK, SDLK_F1, SDLK_F2, SDLK_F3, SDLK_F4, SDLK_F5,
SDLK_F6, SDLK_F7, SDLK_F8, SDLK_F9, SDLK_F10, SDLK_NUMLOCKCLEAR, SDLK_SCROLLLOCK, SDLK_KP_7,
SDLK_KP_8, SDLK_KP_9, SDLK_KP_MINUS, SDLK_KP_4, SDLK_KP_5, SDLK_KP_6, SDLK_KP_PLUS, SDLK_KP_1,
SDLK_KP_2, SDLK_KP_3, SDLK_KP_0, SDLK_KP_PERIOD, 0, 0, 0, SDLK_F11,
SDLK_F12, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, SDLK_F13, SDLK_F14, SDLK_F15, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, SDLK_KP_EQUALS, 0, 0,
0, SDLK_AT, SDLK_COLON, 0, 0, 0, 0, 0,
0, 0, 0, 0, SDLK_KP_ENTER, SDLK_RCTRL, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, SDLK_KP_COMMA, 0, SDLK_KP_DIVIDE, 0, SDLK_SYSREQ,
SDLK_RALT, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, SDLK_PAUSE, 0, SDLK_HOME,
SDLK_UP, SDLK_PAGEUP, 0, SDLK_LEFT, 0, SDLK_RIGHT, 0, SDLK_END,
SDLK_DOWN, SDLK_PAGEDOWN, SDLK_INSERT, SDLK_DELETE, 0, 0, 0, 0,
0, 0, 0, SDLK_LGUI, SDLK_RGUI, SDLK_MENU, SDLK_POWER, SDLK_SLEEP,
0, 0, 0, 0, 0, SDLK_AC_SEARCH, SDLK_AC_BOOKMARKS, SDLK_AC_REFRESH,
SDLK_AC_STOP, SDLK_AC_FORWARD, SDLK_AC_BACK, SDLK_COMPUTER, SDLK_MAIL, SDLK_MEDIASELECT, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
static const SDL_Scancode DIKToKeyScan[256] =
{
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_ESCAPE, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_3, SDL_SCANCODE_4, SDL_SCANCODE_5, SDL_SCANCODE_6,
SDL_SCANCODE_7, SDL_SCANCODE_8, SDL_SCANCODE_9, SDL_SCANCODE_0 ,SDL_SCANCODE_MINUS, SDL_SCANCODE_EQUALS, SDL_SCANCODE_BACKSPACE, SDL_SCANCODE_TAB,
SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_E, SDL_SCANCODE_R, SDL_SCANCODE_T, SDL_SCANCODE_Y, SDL_SCANCODE_U, SDL_SCANCODE_I,
SDL_SCANCODE_O, SDL_SCANCODE_P, SDL_SCANCODE_LEFTBRACKET, SDL_SCANCODE_RIGHTBRACKET, SDL_SCANCODE_RETURN, SDL_SCANCODE_LCTRL, SDL_SCANCODE_A, SDL_SCANCODE_S,
SDL_SCANCODE_D, SDL_SCANCODE_F, SDL_SCANCODE_G, SDL_SCANCODE_H, SDL_SCANCODE_J, SDL_SCANCODE_K, SDL_SCANCODE_L, SDL_SCANCODE_SEMICOLON,
SDL_SCANCODE_APOSTROPHE, SDL_SCANCODE_GRAVE, SDL_SCANCODE_LSHIFT, SDL_SCANCODE_BACKSLASH, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_C, SDL_SCANCODE_V,
SDL_SCANCODE_B, SDL_SCANCODE_N, SDL_SCANCODE_M, SDL_SCANCODE_COMMA, SDL_SCANCODE_PERIOD, SDL_SCANCODE_SLASH, SDL_SCANCODE_RSHIFT, SDL_SCANCODE_KP_MULTIPLY,
SDL_SCANCODE_LALT, SDL_SCANCODE_SPACE, SDL_SCANCODE_CAPSLOCK, SDL_SCANCODE_F1, SDL_SCANCODE_F2, SDL_SCANCODE_F3, SDL_SCANCODE_F4, SDL_SCANCODE_F5,
SDL_SCANCODE_F6, SDL_SCANCODE_F7, SDL_SCANCODE_F8, SDL_SCANCODE_F9, SDL_SCANCODE_F10, SDL_SCANCODE_NUMLOCKCLEAR, SDL_SCANCODE_SCROLLLOCK, SDL_SCANCODE_KP_7,
SDL_SCANCODE_KP_8, SDL_SCANCODE_KP_9, SDL_SCANCODE_KP_MINUS, SDL_SCANCODE_KP_4, SDL_SCANCODE_KP_5, SDL_SCANCODE_KP_6, SDL_SCANCODE_KP_PLUS, SDL_SCANCODE_KP_1,
SDL_SCANCODE_KP_2, SDL_SCANCODE_KP_3, SDL_SCANCODE_KP_0, SDL_SCANCODE_KP_PERIOD, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_F11,
SDL_SCANCODE_F12, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_F13, SDL_SCANCODE_F14, SDL_SCANCODE_F15, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_EQUALS, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_ENTER, SDL_SCANCODE_RCTRL, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_COMMA, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_DIVIDE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_SYSREQ,
SDL_SCANCODE_RALT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_PAUSE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_HOME,
SDL_SCANCODE_UP, SDL_SCANCODE_PAGEUP, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_RIGHT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_END,
SDL_SCANCODE_DOWN, SDL_SCANCODE_PAGEDOWN, SDL_SCANCODE_INSERT, SDL_SCANCODE_DELETE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_LGUI, SDL_SCANCODE_RGUI, SDL_SCANCODE_MENU, SDL_SCANCODE_POWER, SDL_SCANCODE_SLEEP,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_AC_SEARCH, SDL_SCANCODE_AC_BOOKMARKS, SDL_SCANCODE_AC_REFRESH,
SDL_SCANCODE_AC_STOP, SDL_SCANCODE_AC_FORWARD, SDL_SCANCODE_AC_BACK, SDL_SCANCODE_COMPUTER, SDL_SCANCODE_MAIL, SDL_SCANCODE_MEDIASELECT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN
};
static TMap<SDL_Keycode, uint8_t> InitKeySymMap ()
{
TMap<SDL_Keycode, uint8_t> KeySymToDIK;
for (int i = 0; i < 256; ++i)
{
KeySymToDIK[DIKToKeySym[i]] = i;
}
KeySymToDIK[0] = 0;
KeySymToDIK[SDLK_RSHIFT] = DIK_LSHIFT;
KeySymToDIK[SDLK_RCTRL] = DIK_LCONTROL;
KeySymToDIK[SDLK_RALT] = DIK_LMENU;
// Depending on your Linux flavor, you may get SDLK_PRINT or SDLK_SYSREQ
KeySymToDIK[SDLK_PRINTSCREEN] = DIK_SYSRQ;
return KeySymToDIK;
}
static const TMap<SDL_Keycode, uint8_t> KeySymToDIK(InitKeySymMap());
static TMap<SDL_Scancode, uint8_t> InitKeyScanMap ()
{
TMap<SDL_Scancode, uint8_t> KeyScanToDIK;
for (int i = 0; i < 256; ++i)
{
KeyScanToDIK[DIKToKeyScan[i]] = i;
}
return KeyScanToDIK;
}
static const TMap<SDL_Scancode, uint8_t> KeyScanToDIK(InitKeyScanMap());
static void I_CheckGUICapture ()
{
bool wantCapt;
if (menuactive == MENU_Off)
{
wantCapt = ConsoleState == c_down || ConsoleState == c_falling || chatmodeon;
}
else
{
wantCapt = (menuactive == MENU_On || menuactive == MENU_OnNoPause);
}
// [ZZ] check active event handlers that want the UI processing
if (!wantCapt && E_CheckUiProcessors())
wantCapt = true;
if (wantCapt != GUICapture)
{
GUICapture = wantCapt;
ResetButtonStates();
}
}
void I_SetMouseCapture()
{
// Clear out any mouse movement.
SDL_GetRelativeMouseState (NULL, NULL);
SDL_SetRelativeMouseMode (SDL_TRUE);
#ifdef __MOBILE__
// Need to clear this again because setting mode above adds relative values
SDL_GetRelativeMouseState (NULL, NULL);
#endif
}
void I_ReleaseMouseCapture()
{
SDL_SetRelativeMouseMode (SDL_FALSE);
}
static void PostMouseMove (int x, int y)
{
static int lastx = 0, lasty = 0;
event_t ev = { 0,0,0,0,0,0,0 };
if (m_filter)
{
ev.x = (x + lastx) / 2;
ev.y = (y + lasty) / 2;
}
else
{
ev.x = x;
ev.y = y;
}
lastx = x;
lasty = y;
if (ev.x | ev.y)
{
ev.type = EV_Mouse;
D_PostEvent (&ev);
}
}
static void MouseRead ()
{
int x, y;
if (NativeMouse)
{
return;
}
SDL_GetRelativeMouseState (&x, &y);
if (!m_noprescale)
{
x *= 3;
y *= 2;
}
if (x | y)
{
PostMouseMove (x, -y);
}
}
CUSTOM_CVAR(Int, mouse_capturemode, 1, CVAR_GLOBALCONFIG|CVAR_ARCHIVE)
{
if (self < 0) self = 0;
else if (self > 2) self = 2;
}
static bool inGame()
{
switch (mouse_capturemode)
{
default:
case 0:
return gamestate == GS_LEVEL;
case 1:
return gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_FINALE;
case 2:
return true;
}
}
static void I_CheckNativeMouse ()
{
bool focus = SDL_GetKeyboardFocus() != NULL;
bool fs = screen->IsFullscreen();
bool wantNative = !focus || (!use_mouse || GUICapture || paused || demoplayback || !inGame());
if (wantNative != NativeMouse)
{
NativeMouse = wantNative;
SDL_ShowCursor (wantNative);
if (wantNative)
I_ReleaseMouseCapture ();
else
I_SetMouseCapture ();
}
}
void MessagePump (const SDL_Event &sev)
{
static int lastx = 0, lasty = 0;
int x, y;
event_t event = { 0,0,0,0,0,0,0 };
switch (sev.type)
{
case SDL_QUIT:
throw CExitEvent(0);
case SDL_WINDOWEVENT:
switch (sev.window.event)
{
extern bool AppActive;
case SDL_WINDOWEVENT_FOCUS_GAINED:
S_SetSoundPaused(1);
AppActive = true;
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
S_SetSoundPaused(i_soundinbackground);
AppActive = false;
break;
}
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
if (!GUICapture)
{
event.type = sev.type == SDL_MOUSEBUTTONDOWN ? EV_KeyDown : EV_KeyUp;
switch (sev.button.button)
{
case SDL_BUTTON_LEFT: event.data1 = KEY_MOUSE1; break;
case SDL_BUTTON_MIDDLE: event.data1 = KEY_MOUSE3; break;
case SDL_BUTTON_RIGHT: event.data1 = KEY_MOUSE2; break;
case SDL_BUTTON_X1: event.data1 = KEY_MOUSE4; break;
case SDL_BUTTON_X2: event.data1 = KEY_MOUSE5; break;
case 6: event.data1 = KEY_MOUSE6; break;
case 7: event.data1 = KEY_MOUSE7; break;
case 8: event.data1 = KEY_MOUSE8; break;
default: printf("SDL mouse button %s %d\n",
sev.type == SDL_MOUSEBUTTONDOWN ? "down" : "up", sev.button.button); break;
}
if (event.data1 != 0)
{
D_PostEvent(&event);
}
}
else if ((sev.button.button >= SDL_BUTTON_LEFT && sev.button.button <= SDL_BUTTON_X2))
{
int x, y;
SDL_GetMouseState(&x, &y);
event.type = EV_GUI_Event;
event.data1 = x;
event.data2 = y;
screen->ScaleCoordsFromWindow(event.data1, event.data2);
if (sev.type == SDL_MOUSEBUTTONDOWN)
{
switch(sev.button.button)
{
case SDL_BUTTON_LEFT: event.subtype = EV_GUI_LButtonDown; break;
case SDL_BUTTON_MIDDLE: event.subtype = EV_GUI_MButtonDown; break;
case SDL_BUTTON_RIGHT: event.subtype = EV_GUI_RButtonDown; break;
case SDL_BUTTON_X1: event.subtype = EV_GUI_BackButtonDown; break;
case SDL_BUTTON_X2: event.subtype = EV_GUI_FwdButtonDown; break;
default: assert(false); event.subtype = EV_GUI_None; break;
}
}
else
{
switch(sev.button.button)
{
case SDL_BUTTON_LEFT: event.subtype = EV_GUI_LButtonUp; break;
case SDL_BUTTON_MIDDLE: event.subtype = EV_GUI_MButtonUp; break;
case SDL_BUTTON_RIGHT: event.subtype = EV_GUI_RButtonUp; break;
case SDL_BUTTON_X1: event.subtype = EV_GUI_BackButtonUp; break;
case SDL_BUTTON_X2: event.subtype = EV_GUI_FwdButtonUp; break;
default: assert(false); event.subtype = EV_GUI_None; break;
}
}
SDL_Keymod kmod = SDL_GetModState();
event.data3 = ((kmod & KMOD_SHIFT) ? GKM_SHIFT : 0) |
((kmod & KMOD_CTRL) ? GKM_CTRL : 0) |
((kmod & KMOD_ALT) ? GKM_ALT : 0);
D_PostEvent(&event);
}
break;
case SDL_MOUSEMOTION:
if (GUICapture)
{
event.data1 = sev.motion.x;
event.data2 = sev.motion.y;
screen->ScaleCoordsFromWindow(event.data1, event.data2);
event.type = EV_GUI_Event;
event.subtype = EV_GUI_MouseMove;
SDL_Keymod kmod = SDL_GetModState();
event.data3 = ((kmod & KMOD_SHIFT) ? GKM_SHIFT : 0) |
((kmod & KMOD_CTRL) ? GKM_CTRL : 0) |
((kmod & KMOD_ALT) ? GKM_ALT : 0);
D_PostEvent(&event);
}
break;
case SDL_MOUSEWHEEL:
if (GUICapture)
{
event.type = EV_GUI_Event;
if (sev.wheel.y == 0)
event.subtype = sev.wheel.x > 0 ? EV_GUI_WheelRight : EV_GUI_WheelLeft;
else
event.subtype = sev.wheel.y > 0 ? EV_GUI_WheelUp : EV_GUI_WheelDown;
SDL_Keymod kmod = SDL_GetModState();
event.data3 = ((kmod & KMOD_SHIFT) ? GKM_SHIFT : 0) |
((kmod & KMOD_CTRL) ? GKM_CTRL : 0) |
((kmod & KMOD_ALT) ? GKM_ALT : 0);
D_PostEvent (&event);
}
else
{
event.type = EV_KeyDown;
if (sev.wheel.y != 0)
event.data1 = sev.wheel.y > 0 ? KEY_MWHEELUP : KEY_MWHEELDOWN;
else
event.data1 = sev.wheel.x > 0 ? KEY_MWHEELRIGHT : KEY_MWHEELLEFT;
D_PostEvent (&event);
event.type = EV_KeyUp;
D_PostEvent (&event);
}
break;
case SDL_KEYDOWN:
case SDL_KEYUP:
if (!GUICapture)
{
if (sev.key.repeat)
{
break;
}
event.type = sev.type == SDL_KEYDOWN ? EV_KeyDown : EV_KeyUp;
// Try to look up our key mapped key for conversion to DirectInput.
// If that fails, then we'll do a lookup against the scan code,
// which may not return the right key, but at least the key should
// work in the game.
if (const uint8_t *dik = KeySymToDIK.CheckKey (sev.key.keysym.sym))
event.data1 = *dik;
else if (const uint8_t *dik = KeyScanToDIK.CheckKey (sev.key.keysym.scancode))
event.data1 = *dik;
if (event.data1)
{
if (sev.key.keysym.sym < 256)
{
event.data2 = sev.key.keysym.sym;
}
D_PostEvent (&event);
}
}
else
{
event.type = EV_GUI_Event;
event.subtype = sev.type == SDL_KEYDOWN ? EV_GUI_KeyDown : EV_GUI_KeyUp;
SDL_Keymod kmod = SDL_GetModState();
event.data3 = ((kmod & KMOD_SHIFT) ? GKM_SHIFT : 0) |
((kmod & KMOD_CTRL) ? GKM_CTRL : 0) |
((kmod & KMOD_ALT) ? GKM_ALT : 0);
if (event.subtype == EV_GUI_KeyDown && sev.key.repeat)
{
event.subtype = EV_GUI_KeyRepeat;
}
switch (sev.key.keysym.sym)
{
case SDLK_KP_ENTER: event.data1 = GK_RETURN; break;
case SDLK_PAGEUP: event.data1 = GK_PGUP; break;
case SDLK_PAGEDOWN: event.data1 = GK_PGDN; break;
case SDLK_END: event.data1 = GK_END; break;
case SDLK_HOME: event.data1 = GK_HOME; break;
case SDLK_LEFT: event.data1 = GK_LEFT; break;
case SDLK_RIGHT: event.data1 = GK_RIGHT; break;
case SDLK_UP: event.data1 = GK_UP; break;
case SDLK_DOWN: event.data1 = GK_DOWN; break;
case SDLK_DELETE: event.data1 = GK_DEL; break;
case SDLK_ESCAPE: event.data1 = GK_ESCAPE; break;
case SDLK_F1: event.data1 = GK_F1; break;
case SDLK_F2: event.data1 = GK_F2; break;
case SDLK_F3: event.data1 = GK_F3; break;
case SDLK_F4: event.data1 = GK_F4; break;
case SDLK_F5: event.data1 = GK_F5; break;
case SDLK_F6: event.data1 = GK_F6; break;
case SDLK_F7: event.data1 = GK_F7; break;
case SDLK_F8: event.data1 = GK_F8; break;
case SDLK_F9: event.data1 = GK_F9; break;
case SDLK_F10: event.data1 = GK_F10; break;
case SDLK_F11: event.data1 = GK_F11; break;
case SDLK_F12: event.data1 = GK_F12; break;
case SDLK_SYSREQ: event.data1 = GK_SYSRQ; break;
default:
if (sev.key.keysym.sym < 256)
{
event.data1 = sev.key.keysym.sym;
}
break;
}
if (event.data1 < 128)
{
event.data1 = toupper(event.data1);
D_PostEvent (&event);
}
}
break;
case SDL_TEXTINPUT:
if (GUICapture)
{
int size;
int unichar = utf8_decode((const uint8_t*)sev.text.text, &size);
if (size != 4)
{
event.type = EV_GUI_Event;
event.subtype = EV_GUI_Char;
event.data1 = (int16_t)unichar;
event.data2 = !!(SDL_GetModState() & KMOD_ALT);
D_PostEvent (&event);
}
}
break;
case SDL_JOYBUTTONDOWN:
case SDL_JOYBUTTONUP:
if (!GUICapture)
{
event.type = sev.type == SDL_JOYBUTTONDOWN ? EV_KeyDown : EV_KeyUp;
event.data1 = KEY_FIRSTJOYBUTTON + sev.jbutton.button;
if(event.data1 != 0)
D_PostEvent(&event);
}
break;
}
}
void I_GetEvent ()
{
SDL_Event sev;
while (SDL_PollEvent (&sev))
{
MessagePump (sev);
}
if (use_mouse)
{
MouseRead ();
}
}
void I_StartTic ()
{
I_CheckGUICapture ();
I_CheckNativeMouse ();
I_GetEvent ();
}
void I_ProcessJoysticks ();
void I_StartFrame ()
{
I_ProcessJoysticks();
}

View file

@ -0,0 +1,349 @@
/*
** i_joystick.cpp
**
**---------------------------------------------------------------------------
** Copyright 2005-2016 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include <SDL.h>
#include "doomdef.h"
#include "version.h"
#include "templates.h"
#include "m_joy.h"
// Very small deadzone so that floating point magic doesn't happen
#define MIN_DEADZONE 0.000001f
CUSTOM_CVAR(Bool, joy_background, true, CVAR_GLOBALCONFIG|CVAR_ARCHIVE|CVAR_NOINITCALL)
{
Printf("This won't take effect until " GAMENAME " is restarted.\n");
}
class SDLInputJoystick: public IJoystickConfig
{
public:
SDLInputJoystick(int DeviceIndex) : DeviceIndex(DeviceIndex), Multiplier(1.0f)
{
Device = SDL_JoystickOpen(DeviceIndex);
if(Device != NULL)
{
NumAxes = SDL_JoystickNumAxes(Device);
NumHats = SDL_JoystickNumHats(Device);
SetDefaultConfig();
}
}
~SDLInputJoystick()
{
if(Device != NULL)
M_SaveJoystickConfig(this);
SDL_JoystickClose(Device);
}
bool IsValid() const
{
return Device != NULL;
}
FString GetName()
{
return SDL_JoystickName(Device);
}
float GetSensitivity()
{
return Multiplier;
}
void SetSensitivity(float scale)
{
Multiplier = scale;
}
int GetNumAxes()
{
return NumAxes + NumHats*2;
}
float GetAxisDeadZone(int axis)
{
return Axes[axis].DeadZone;
}
EJoyAxis GetAxisMap(int axis)
{
return Axes[axis].GameAxis;
}
const char *GetAxisName(int axis)
{
return Axes[axis].Name.GetChars();
}
float GetAxisScale(int axis)
{
return Axes[axis].Multiplier;
}
void SetAxisDeadZone(int axis, float zone)
{
Axes[axis].DeadZone = clamp(zone, MIN_DEADZONE, 1.f);
}
void SetAxisMap(int axis, EJoyAxis gameaxis)
{
Axes[axis].GameAxis = gameaxis;
}
void SetAxisScale(int axis, float scale)
{
Axes[axis].Multiplier = scale;
}
// Used by the saver to not save properties that are at their defaults.
bool IsSensitivityDefault()
{
return Multiplier == 1.0f;
}
bool IsAxisDeadZoneDefault(int axis)
{
return Axes[axis].DeadZone <= MIN_DEADZONE;
}
bool IsAxisMapDefault(int axis)
{
if(axis >= 5)
return Axes[axis].GameAxis == JOYAXIS_None;
return Axes[axis].GameAxis == DefaultAxes[axis];
}
bool IsAxisScaleDefault(int axis)
{
return Axes[axis].Multiplier == 1.0f;
}
void SetDefaultConfig()
{
for(int i = 0;i < GetNumAxes();i++)
{
AxisInfo info;
if(i < NumAxes)
info.Name.Format("Axis %d", i+1);
else
info.Name.Format("Hat %d (%c)", (i-NumAxes)/2 + 1, (i-NumAxes)%2 == 0 ? 'x' : 'y');
info.DeadZone = MIN_DEADZONE;
info.Multiplier = 1.0f;
info.Value = 0.0;
info.ButtonValue = 0;
if(i >= 5)
info.GameAxis = JOYAXIS_None;
else
info.GameAxis = DefaultAxes[i];
Axes.Push(info);
}
}
FString GetIdentifier()
{
char id[16];
mysnprintf(id, countof(id), "JS:%d", DeviceIndex);
return id;
}
void AddAxes(float axes[NUM_JOYAXIS])
{
// Add to game axes.
for (int i = 0; i < GetNumAxes(); ++i)
{
if(Axes[i].GameAxis != JOYAXIS_None)
axes[Axes[i].GameAxis] -= float(Axes[i].Value * Multiplier * Axes[i].Multiplier);
}
}
void ProcessInput()
{
uint8_t buttonstate;
for (int i = 0; i < NumAxes; ++i)
{
buttonstate = 0;
Axes[i].Value = SDL_JoystickGetAxis(Device, i)/32767.0;
Axes[i].Value = Joy_RemoveDeadZone(Axes[i].Value, Axes[i].DeadZone, &buttonstate);
// Map button to axis
// X and Y are handled differently so if we have 2 or more axes then we'll use that code instead.
if (NumAxes == 1 || (i >= 2 && i < NUM_JOYAXISBUTTONS))
{
Joy_GenerateButtonEvents(Axes[i].ButtonValue, buttonstate, 2, KEY_JOYAXIS1PLUS + i*2);
Axes[i].ButtonValue = buttonstate;
}
}
if(NumAxes > 1)
{
buttonstate = Joy_XYAxesToButtons(Axes[0].Value, Axes[1].Value);
Joy_GenerateButtonEvents(Axes[0].ButtonValue, buttonstate, 4, KEY_JOYAXIS1PLUS);
Axes[0].ButtonValue = buttonstate;
}
// Map POV hats to buttons and axes. Why axes? Well apparently I have
// a gamepad where the left control stick is a POV hat (instead of the
// d-pad like you would expect, no that's pressure sensitive). Also
// KDE's joystick dialog maps them to axes as well.
for (int i = 0; i < NumHats; ++i)
{
AxisInfo &x = Axes[NumAxes + i*2];
AxisInfo &y = Axes[NumAxes + i*2 + 1];
buttonstate = SDL_JoystickGetHat(Device, i);
// If we're going to assume that we can pass SDL's value into
// Joy_GenerateButtonEvents then we might as well assume the format here.
if(buttonstate & 0x1) // Up
y.Value = -1.0;
else if(buttonstate & 0x4) // Down
y.Value = 1.0;
else
y.Value = 0.0;
if(buttonstate & 0x2) // Left
x.Value = 1.0;
else if(buttonstate & 0x8) // Right
x.Value = -1.0;
else
x.Value = 0.0;
if(i < 4)
{
Joy_GenerateButtonEvents(x.ButtonValue, buttonstate, 4, KEY_JOYPOV1_UP + i*4);
x.ButtonValue = buttonstate;
}
}
}
protected:
struct AxisInfo
{
FString Name;
float DeadZone;
float Multiplier;
EJoyAxis GameAxis;
double Value;
uint8_t ButtonValue;
};
static const EJoyAxis DefaultAxes[5];
int DeviceIndex;
SDL_Joystick *Device;
float Multiplier;
TArray<AxisInfo> Axes;
int NumAxes;
int NumHats;
};
const EJoyAxis SDLInputJoystick::DefaultAxes[5] = {JOYAXIS_Side, JOYAXIS_Forward, JOYAXIS_Pitch, JOYAXIS_Yaw, JOYAXIS_Up};
class SDLInputJoystickManager
{
public:
SDLInputJoystickManager()
{
for(int i = 0;i < SDL_NumJoysticks();i++)
{
SDLInputJoystick *device = new SDLInputJoystick(i);
if(device->IsValid())
Joysticks.Push(device);
else
delete device;
}
}
~SDLInputJoystickManager()
{
for(unsigned int i = 0;i < Joysticks.Size();i++)
delete Joysticks[i];
}
void AddAxes(float axes[NUM_JOYAXIS])
{
for(unsigned int i = 0;i < Joysticks.Size();i++)
Joysticks[i]->AddAxes(axes);
}
void GetDevices(TArray<IJoystickConfig *> &sticks)
{
for(unsigned int i = 0;i < Joysticks.Size();i++)
{
M_LoadJoystickConfig(Joysticks[i]);
sticks.Push(Joysticks[i]);
}
}
void ProcessInput() const
{
for(unsigned int i = 0;i < Joysticks.Size();++i)
Joysticks[i]->ProcessInput();
}
protected:
TArray<SDLInputJoystick *> Joysticks;
};
static SDLInputJoystickManager *JoystickManager;
void I_StartupJoysticks()
{
if (joy_background)
SDL_SetHint("SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS", "1");
if(SDL_InitSubSystem(SDL_INIT_JOYSTICK) >= 0)
JoystickManager = new SDLInputJoystickManager();
}
void I_ShutdownInput()
{
if(JoystickManager)
{
delete JoystickManager;
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
}
}
void I_GetJoysticks(TArray<IJoystickConfig *> &sticks)
{
sticks.Clear();
JoystickManager->GetDevices(sticks);
}
void I_GetAxes(float axes[NUM_JOYAXIS])
{
for (int i = 0; i < NUM_JOYAXIS; ++i)
{
axes[i] = 0;
}
if (use_joystick)
{
JoystickManager->AddAxes(axes);
}
}
void I_ProcessJoysticks()
{
if (use_joystick)
JoystickManager->ProcessInput();
}
IJoystickConfig *I_UpdateDeviceList()
{
return NULL;
}

View file

@ -0,0 +1,229 @@
/*
** i_main.cpp
** System-specific startup code. Eventually calls D_DoomMain.
**
**---------------------------------------------------------------------------
** Copyright 1998-2007 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
// HEADER FILES ------------------------------------------------------------
#include <SDL.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <new>
#include <sys/param.h>
#include <locale.h>
#include "doomerrors.h"
#include "m_argv.h"
#include "d_main.h"
#include "i_system.h"
#include "i_video.h"
#include "c_console.h"
#include "errors.h"
#include "version.h"
#include "w_wad.h"
#include "g_level.h"
#include "g_levellocals.h"
#include "r_state.h"
#include "cmdlib.h"
#include "r_utility.h"
#include "doomstat.h"
#include "vm.h"
// MACROS ------------------------------------------------------------------
// TYPES -------------------------------------------------------------------
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
extern "C" int cc_install_handlers(int, char**, int, int*, const char*, int(*)(char*, char*));
#ifdef __APPLE__
void Mac_I_FatalError(const char* errortext);
#endif
#ifdef __linux__
void Linux_I_FatalError(const char* errortext);
#endif
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
// PUBLIC DATA DEFINITIONS -------------------------------------------------
// The command line arguments.
FArgs *Args;
// PRIVATE DATA DEFINITIONS ------------------------------------------------
// CODE --------------------------------------------------------------------
static int DoomSpecificInfo (char *buffer, char *end)
{
const char *arg;
int size = end-buffer-2;
int i, p;
p = 0;
p += snprintf (buffer+p, size-p, GAMENAME" version %s (%s)\n", GetVersionString(), GetGitHash());
#ifdef __VERSION__
p += snprintf (buffer+p, size-p, "Compiler version: %s\n", __VERSION__);
#endif
// If Args is nullptr, then execution is at either
// * early stage of initialization, additional info contains only default values
// * late stage of shutdown, most likely main() was done, and accessing global variables is no longer safe
if (Args)
{
p += snprintf(buffer + p, size - p, "\nCommand line:");
for (i = 0; i < Args->NumArgs(); ++i)
{
p += snprintf(buffer + p, size - p, " %s", Args->GetArg(i));
}
p += snprintf(buffer + p, size - p, "\n");
for (i = 0; (arg = Wads.GetWadName(i)) != NULL; ++i)
{
p += snprintf(buffer + p, size - p, "\nWad %d: %s", i, arg);
}
if (gamestate != GS_LEVEL && gamestate != GS_TITLELEVEL)
{
p += snprintf(buffer + p, size - p, "\n\nNot in a level.");
}
else
{
p += snprintf(buffer + p, size - p, "\n\nCurrent map: %s", level.MapName.GetChars());
if (!viewactive)
{
p += snprintf(buffer + p, size - p, "\n\nView not active.");
}
else
{
auto& vp = r_viewpoint;
p += snprintf(buffer + p, size - p, "\n\nviewx = %f", vp.Pos.X);
p += snprintf(buffer + p, size - p, "\nviewy = %f", vp.Pos.Y);
p += snprintf(buffer + p, size - p, "\nviewz = %f", vp.Pos.Z);
p += snprintf(buffer + p, size - p, "\nviewangle = %f", vp.Angles.Yaw.Degrees);
}
}
}
buffer[p++] = '\n';
buffer[p++] = '\0';
return p;
}
void I_DetectOS()
{
// The POSIX version never implemented this.
}
void I_StartupJoysticks();
#ifdef __ANDROID__
#include <android/log.h>
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO,"JNITouchControlsUtils", __VA_ARGS__))
#include "LogWritter.h"
int main_android (int argc, char **argv)
{
#else
int main (int argc, char **argv)
{
#endif
#if !defined (__APPLE__) && !defined (__ANDROID__)
{
int s[4] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS };
cc_install_handlers(argc, argv, 4, s, GAMENAMELOWERCASE "-crash.log", DoomSpecificInfo);
}
#endif // !__APPLE__
printf(GAMENAME" %s - %s - SDL version\nCompiled on %s\n",
GetVersionString(), GetGitTime(), __DATE__);
seteuid (getuid ());
// Set LC_NUMERIC environment variable in case some library decides to
// clear the setlocale call at least this will be correct.
// Note that the LANG environment variable is overridden by LC_*
setenv ("LC_NUMERIC", "C", 1);
setlocale (LC_ALL, "C");
if (SDL_Init (0) < 0)
{
fprintf (stderr, "Could not initialize SDL:\n%s\n", SDL_GetError());
return -1;
}
printf("\n");
Args = new FArgs(argc, argv);
// Should we even be doing anything with progdir on Unix systems?
char program[PATH_MAX];
if (realpath (argv[0], program) == NULL)
strcpy (program, argv[0]);
char *slash = strrchr (program, '/');
if (slash != NULL)
{
*(slash + 1) = '\0';
progdir = program;
}
else
{
progdir = "./";
}
I_StartupJoysticks();
const int result = D_DoomMain();
SDL_Quit();
#ifdef __ANDROID__ // So it it properly reset next time it starts up
usleep(1000 * 500);
exit(0);
#endif
return result;
}

View file

@ -0,0 +1,281 @@
//-----------------------------------------------------------------------------
//
// Copyright 1993-1996 id Software
// Copyright 1999-2016 Randy Heit
//
// 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.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//-----------------------------------------------------------------------------
//
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <SDL.h>
#include "d_main.h"
#include "i_system.h"
#include "version.h"
#include "x86.h"
#ifndef NO_GTK
bool I_GtkAvailable ();
int I_PickIWad_Gtk (WadStuff *wads, int numwads, bool showwin, int defaultiwad);
void I_ShowFatalError_Gtk(const char* errortext);
#elif defined(__APPLE__)
int I_PickIWad_Cocoa (WadStuff *wads, int numwads, bool showwin, int defaultiwad);
#endif
double PerfToSec, PerfToMillisec;
void I_SetIWADInfo()
{
}
//
// I_Error
//
#ifdef __APPLE__
void Mac_I_FatalError(const char* errortext);
#endif
#ifdef __ANDROID__
#include <android/log.h>
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO,"Gzdoom", __VA_ARGS__))
#include "LogWritter.h"
#endif
#ifdef __linux__
void Linux_I_FatalError(const char* errortext)
{
// Close window or exit fullscreen and release mouse capture
SDL_Quit();
const char *str;
if((str=getenv("KDE_FULL_SESSION")) && strcmp(str, "true") == 0)
{
FString cmd;
cmd << "kdialog --title \"" GAMESIG " " << GetVersionString()
<< "\" --msgbox \"" << errortext << "\"";
popen(cmd, "r");
}
#ifndef NO_GTK
else if (I_GtkAvailable())
{
I_ShowFatalError_Gtk(errortext);
}
#endif
else
{
FString title;
title << GAMESIG " " << GetVersionString();
#ifdef __ANDROID__
LOGI("FATAL ERROR: %s", errortext);
LogWritter_Write(errortext);
#endif
if (SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, title, errortext, NULL) < 0)
{
printf("\n%s\n", errortext);
}
}
}
#endif
void I_ShowFatalError(const char *message)
{
#ifdef __ANDROID__
LOGI("ERROR: %s", message);
LogWritter_Write(message);
#endif
#ifdef __APPLE__
Mac_I_FatalError(message);
#elif defined __linux__
Linux_I_FatalError(message);
#else
// ???
#endif
}
void CalculateCPUSpeed()
{
}
void I_DebugPrint(const char *cp)
{
}
void I_PrintStr(const char *cp)
{
// Strip out any color escape sequences before writing to debug output
TArray<char> copy(strlen(cp) + 1, true);
const char * srcp = cp;
char * dstp = copy.Data();
while (*srcp != 0)
{
if (*srcp != 0x1c && *srcp != 0x1d && *srcp != 0x1e && *srcp != 0x1f)
{
*dstp++ = *srcp++;
}
else
{
if (srcp[1] != 0) srcp += 2;
else break;
}
}
*dstp = 0;
fputs(copy.Data(), stdout);
fflush(stdout);
}
int I_PickIWad (WadStuff *wads, int numwads, bool showwin, int defaultiwad)
{
int i;
if (!showwin)
{
return defaultiwad;
}
#ifndef __APPLE__
const char *str;
if((str=getenv("KDE_FULL_SESSION")) && strcmp(str, "true") == 0)
{
FString cmd("kdialog --title \"" GAMESIG " ");
cmd << GetVersionString() << ": Select an IWAD to use\""
" --menu \"" GAMENAME " found more than one IWAD\n"
"Select from the list below to determine which one to use:\"";
for(i = 0; i < numwads; ++i)
{
const char *filepart = strrchr(wads[i].Path, '/');
if(filepart == NULL)
filepart = wads[i].Path;
else
filepart++;
// Menu entries are specified in "tag" "item" pairs, where when a
// particular item is selected (and the Okay button clicked), its
// corresponding tag is printed to stdout for identification.
cmd.AppendFormat(" \"%d\" \"%s (%s)\"", i, wads[i].Name.GetChars(), filepart);
}
if(defaultiwad >= 0 && defaultiwad < numwads)
{
const char *filepart = strrchr(wads[defaultiwad].Path, '/');
if(filepart == NULL)
filepart = wads[defaultiwad].Path;
else
filepart++;
cmd.AppendFormat(" --default \"%s (%s)\"", wads[defaultiwad].Name.GetChars(), filepart);
}
FILE *f = popen(cmd, "r");
if(f != NULL)
{
char gotstr[16];
if(fgets(gotstr, sizeof(gotstr), f) == NULL ||
sscanf(gotstr, "%d", &i) != 1)
i = -1;
// Exit status = 1 means the selection was canceled (either by
// Cancel/Esc or the X button), not that there was an error running
// the program. In that case, nothing was printed so fgets will
// have failed. Other values can indicate an error running the app,
// so fall back to whatever else can be used.
int status = pclose(f);
if(WIFEXITED(status) && (WEXITSTATUS(status) == 0 || WEXITSTATUS(status) == 1))
return i;
}
}
#endif
#ifndef NO_GTK
if (I_GtkAvailable())
{
return I_PickIWad_Gtk (wads, numwads, showwin, defaultiwad);
}
#endif
#ifdef __APPLE__
return I_PickIWad_Cocoa (wads, numwads, showwin, defaultiwad);
#endif
if (!isatty(fileno(stdin)))
{
return defaultiwad;
}
printf ("Please select a game wad (or 0 to exit):\n");
for (i = 0; i < numwads; ++i)
{
const char *filepart = strrchr (wads[i].Path, '/');
if (filepart == NULL)
filepart = wads[i].Path;
else
filepart++;
printf ("%d. %s (%s)\n", i+1, wads[i].Name.GetChars(), filepart);
}
printf ("Which one? ");
if (scanf ("%d", &i) != 1 || i > numwads)
return -1;
return i-1;
}
void I_PutInClipboard (const char *str)
{
SDL_SetClipboardText(str);
}
FString I_GetFromClipboard (bool use_primary_selection)
{
if(char *ret = SDL_GetClipboardText())
{
FString text(ret);
SDL_free(ret);
return text;
}
return "";
}
// Return a random seed, preferably one with lots of entropy.
unsigned int I_MakeRNGSeed()
{
unsigned int seed;
int file;
// Try reading from /dev/urandom first, then /dev/random, then
// if all else fails, use a crappy seed from time().
seed = time(NULL);
file = open("/dev/urandom", O_RDONLY);
if (file < 0)
{
file = open("/dev/random", O_RDONLY);
}
if (file >= 0)
{
read(file, &seed, sizeof(seed));
close(file);
}
return seed;
}

View file

@ -0,0 +1,19 @@
#include <CoreFoundation/CoreFoundation.h>
#include "SDL.h"
void Mac_I_FatalError(const char* errortext)
{
// Close window or exit fullscreen and release mouse capture
SDL_Quit();
const CFStringRef errorString = CFStringCreateWithCStringNoCopy( kCFAllocatorDefault,
errortext, kCFStringEncodingASCII, kCFAllocatorNull );
if ( NULL != errorString )
{
CFOptionFlags dummy;
CFUserNotificationDisplayAlert( 0, kCFUserNotificationStopAlertLevel, NULL, NULL, NULL,
CFSTR( "Fatal Error" ), errorString, CFSTR( "Exit" ), NULL, NULL, &dummy );
CFRelease( errorString );
}
}

View file

@ -0,0 +1,591 @@
/*
** sdlglvideo.cpp
**
**---------------------------------------------------------------------------
** Copyright 2005-2016 Christoph Oelckers et.al.
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
// HEADER FILES ------------------------------------------------------------
#include "doomtype.h"
#include "templates.h"
#include "i_system.h"
#include "i_video.h"
#include "m_argv.h"
#include "v_video.h"
#include "v_pfx.h"
#include "stats.h"
#include "version.h"
#include "c_console.h"
#include "videomodes.h"
#include "sdlglvideo.h"
#include "sdlvideo.h"
#include "gl/system/gl_system.h"
#include "r_defs.h"
#include "gl/gl_functions.h"
//#include "gl/gl_intern.h"
#include "gl/renderer/gl_renderer.h"
#include "gl/system/gl_framebuffer.h"
#include "gl/shaders/gl_shader.h"
#include "gl/utility/gl_templates.h"
#include "gl/textures/gl_material.h"
#include "gl/system/gl_cvars.h"
// MACROS ------------------------------------------------------------------
// TYPES -------------------------------------------------------------------
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
extern IVideo *Video;
// extern int vid_renderer;
EXTERN_CVAR (Float, Gamma)
EXTERN_CVAR (Int, vid_adapter)
EXTERN_CVAR (Int, vid_displaybits)
EXTERN_CVAR (Int, vid_renderer)
EXTERN_CVAR (Int, vid_maxfps)
EXTERN_CVAR (Bool, cl_capfps)
DFrameBuffer *CreateGLSWFrameBuffer(int width, int height, bool bgra, bool fullscreen);
// PUBLIC DATA DEFINITIONS -------------------------------------------------
CUSTOM_CVAR(Bool, gl_debug, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
{
Printf("This won't take effect until " GAMENAME " is restarted.\n");
}
#ifdef __arm__
CUSTOM_CVAR(Bool, vid_glswfb, false, CVAR_NOINITCALL)
{
Printf("This won't take effect until " GAMENAME " is restarted.\n");
}
CUSTOM_CVAR(Bool, gl_es, false, CVAR_NOINITCALL)
{
Printf("This won't take effect until " GAMENAME " is restarted.\n");
}
#else
CUSTOM_CVAR(Bool, vid_glswfb, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
{
Printf("This won't take effect until " GAMENAME " is restarted.\n");
}
CUSTOM_CVAR(Bool, gl_es, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
{
Printf("This won't take effect until " GAMENAME " is restarted.\n");
}
#endif
// PRIVATE DATA DEFINITIONS ------------------------------------------------
// CODE --------------------------------------------------------------------
SDLGLVideo::SDLGLVideo (int parm)
{
IteratorBits = 0;
if( SDL_Init( SDL_INIT_VIDEO ) < 0 ) {
fprintf( stderr, "Video initialization failed: %s\n",
SDL_GetError( ) );
}
}
SDLGLVideo::~SDLGLVideo ()
{
if (GLRenderer != NULL) GLRenderer->FlushTextures();
}
void SDLGLVideo::StartModeIterator (int bits, bool fs)
{
IteratorMode = 0;
IteratorBits = bits;
}
bool SDLGLVideo::NextMode (int *width, int *height, bool *letterbox)
{
if (IteratorBits != 8)
return false;
if ((unsigned)IteratorMode < sizeof(VideoModes)/sizeof(VideoModes[0]))
{
*width = VideoModes[IteratorMode].width;
*height = VideoModes[IteratorMode].height;
++IteratorMode;
return true;
}
return false;
}
DFrameBuffer *SDLGLVideo::CreateFrameBuffer (int width, int height, bool bgra, bool fullscreen, DFrameBuffer *old)
{
static int retry = 0;
static int owidth, oheight;
PalEntry flashColor;
// int flashAmount;
if (old != NULL)
{ // Reuse the old framebuffer if its attributes are the same
SDLBaseFB *fb = static_cast<SDLBaseFB *> (old);
if (fb->Width == width &&
fb->Height == height)
{
bool fsnow = (SDL_GetWindowFlags (fb->GetSDLWindow()) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
if (fsnow != fullscreen)
{
SDL_SetWindowFullscreen (fb->GetSDLWindow(), fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
}
return old;
}
// old->GetFlash (flashColor, flashAmount);
delete old;
}
else
{
flashColor = 0;
// flashAmount = 0;
}
SDLBaseFB *fb;
if (vid_renderer == 1)
{
#ifdef USE_GL_HW_BUFFERS
const char *hwBuffers = Args->CheckValue("-hwbuffers");
int buffers = 1;
if (hwBuffers)
{
buffers = atoi(hwBuffers);
}
Printf("HW buffers = %d\n", buffers);
fb = new OpenGLFrameBuffer(0, width, height, 32, 60, fullscreen, buffers);
#else
fb = new OpenGLFrameBuffer(0, width, height, 32, 60, fullscreen);
#endif
}
else if (vid_glswfb == 0)
{
fb = new SDLFB(width, height, bgra, fullscreen, nullptr);
}
else
{
fb = (SDLBaseFB*)CreateGLSWFrameBuffer(width, height, bgra, fullscreen);
if (!fb->IsValid())
{
delete fb;
fb = new SDLFB(width, height, bgra, fullscreen, nullptr);
}
}
retry = 0;
// If we could not create the framebuffer, try again with slightly
// different parameters in this order:
// 1. Try with the closest size
// 2. Try in the opposite screen mode with the original size
// 3. Try in the opposite screen mode with the closest size
// This is a somewhat confusing mass of recursion here.
while (fb == NULL || !fb->IsValid ())
{
if (fb != NULL)
{
delete fb;
}
switch (retry)
{
case 0:
owidth = width;
oheight = height;
case 2:
// Try a different resolution. Hopefully that will work.
I_ClosestResolution (&width, &height, 8);
break;
case 1:
// Try changing fullscreen mode. Maybe that will work.
width = owidth;
height = oheight;
fullscreen = !fullscreen;
break;
default:
// I give up!
I_FatalError ("Could not create new screen (%d x %d)", owidth, oheight);
fprintf( stderr, "!!! [SDLGLVideo::CreateFrameBuffer] Got beyond I_FatalError !!!" );
return NULL; //[C] actually this shouldn't be reached; probably should be replaced with an ASSERT
}
++retry;
fb = static_cast<SDLBaseFB *>(CreateFrameBuffer (width, height, false, fullscreen, NULL));
}
// fb->SetFlash (flashColor, flashAmount);
return fb;
}
void SDLGLVideo::SetWindowedScale (float scale)
{
}
bool SDLGLVideo::SetResolution (int width, int height, int bits)
{
// FIXME: Is it possible to do this without completely destroying the old
// interface?
#ifndef NO_GL
if (GLRenderer != NULL) GLRenderer->FlushTextures();
I_ShutdownGraphics();
Video = new SDLGLVideo(0);
if (Video == NULL) I_FatalError ("Failed to initialize display");
#if (defined(WINDOWS)) || defined(WIN32)
bits=32;
#else
bits=24;
#endif
V_DoModeSetup(width, height, bits);
#endif
return true; // We must return true because the old video context no longer exists.
}
//==========================================================================
//
//
//
//==========================================================================
#ifdef __MOBILE__
extern "C" extern int glesLoad;
#endif
void SDLGLVideo::SetupPixelFormat(bool allowsoftware, int multisample, const int *glver)
{
SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 );
SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 );
#ifdef __MOBILE__
SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );
#else
SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 24 );
#endif
SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, 8 );
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
if (multisample > 0) {
SDL_GL_SetAttribute( SDL_GL_MULTISAMPLEBUFFERS, 1 );
SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, multisample );
}
if (gl_debug)
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
#ifdef __MOBILE__
const char *version = Args->CheckValue("-glversion");
int major,min;
if( !strcmp(version, "gles1") )
{
glesLoad = 1;
major = 1;
min = 0;
}
else if ( !strcmp(version, "gles2") )
{
glesLoad = 2;
major = 2;
min = 0;
}
else if ( !strcmp(version, "gles3") )
{
glesLoad = 3;
major = 3;
min = 1;
}
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, min);
return;
#endif
if (gl_es)
{
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
}
else if (glver[0] > 2)
{
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, glver[0]);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, glver[1]);
}
else
{
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
}
}
// FrameBuffer implementation -----------------------------------------------
SDLGLFB::SDLGLFB (void *, int width, int height, int, int, bool fullscreen, bool bgra)
: SDLBaseFB (width, height, bgra)
{
// NOTE: Core profiles were added with GL 3.2, so there's no sense trying
// to set core 3.1 or 3.0. We could try a forward-compatible context
// instead, but that would be too restrictive (w.r.t. shaders).
static const int glvers[][2] = {
{ 4, 5 }, { 4, 4 }, { 4, 3 }, { 4, 2 }, { 4, 1 }, { 4, 0 },
{ 3, 3 }, { 3, 2 }, { 2, 0 },
{ 0, 0 },
};
int glveridx = 0;
int i;
m_Lock=0;
UpdatePending = false;
const char *version = Args->CheckValue("-glversion");
if (version != NULL)
{
double gl_version = strtod(version, NULL) + 0.01;
int vermaj = (int)gl_version;
int vermin = (int)(gl_version*10.0) % 10;
while (glvers[glveridx][0] > vermaj || (glvers[glveridx][0] == vermaj &&
glvers[glveridx][1] > vermin))
{
glveridx++;
if (glvers[glveridx][0] == 0)
{
glveridx = 0;
break;
}
}
}
FString caption;
caption.Format(GAMESIG " %s (%s)", GetVersionString(), GetGitTime());
for ( ; glvers[glveridx][0] > 0; ++glveridx)
{
static_cast<SDLGLVideo*>(Video)->SetupPixelFormat(false, 0, glvers[glveridx]);
Screen = SDL_CreateWindow (caption,
SDL_WINDOWPOS_UNDEFINED_DISPLAY(vid_adapter),
SDL_WINDOWPOS_UNDEFINED_DISPLAY(vid_adapter),
width, height, (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0)|SDL_WINDOW_OPENGL
);
if (Screen != NULL)
{
GLContext = SDL_GL_CreateContext(Screen);
if (GLContext != NULL)
{
m_supportsGamma = -1 != SDL_GetWindowGammaRamp(Screen,
m_origGamma[0], m_origGamma[1], m_origGamma[2]
);
return;
}
SDL_DestroyWindow(Screen);
Screen = NULL;
}
}
}
SDLGLFB::~SDLGLFB ()
{
if (Screen)
{
ResetGammaTable();
if (GLContext)
{
SDL_GL_DeleteContext(GLContext);
}
SDL_DestroyWindow(Screen);
}
}
void SDLGLFB::InitializeState()
{
}
void SDLGLFB::SetGammaTable(uint16_t *tbl)
{
if (m_supportsGamma)
{
SDL_SetWindowGammaRamp(Screen, &tbl[0], &tbl[256], &tbl[512]);
}
}
void SDLGLFB::ResetGammaTable()
{
if (m_supportsGamma)
{
SDL_SetWindowGammaRamp(Screen, m_origGamma[0], m_origGamma[1], m_origGamma[2]);
}
}
bool SDLGLFB::Lock(bool buffered)
{
m_Lock++;
Buffer = MemBuffer;
return true;
}
bool SDLGLFB::Lock ()
{
return Lock(false);
}
void SDLGLFB::Unlock ()
{
if (UpdatePending && m_Lock == 1)
{
Update ();
}
else if (--m_Lock <= 0)
{
m_Lock = 0;
}
}
bool SDLGLFB::IsLocked ()
{
return m_Lock>0;// true;
}
bool SDLGLFB::IsFullscreen ()
{
return (SDL_GetWindowFlags (Screen) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
}
bool SDLGLFB::IsValid ()
{
return DFrameBuffer::IsValid() && Screen != NULL;
}
void SDLGLFB::SetVSync( bool vsync )
{
#if defined (__APPLE__)
const GLint value = vsync ? 1 : 0;
CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval, &value );
#else
if (vsync)
{
if (SDL_GL_SetSwapInterval(-1) == -1)
SDL_GL_SetSwapInterval(1);
}
else
{
SDL_GL_SetSwapInterval(0);
}
#endif
}
void SDLGLFB::NewRefreshRate ()
{
}
void SDLGLFB::SwapBuffers()
{
#if !defined(__APPLE__) && !defined(__OpenBSD__)
if (vid_maxfps && !cl_capfps)
{
SEMAPHORE_WAIT(FPSLimitSemaphore)
}
#endif
SDL_GL_SwapWindow (Screen);
}
int SDLGLFB::GetClientWidth()
{
int width = 0;
SDL_GL_GetDrawableSize(Screen, &width, nullptr);
return width;
}
int SDLGLFB::GetClientHeight()
{
int height = 0;
SDL_GL_GetDrawableSize(Screen, nullptr, &height);
return height;
}
void SDLGLFB::ScaleCoordsFromWindow(int16_t &x, int16_t &y)
{
int w, h;
SDL_GetWindowSize (Screen, &w, &h);
// Detect if we're doing scaling in the Window and adjust the mouse
// coordinates accordingly. This could be more efficent, but I
// don't think performance is an issue in the menus.
if(IsFullscreen())
{
int realw = w, realh = h;
ScaleWithAspect (realw, realh, SCREENWIDTH, SCREENHEIGHT);
if (realw != SCREENWIDTH || realh != SCREENHEIGHT)
{
double xratio = (double)SCREENWIDTH/realw;
double yratio = (double)SCREENHEIGHT/realh;
if (realw < w)
{
x = (x - (w - realw)/2)*xratio;
y *= yratio;
}
else
{
y = (y - (h - realh)/2)*yratio;
x *= xratio;
}
}
}
else
{
x = (int16_t)(x*Width/w);
y = (int16_t)(y*Height/h);
}
}

View file

@ -0,0 +1,580 @@
/*
** sdlvideo.cpp
**
**---------------------------------------------------------------------------
** Copyright 2005-2016 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
// HEADER FILES ------------------------------------------------------------
#include "doomtype.h"
#include "templates.h"
#include "i_system.h"
#include "i_video.h"
#include "v_video.h"
#include "v_pfx.h"
#include "stats.h"
#include "v_palette.h"
#include "sdlvideo.h"
#include "swrenderer/r_swrenderer.h"
#include "version.h"
#include <SDL.h>
#ifdef __APPLE__
#include <OpenGL/OpenGL.h>
#endif // __APPLE__
// MACROS ------------------------------------------------------------------
// TYPES -------------------------------------------------------------------
struct MiniModeInfo
{
uint16_t Width, Height;
};
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
extern IVideo *Video;
extern bool GUICapture;
EXTERN_CVAR (Float, Gamma)
EXTERN_CVAR (Int, vid_maxfps)
EXTERN_CVAR (Bool, cl_capfps)
EXTERN_CVAR (Bool, vid_vsync)
// PUBLIC DATA DEFINITIONS -------------------------------------------------
CVAR (Int, vid_adapter, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Int, vid_displaybits, 32, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, vid_forcesurface, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CUSTOM_CVAR (Float, rgamma, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
{
if (screen != NULL)
{
screen->SetGamma (Gamma);
}
}
CUSTOM_CVAR (Float, ggamma, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
{
if (screen != NULL)
{
screen->SetGamma (Gamma);
}
}
CUSTOM_CVAR (Float, bgamma, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
{
if (screen != NULL)
{
screen->SetGamma (Gamma);
}
}
// PRIVATE DATA DEFINITIONS ------------------------------------------------
static cycle_t BlitCycles;
static cycle_t SDLFlipCycles;
// CODE --------------------------------------------------------------------
// FrameBuffer implementation -----------------------------------------------
SDLFB::SDLFB (int width, int height, bool bgra, bool fullscreen, SDL_Window *oldwin)
: SDLBaseFB (width, height, bgra)
{
int i;
NeedPalUpdate = false;
NeedGammaUpdate = false;
UpdatePending = false;
NotPaletted = false;
FlashAmount = 0;
if (oldwin)
{
// In some cases (Mac OS X fullscreen) SDL2 doesn't like having multiple windows which
// appears to inevitably happen while compositor animations are running. So lets try
// to reuse the existing window.
Screen = oldwin;
SDL_SetWindowSize (Screen, width, height);
SetFullscreen (fullscreen);
}
else
{
FString caption;
caption.Format(GAMESIG " %s (%s)", GetVersionString(), GetGitTime());
#ifdef __ANDROID__
SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 ); // Defaults to 24 which is not needed and fails on old Tegras
#endif
Screen = SDL_CreateWindow (caption,
SDL_WINDOWPOS_UNDEFINED_DISPLAY(vid_adapter), SDL_WINDOWPOS_UNDEFINED_DISPLAY(vid_adapter),
width, height, (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0)|SDL_WINDOW_RESIZABLE);
if (Screen == NULL)
return;
}
Renderer = NULL;
Texture = NULL;
ResetSDLRenderer ();
for (i = 0; i < 256; i++)
{
GammaTable[0][i] = GammaTable[1][i] = GammaTable[2][i] = i;
}
memcpy (SourcePalette, GPalette.BaseColors, sizeof(PalEntry)*256);
UpdateColors ();
#ifdef __APPLE__
SetVSync (vid_vsync);
#endif
}
SDLFB::~SDLFB ()
{
if (Renderer)
{
if (Texture)
SDL_DestroyTexture (Texture);
SDL_DestroyRenderer (Renderer);
}
if(Screen)
{
SDL_DestroyWindow (Screen);
}
}
bool SDLFB::IsValid ()
{
return DFrameBuffer::IsValid() && Screen != NULL;
}
int SDLFB::GetPageCount ()
{
return 1;
}
bool SDLFB::Lock (bool buffered)
{
return DSimpleCanvas::Lock ();
}
bool SDLFB::Relock ()
{
return DSimpleCanvas::Lock ();
}
void SDLFB::Unlock ()
{
if (UpdatePending && LockCount == 1)
{
Update ();
}
else if (--LockCount <= 0)
{
Buffer = NULL;
LockCount = 0;
}
}
void SDLFB::Update ()
{
if (LockCount != 1)
{
if (LockCount > 0)
{
UpdatePending = true;
--LockCount;
}
return;
}
DrawRateStuff ();
#if !defined(__APPLE__) && !defined(__OpenBSD__)
if(vid_maxfps && !cl_capfps)
{
SEMAPHORE_WAIT(FPSLimitSemaphore)
}
#endif
Buffer = NULL;
LockCount = 0;
UpdatePending = false;
BlitCycles.Reset();
SDLFlipCycles.Reset();
BlitCycles.Clock();
void *pixels;
int pitch;
if (UsingRenderer)
{
if (SDL_LockTexture (Texture, NULL, &pixels, &pitch))
return;
}
else
{
if (SDL_LockSurface (Surface))
return;
pixels = Surface->pixels;
pitch = Surface->pitch;
}
if (Bgra)
{
CopyWithGammaBgra(pixels, pitch, GammaTable[0], GammaTable[1], GammaTable[2], Flash, FlashAmount);
}
else if (NotPaletted)
{
GPfx.Convert (MemBuffer, Pitch,
pixels, pitch, Width, Height,
FRACUNIT, FRACUNIT, 0, 0);
}
else
{
if (pitch == Pitch)
{
memcpy (pixels, MemBuffer, Width*Height);
}
else
{
for (int y = 0; y < Height; ++y)
{
memcpy ((uint8_t *)pixels+y*pitch, MemBuffer+y*Pitch, Width);
}
}
}
if (UsingRenderer)
{
SDL_UnlockTexture (Texture);
SDLFlipCycles.Clock();
SDL_RenderClear(Renderer);
SDL_RenderCopy(Renderer, Texture, NULL, NULL);
SDL_RenderPresent(Renderer);
SDLFlipCycles.Unclock();
}
else
{
SDL_UnlockSurface (Surface);
SDLFlipCycles.Clock();
SDL_UpdateWindowSurface (Screen);
SDLFlipCycles.Unclock();
}
BlitCycles.Unclock();
if (NeedGammaUpdate)
{
bool Windowed = false;
NeedGammaUpdate = false;
CalcGamma ((Windowed || rgamma == 0.f) ? Gamma : (Gamma * rgamma), GammaTable[0]);
CalcGamma ((Windowed || ggamma == 0.f) ? Gamma : (Gamma * ggamma), GammaTable[1]);
CalcGamma ((Windowed || bgamma == 0.f) ? Gamma : (Gamma * bgamma), GammaTable[2]);
NeedPalUpdate = true;
}
if (NeedPalUpdate)
{
NeedPalUpdate = false;
UpdateColors ();
}
}
void SDLFB::UpdateColors ()
{
if (NotPaletted)
{
PalEntry palette[256];
for (int i = 0; i < 256; ++i)
{
palette[i].r = GammaTable[0][SourcePalette[i].r];
palette[i].g = GammaTable[1][SourcePalette[i].g];
palette[i].b = GammaTable[2][SourcePalette[i].b];
}
if (FlashAmount)
{
DoBlending (palette, palette,
256, GammaTable[0][Flash.r], GammaTable[1][Flash.g], GammaTable[2][Flash.b],
FlashAmount);
}
GPfx.SetPalette (palette);
}
else
{
SDL_Color colors[256];
for (int i = 0; i < 256; ++i)
{
colors[i].r = GammaTable[0][SourcePalette[i].r];
colors[i].g = GammaTable[1][SourcePalette[i].g];
colors[i].b = GammaTable[2][SourcePalette[i].b];
}
if (FlashAmount)
{
DoBlending ((PalEntry *)colors, (PalEntry *)colors,
256, GammaTable[2][Flash.b], GammaTable[1][Flash.g], GammaTable[0][Flash.r],
FlashAmount);
}
SDL_SetPaletteColors (Surface->format->palette, colors, 0, 256);
}
}
PalEntry *SDLFB::GetPalette ()
{
return SourcePalette;
}
void SDLFB::UpdatePalette ()
{
NeedPalUpdate = true;
}
bool SDLFB::SetGamma (float gamma)
{
Gamma = gamma;
NeedGammaUpdate = true;
return true;
}
bool SDLFB::SetFlash (PalEntry rgb, int amount)
{
Flash = rgb;
FlashAmount = amount;
NeedPalUpdate = true;
return true;
}
void SDLFB::GetFlash (PalEntry &rgb, int &amount)
{
rgb = Flash;
amount = FlashAmount;
}
// Q: Should I gamma adjust the returned palette?
void SDLFB::GetFlashedPalette (PalEntry pal[256])
{
memcpy (pal, SourcePalette, 256*sizeof(PalEntry));
if (FlashAmount)
{
DoBlending (pal, pal, 256, Flash.r, Flash.g, Flash.b, FlashAmount);
}
}
void SDLFB::SetFullscreen (bool fullscreen)
{
if (IsFullscreen() == fullscreen)
return;
SDL_SetWindowFullscreen (Screen, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
if (!fullscreen)
{
// Restore proper window size
SDL_SetWindowSize (Screen, Width, Height);
}
ResetSDLRenderer ();
}
bool SDLFB::IsFullscreen ()
{
return (SDL_GetWindowFlags (Screen) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
}
void SDLFB::ResetSDLRenderer ()
{
if (Renderer)
{
if (Texture)
SDL_DestroyTexture (Texture);
SDL_DestroyRenderer (Renderer);
}
UsingRenderer = !vid_forcesurface;
if (UsingRenderer)
{
#ifdef __MOBILE__
Renderer = SDL_CreateRenderer (Screen, -1,SDL_RENDERER_ACCELERATED);
#else
Renderer = SDL_CreateRenderer (Screen, -1,SDL_RENDERER_ACCELERATED|SDL_RENDERER_TARGETTEXTURE|
(vid_vsync ? SDL_RENDERER_PRESENTVSYNC : 0));
#endif
if (!Renderer)
return;
SDL_SetRenderDrawColor(Renderer, 0, 0, 0, 255);
Uint32 fmt;
if (Bgra)
{
fmt = SDL_PIXELFORMAT_ARGB8888;
}
else
{
switch (vid_displaybits)
{
default: fmt = SDL_PIXELFORMAT_ARGB8888; break;
case 30: fmt = SDL_PIXELFORMAT_ARGB2101010; break;
case 24: fmt = SDL_PIXELFORMAT_RGB888; break;
case 16: fmt = SDL_PIXELFORMAT_RGB565; break;
case 15: fmt = SDL_PIXELFORMAT_ARGB1555; break;
}
}
Texture = SDL_CreateTexture (Renderer, fmt, SDL_TEXTUREACCESS_STREAMING, Width, Height);
{
NotPaletted = true;
Uint32 format;
SDL_QueryTexture(Texture, &format, NULL, NULL, NULL);
Uint32 Rmask, Gmask, Bmask, Amask;
int bpp;
SDL_PixelFormatEnumToMasks(format, &bpp, &Rmask, &Gmask, &Bmask, &Amask);
GPfx.SetFormat (bpp, Rmask, Gmask, Bmask);
}
}
else
{
Surface = SDL_GetWindowSurface (Screen);
if (Surface->format->palette == NULL)
{
NotPaletted = true;
GPfx.SetFormat (Surface->format->BitsPerPixel, Surface->format->Rmask, Surface->format->Gmask, Surface->format->Bmask);
}
else
NotPaletted = false;
}
// In fullscreen, set logical size according to animorphic ratio.
// Windowed modes are rendered to fill the window (usually 1:1)
if (IsFullscreen ())
{
int w, h;
SDL_GetWindowSize (Screen, &w, &h);
ScaleWithAspect (w, h, Width, Height);
#ifndef __MOBILE__
SDL_RenderSetLogicalSize (Renderer, w, h);
#endif
}
}
void SDLFB::SetVSync (bool vsync)
{
#ifdef __APPLE__
if (CGLContextObj context = CGLGetCurrentContext())
{
// Apply vsync for native backend only (where OpenGL context is set)
const GLint value = vsync ? 1 : 0;
CGLSetParameter(context, kCGLCPSwapInterval, &value);
}
#else
ResetSDLRenderer ();
#endif // __APPLE__
}
void SDLFB::ScaleCoordsFromWindow(int16_t &x, int16_t &y)
{
int w, h;
SDL_GetWindowSize (Screen, &w, &h);
// Detect if we're doing scaling in the Window and adjust the mouse
// coordinates accordingly. This could be more efficent, but I
// don't think performance is an issue in the menus.
if(IsFullscreen())
{
int realw = w, realh = h;
ScaleWithAspect (realw, realh, SCREENWIDTH, SCREENHEIGHT);
if (realw != SCREENWIDTH || realh != SCREENHEIGHT)
{
double xratio = (double)SCREENWIDTH/realw;
double yratio = (double)SCREENHEIGHT/realh;
if (realw < w)
{
x = (x - (w - realw)/2)*xratio;
y *= yratio;
}
else
{
y = (y - (h - realh)/2)*yratio;
x *= xratio;
}
}
}
else
{
x = (int16_t)(x*Width/w);
y = (int16_t)(y*Height/h);
}
}
ADD_STAT (blit)
{
FString out;
out.Format ("blit=%04.1f ms flip=%04.1f ms",
BlitCycles.TimeMS(), SDLFlipCycles.TimeMS());
return out;
}
// each platform has its own specific version of this function.
void I_SetWindowTitle(const char* caption)
{
auto Screen = static_cast<SDLFB *>(screen)->GetSDLWindow();
if (caption)
SDL_SetWindowTitle(static_cast<SDLFB *>(screen)->GetSDLWindow(), caption);
else
{
FString default_caption;
default_caption.Format(GAMESIG " %s (%s)", GetVersionString(), GetGitTime());
SDL_SetWindowTitle(static_cast<SDLFB *>(screen)->GetSDLWindow(), default_caption);
}
}

View file

@ -0,0 +1,371 @@
/*
** st_start.cpp
** Handles the startup screen.
**
**---------------------------------------------------------------------------
** Copyright 2006-2007 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
// HEADER FILES ------------------------------------------------------------
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <termios.h>
#include "st_start.h"
#include "doomdef.h"
#include "i_system.h"
#include "c_cvars.h"
#include "doomerrors.h"
#ifdef __ANDROID__
#include "JNITouchControlsUtils.h"
#include <android/log.h>
#define LOG_TAG "JWZGLES"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,"NETOWRK",__VA_ARGS__)
#define fprintf my_fprintf
void my_fprintf(FILE * x, const char *format, ...)
{
FString str;
va_list argptr;
va_start (argptr, format);
str.VFormat (format, argptr);
va_end (argptr);
//fprintf (stderr, "\r%-40s\n", str.GetChars());
addTextConsoleBox(str.GetChars());
}
#endif
// MACROS ------------------------------------------------------------------
// TYPES -------------------------------------------------------------------
class FTTYStartupScreen : public FStartupScreen
{
public:
FTTYStartupScreen(int max_progress);
~FTTYStartupScreen();
void Progress();
void NetInit(const char *message, int num_players);
void NetProgress(int count);
void NetMessage(const char *format, ...); // cover for printf
void NetDone();
bool NetLoop(bool (*timer_callback)(void *), void *userdata);
protected:
bool DidNetInit;
int NetMaxPos, NetCurPos;
const char *TheNetMessage;
termios OldTermIOS;
};
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
// PUBLIC DATA DEFINITIONS -------------------------------------------------
FStartupScreen *StartScreen;
CUSTOM_CVAR(Int, showendoom, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
{
if (self < 0) self = 0;
else if (self > 2) self=2;
}
// PRIVATE DATA DEFINITIONS ------------------------------------------------
static const char SpinnyProgressChars[4] = { '|', '/', '-', '\\' };
// CODE --------------------------------------------------------------------
//==========================================================================
//
// FStartupScreen :: CreateInstance
//
// Initializes the startup screen for the detected game.
// Sets the size of the progress bar and displays the startup screen.
//
//==========================================================================
FStartupScreen *FStartupScreen::CreateInstance(int max_progress)
{
return new FTTYStartupScreen(max_progress);
}
//===========================================================================
//
// FTTYStartupScreen Constructor
//
// Sets the size of the progress bar and displays the startup screen.
//
//===========================================================================
FTTYStartupScreen::FTTYStartupScreen(int max_progress)
: FStartupScreen(max_progress)
{
DidNetInit = false;
NetMaxPos = 0;
NetCurPos = 0;
TheNetMessage = NULL;
}
//===========================================================================
//
// FTTYStartupScreen Destructor
//
// Called just before entering graphics mode to deconstruct the startup
// screen.
//
//===========================================================================
FTTYStartupScreen::~FTTYStartupScreen()
{
NetDone(); // Just in case it wasn't called yet and needs to be.
}
//===========================================================================
//
// FTTYStartupScreen :: Progress
//
// If there was a progress bar, this would move it. But the basic TTY
// startup screen doesn't have one, so this function does nothing.
//
//===========================================================================
void FTTYStartupScreen::Progress()
{
}
//===========================================================================
//
// FTTYStartupScreen :: NetInit
//
// Sets stdin for unbuffered I/O, displays the given message, and shows
// a progress meter.
//
//===========================================================================
void FTTYStartupScreen::NetInit(const char *message, int numplayers)
{
if (!DidNetInit)
{
termios rawtermios;
fprintf (stderr, "Press 'Q' to abort network game synchronization.");
// Set stdin to raw mode so we can get keypresses in ST_CheckNetAbort()
// immediately without waiting for an EOL.
tcgetattr (STDIN_FILENO, &OldTermIOS);
rawtermios = OldTermIOS;
rawtermios.c_lflag &= ~(ICANON | ECHO);
tcsetattr (STDIN_FILENO, TCSANOW, &rawtermios);
DidNetInit = true;
#ifdef __ANDROID__
openConsoleBox( "Network synchronization" );
#endif
}
if (numplayers == 1)
{
// Status message without any real progress info.
fprintf (stderr, "\n%s.", message);
}
else
{
fprintf (stderr, "\n%s: ", message);
}
fflush (stderr);
TheNetMessage = message;
NetMaxPos = numplayers;
NetCurPos = 0;
NetProgress(1); // You always know about yourself
}
//===========================================================================
//
// FTTYStartupScreen :: NetDone
//
// Restores the old stdin tty settings.
//
//===========================================================================
void FTTYStartupScreen::NetDone()
{
// Restore stdin settings
if (DidNetInit)
{
tcsetattr (STDIN_FILENO, TCSANOW, &OldTermIOS);
printf ("\n");
DidNetInit = false;
#ifdef __ANDROID__
closeConsoleBox();
#endif
}
}
//===========================================================================
//
// FTTYStartupScreen :: NetMessage
//
// Call this between NetInit() and NetDone() instead of Printf() to
// display messages, because the progress meter is mixed in the same output
// stream as normal messages.
//
//===========================================================================
void FTTYStartupScreen::NetMessage(const char *format, ...)
{
FString str;
va_list argptr;
va_start (argptr, format);
str.VFormat (format, argptr);
va_end (argptr);
fprintf (stderr, "\r%-40s\n", str.GetChars());
}
//===========================================================================
//
// FTTYStartupScreen :: NetProgress
//
// Sets the network progress meter. If count is 0, it gets bumped by 1.
// Otherwise, it is set to count.
//
//===========================================================================
void FTTYStartupScreen::NetProgress(int count)
{
int i;
if (count == 0)
{
NetCurPos++;
}
else if (count > 0)
{
NetCurPos = count;
}
if (NetMaxPos == 0)
{
// Spinny-type progress meter, because we're a guest waiting for the host.
fprintf (stderr, "\r%s: %c", TheNetMessage, SpinnyProgressChars[NetCurPos & 3]);
fflush (stderr);
}
else if (NetMaxPos > 1)
{
// Dotty-type progress meter.
fprintf (stderr, "\r%s: ", TheNetMessage);
for (i = 0; i < NetCurPos; ++i)
{
fputc ('.', stderr);
}
fprintf (stderr, "%*c[%2d/%2d]", NetMaxPos + 1 - NetCurPos, ' ', NetCurPos, NetMaxPos);
fflush (stderr);
}
}
//===========================================================================
//
// FTTYStartupScreen :: NetLoop
//
// The timer_callback function is called at least two times per second
// and passed the userdata value. It should return true to stop the loop and
// return control to the caller or false to continue the loop.
//
// ST_NetLoop will return true if the loop was halted by the callback and
// false if the loop was halted because the user wants to abort the
// network synchronization.
//
//===========================================================================
bool FTTYStartupScreen::NetLoop(bool (*timer_callback)(void *), void *userdata)
{
fd_set rfds;
struct timeval tv;
int retval;
char k;
for (;;)
{
// Don't flood the network with packets on startup.
tv.tv_sec = 0;
tv.tv_usec = 500000;
FD_ZERO (&rfds);
FD_SET (STDIN_FILENO, &rfds);
retval = select (1, &rfds, NULL, NULL, &tv);
#ifdef __ANDROID__
usleep(1000*200);
//The select command is to wait for the console input to be ready, obv don't need this on droid
retval = 0;
if( getConsoleBoxCanceled() )
{
return false;
}
#endif
if (retval == -1)
{
// Error
}
else if (retval == 0)
{
if (timer_callback (userdata))
{
fputc ('\n', stderr);
return true;
}
}
else if (read (STDIN_FILENO, &k, 1) == 1)
{
// Check input on stdin
if (k == 'q' || k == 'Q')
{
fprintf (stderr, "\nNetwork game synchronization aborted.");
return false;
}
}
}
}
void ST_Endoom()
{
throw CExitEvent(0);
}

View file

@ -79,7 +79,7 @@ const char *GetVersionString();
#define SAVEGAME_EXT "zds"
// MINSAVEVER is the minimum level snapshot version that can be loaded.
#define MINSAVEVER 4555
#define MINSAVEVER 4555
// Use 4500 as the base git save version, since it's higher than the
// SVN revision ever got.
@ -98,7 +98,10 @@ const char *GetVersionString();
// More stuff that needs to be different for derivatives.
#define GAMENAME "QZDoom"
#define WGAMENAME L"QZDoom"
#define GAMENAMELOWERCASE "qzdoom"
#define FORUM_URL "http://forum.zdoom.org/"
#define BUGS_FORUM_URL "http://forum.zdoom.org/viewforum.php?f=2"
#if defined(__APPLE__) || defined(_WIN32)
#define GAME_DIR GAMENAME

View file

@ -2829,6 +2829,10 @@ void D3DFB::DrawTextureParms (FTexture *img, DrawParms &parms)
{
swapvalues(u0, u1);
}
if (parms.flipY)
{
swapvalues(v0, v1);
}
if (parms.windowleft > 0 || parms.windowright < parms.texwidth)
{
double wi = MIN(parms.windowright, parms.texwidth);

View file

@ -59,7 +59,6 @@
#include "cmdlib.h"
#include "v_text.h"
#include "m_argv.h"
#include "rawinput.h"
#define SAFE_RELEASE(x) { if (x != NULL) { x->Release(); x = NULL; } }
@ -263,8 +262,6 @@ protected:
static BOOL CALLBACK EnumCallback(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef);
static int NameSort(const void *a, const void *b);
static bool IsXInputDevice(const GUID *guid);
static bool IsXInputDeviceFast(const GUID *guid);
static bool IsXInputDeviceSlow(const GUID *guid);
};
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
@ -1208,140 +1205,6 @@ BOOL CALLBACK FDInputJoystickManager::EnumCallback(LPCDIDEVICEINSTANCE lpddi, LP
return DIENUM_CONTINUE;
}
//===========================================================================
//
// FDInputJoystickManager :: IsXInputDevice STATIC
//
// Does the DirectInput product GUID correspond to an XInput controller?
// If the product's device ID contains contains "IG_"
// (ex. "VID_045E&PID_028E&IG_00"), then it is an XInput device.
// Unfortunately this information can not be found by just using DirectInput.
//
//===========================================================================
bool FDInputJoystickManager::IsXInputDevice(const GUID *guid)
{
if (MyGetRawInputDeviceList == NULL || MyGetRawInputDeviceInfoA == NULL)
{
return IsXInputDeviceSlow(guid);
}
else
{
return IsXInputDeviceFast(guid);
}
}
//===========================================================================
//
// FDInputJoystickManager :: IsXInputDeviceSlow STATIC
//
// Pretty much copied straight from the article "XInput and DirectInput".
//
// Enum each PNP device using WMI and check each device ID. This is
// Microsoft's reference implementation, but it is damn slow. After
// a hardware change, connecting to the WMI server can take nearly three
// seconds, and creating the instance enumerator consistantly takes longer
// than 0.25 seconds.
//
// The XInput DLL can apparently be hacked fairly simply to work with
// Windows 2000, and since Raw Input wasn't introduced until XP, I think
// that's reason enough to keep this version around, despite it being
// so horrendously slow.
//
//===========================================================================
bool FDInputJoystickManager::IsXInputDeviceSlow(const GUID *guid)
{
IWbemLocator *wbemlocator = NULL;
IEnumWbemClassObject *enumdevices = NULL;
IWbemClassObject *devices[20] = { 0 };
IWbemServices *wbemservices = NULL;
BSTR namespce = NULL;
BSTR deviceid = NULL;
BSTR classname = NULL;
DWORD returned = 0;
bool isxinput = false;
UINT device = 0;
VARIANT var;
HRESULT hr;
// Create WMI
hr = CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&wbemlocator);
if (FAILED(hr) || wbemlocator == NULL)
goto cleanup;
if (NULL == (namespce = SysAllocString(OLESTR("\\\\.\\root\\cimv2")))) goto cleanup;
if (NULL == (classname = SysAllocString(OLESTR("Win32_PNPEntity")))) goto cleanup;
if (NULL == (deviceid = SysAllocString(OLESTR("DeviceID")))) goto cleanup;
// Connect to WMI
hr = wbemlocator->ConnectServer(namespce, NULL, NULL, 0, 0, NULL, NULL, &wbemservices);
if (FAILED(hr) || wbemservices == NULL)
goto cleanup;
// Switch security level to IMPERSONATE.
CoSetProxyBlanket(wbemservices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL,
RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
hr = wbemservices->CreateInstanceEnum(classname, 0, NULL, &enumdevices);
if (FAILED(hr) || enumdevices == NULL)
goto cleanup;
// Loop over all devices
for (;;)
{
// Get 20 at a time.
hr = enumdevices->Next(10000, countof(devices), devices, &returned);
if (FAILED(hr))
goto cleanup;
if (returned == 0)
break;
for (device = 0; device < returned; device++)
{
// For each device, get its device ID.
hr = devices[device]->Get(deviceid, 0L, &var, NULL, NULL);
if (SUCCEEDED(hr) && var.vt == VT_BSTR && var.bstrVal != NULL)
{
// Check if the device ID contains "IG_". If it does, then it's an XInput device.
// This information cannot be found from DirectInput.
if (wcsstr(var.bstrVal, L"IG_"))
{
// If it does, then get the VID/PID from var.bstrVal.
DWORD pid = 0, vid = 0;
WCHAR *strvid = wcsstr(var.bstrVal, L"VID_");
if (strvid && swscanf(strvid, L"VID_%4X", &vid) != 1)
vid = 0;
WCHAR *strpid = wcsstr(var.bstrVal, L"PID_");
if (strpid && swscanf(strpid, L"PID_%4X", &pid) != 1)
pid = 0;
// Compare the VID/PID to the DInput device.
DWORD vidpid = MAKELONG(vid, pid);
if (vidpid == guid->Data1)
{
isxinput = true;
goto cleanup;
}
}
}
SAFE_RELEASE(devices[device]);
}
}
cleanup:
if (namespce) SysFreeString(namespce);
if (deviceid) SysFreeString(deviceid);
if (classname) SysFreeString(classname);
for (device = 0; device < countof(devices); ++device)
SAFE_RELEASE(devices[device]);
SAFE_RELEASE(enumdevices);
SAFE_RELEASE(wbemlocator);
SAFE_RELEASE(wbemservices);
return isxinput;
}
//===========================================================================
//
// FDInputJoystickManager :: IsXInputDeviceFast STATIC
@ -1354,14 +1217,14 @@ cleanup:
//
//===========================================================================
bool FDInputJoystickManager::IsXInputDeviceFast(const GUID *guid)
bool FDInputJoystickManager::IsXInputDevice(const GUID *guid)
{
UINT nDevices, numDevices;
RAWINPUTDEVICELIST *devices;
UINT i;
bool isxinput = false;
if (MyGetRawInputDeviceList(NULL, &nDevices, sizeof(RAWINPUTDEVICELIST)) != 0)
if (GetRawInputDeviceList(NULL, &nDevices, sizeof(RAWINPUTDEVICELIST)) != 0)
{
return false;
}
@ -1369,7 +1232,7 @@ bool FDInputJoystickManager::IsXInputDeviceFast(const GUID *guid)
{
return false;
}
if ((numDevices = MyGetRawInputDeviceList(devices, &nDevices, sizeof(RAWINPUTDEVICELIST))) == (UINT)-1)
if ((numDevices = GetRawInputDeviceList(devices, &nDevices, sizeof(RAWINPUTDEVICELIST))) == (UINT)-1)
{
free(devices);
return false;
@ -1385,7 +1248,7 @@ bool FDInputJoystickManager::IsXInputDeviceFast(const GUID *guid)
UINT cbSize;
cbSize = rdi.cbSize = sizeof(rdi);
if ((INT)MyGetRawInputDeviceInfoA(devices[i].hDevice, RIDI_DEVICEINFO, &rdi, &cbSize) >= 0)
if ((INT)GetRawInputDeviceInfoA(devices[i].hDevice, RIDI_DEVICEINFO, &rdi, &cbSize) >= 0)
{
if(MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) == (LONG)guid->Data1)
{
@ -1393,7 +1256,7 @@ bool FDInputJoystickManager::IsXInputDeviceFast(const GUID *guid)
UINT namelen = countof(name);
UINT reslen;
reslen = MyGetRawInputDeviceInfoA(devices[i].hDevice, RIDI_DEVICENAME, name, &namelen);
reslen = GetRawInputDeviceInfoA(devices[i].hDevice, RIDI_DEVICENAME, name, &namelen);
if (reslen != (UINT)-1)
{
isxinput = (strstr(name, "IG_") != NULL);

View file

@ -45,6 +45,7 @@
#ifndef __GNUC__
#define INITGUID
#endif
#define DIRECTINPUT_VERSION 0x800
#include <windows.h>
#include <dbt.h>
#include <dinput.h>
@ -91,13 +92,6 @@
#include "events.h"
#include "doomerrors.h"
// Prototypes and declarations.
#include "rawinput.h"
// Definitions
#define RIF(name, ret, args) \
name##Proto My##name;
#include "rawinput.h"
// Compensate for w32api's lack
#ifndef GET_XBUTTON_WPARAM
@ -112,7 +106,6 @@
#define INGAME_PRIORITY_CLASS NORMAL_PRIORITY_CLASS
#endif
static void FindRawInputFunctions();
FJoystickCollection *JoyDevices[NUM_JOYDEVICES];
@ -152,11 +145,6 @@ int BlockMouseMove;
CVAR (Bool, i_soundinbackground, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, k_allowfullscreentoggle, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CUSTOM_CVAR(Bool, norawinput, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL)
{
Printf("This won't take effect until " GAMENAME " is restarted.\n");
}
extern int chatmodeon;
static void I_CheckGUICapture ()
@ -378,25 +366,22 @@ LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
if (message == WM_INPUT)
{
if (MyGetRawInputData != NULL)
{
UINT size;
UINT size;
if (!MyGetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)) &&
size != 0)
if (!GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)) &&
size != 0)
{
uint8_t *buffer = (uint8_t *)alloca(size);
if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, buffer, &size, sizeof(RAWINPUTHEADER)) == size)
{
uint8_t *buffer = (uint8_t *)alloca(size);
if (MyGetRawInputData((HRAWINPUT)lParam, RID_INPUT, buffer, &size, sizeof(RAWINPUTHEADER)) == size)
int code = GET_RAWINPUT_CODE_WPARAM(wParam);
if (Keyboard == NULL || !Keyboard->ProcessRawInput((RAWINPUT *)buffer, code))
{
int code = GET_RAWINPUT_CODE_WPARAM(wParam);
if (Keyboard == NULL || !Keyboard->ProcessRawInput((RAWINPUT *)buffer, code))
if (Mouse == NULL || !Mouse->ProcessRawInput((RAWINPUT *)buffer, code))
{
if (Mouse == NULL || !Mouse->ProcessRawInput((RAWINPUT *)buffer, code))
if (JoyDevices[INPUT_RawPS2] != NULL)
{
if (JoyDevices[INPUT_RawPS2] != NULL)
{
JoyDevices[INPUT_RawPS2]->ProcessRawInput((RAWINPUT *)buffer, code);
}
JoyDevices[INPUT_RawPS2]->ProcessRawInput((RAWINPUT *)buffer, code);
}
}
}
@ -651,8 +636,6 @@ bool I_InitInput (void *hwnd)
g_pdi = NULL;
g_pdi3 = NULL;
FindRawInputFunctions();
// Try for DirectInput 8 first, then DirectInput 3 for NT 4's benefit.
DInputDLL = LoadLibraryA("dinput8.dll");
if (DInputDLL != NULL)
@ -966,26 +949,3 @@ bool FInputDevice::WndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lP
return false;
}
//==========================================================================
//
// FindRawInputFunctions
//
// Finds functions for raw input, if available.
//
//==========================================================================
static void FindRawInputFunctions()
{
if (!norawinput)
{
HMODULE user32 = GetModuleHandleA("user32.dll");
if (user32 == NULL)
{
return; // WTF kind of broken system are we running on?
}
#define RIF(name,ret,args) \
My##name = (name##Proto)GetProcAddress(user32, #name);
#include "rawinput.h"
}
}

View file

@ -42,12 +42,8 @@
#include "i_input.h"
#include "i_system.h"
#include "d_event.h"
#include "d_gui.h"
#include "c_cvars.h"
#include "doomdef.h"
#include "doomstat.h"
#include "win32iface.h"
#include "rawinput.h"
// MACROS ------------------------------------------------------------------
@ -430,15 +426,12 @@ FRawKeyboard::FRawKeyboard()
FRawKeyboard::~FRawKeyboard()
{
if (MyRegisterRawInputDevices != NULL)
{
RAWINPUTDEVICE rid;
rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE;
rid.usUsage = HID_GDP_KEYBOARD;
rid.dwFlags = RIDEV_REMOVE;
rid.hwndTarget = NULL;
MyRegisterRawInputDevices(&rid, 1, sizeof(rid));
}
RAWINPUTDEVICE rid;
rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE;
rid.usUsage = HID_GDP_KEYBOARD;
rid.dwFlags = RIDEV_REMOVE;
rid.hwndTarget = NULL;
RegisterRawInputDevices(&rid, 1, sizeof(rid));
}
//==========================================================================
@ -453,15 +446,11 @@ bool FRawKeyboard::GetDevice()
{
RAWINPUTDEVICE rid;
if (MyRegisterRawInputDevices == NULL)
{
return false;
}
rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE;
rid.usUsage = HID_GDP_KEYBOARD;
rid.dwFlags = RIDEV_INPUTSINK;
rid.hwndTarget = Window;
if (!MyRegisterRawInputDevices(&rid, 1, sizeof(rid)))
if (!RegisterRawInputDevices(&rid, 1, sizeof(rid)))
{
return false;
}

View file

@ -43,11 +43,8 @@
#include "i_system.h"
#include "d_event.h"
#include "d_gui.h"
#include "c_cvars.h"
#include "doomdef.h"
#include "doomstat.h"
#include "win32iface.h"
#include "rawinput.h"
#include "menu/menu.h"
#include "events.h"
@ -550,21 +547,17 @@ bool FRawMouse::GetDevice()
{
RAWINPUTDEVICE rid;
if (MyRegisterRawInputDevices == NULL)
{
return false;
}
rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE;
rid.usUsage = HID_GDP_MOUSE;
rid.dwFlags = 0;
rid.hwndTarget = Window;
if (!MyRegisterRawInputDevices(&rid, 1, sizeof(rid)))
if (!RegisterRawInputDevices(&rid, 1, sizeof(rid)))
{
return false;
}
rid.dwFlags = RIDEV_REMOVE;
rid.hwndTarget = NULL; // Must be NULL for RIDEV_REMOVE.
MyRegisterRawInputDevices(&rid, 1, sizeof(rid));
RegisterRawInputDevices(&rid, 1, sizeof(rid));
return true;
}
@ -584,7 +577,7 @@ void FRawMouse::Grab()
rid.usUsage = HID_GDP_MOUSE;
rid.dwFlags = RIDEV_CAPTUREMOUSE | RIDEV_NOLEGACY;
rid.hwndTarget = Window;
if (MyRegisterRawInputDevices(&rid, 1, sizeof(rid)))
if (RegisterRawInputDevices(&rid, 1, sizeof(rid)))
{
GetCursorPos(&UngrabbedPointerPos);
Grabbed = true;
@ -613,7 +606,7 @@ void FRawMouse::Ungrab()
rid.usUsage = HID_GDP_MOUSE;
rid.dwFlags = RIDEV_REMOVE;
rid.hwndTarget = NULL;
if (MyRegisterRawInputDevices(&rid, 1, sizeof(rid)))
if (RegisterRawInputDevices(&rid, 1, sizeof(rid)))
{
Grabbed = false;
ClearButtonState();
@ -1189,18 +1182,6 @@ void I_StartupMouse ()
switch(in_mouse)
{
case 0:
default:
if (MyRegisterRawInputDevices != NULL)
{
new_mousemode = MM_RawInput;
}
else
{
new_mousemode = MM_DInput;
}
break;
case 1:
new_mousemode = MM_Win32;
break;
@ -1209,6 +1190,7 @@ void I_StartupMouse ()
new_mousemode = MM_DInput;
break;
default:
case 3:
new_mousemode = MM_RawInput;
break;

View file

@ -53,7 +53,6 @@
#include "cmdlib.h"
#include "v_text.h"
#include "m_argv.h"
#include "rawinput.h"
// MACROS ------------------------------------------------------------------
@ -913,23 +912,17 @@ bool FRawPS2Manager::GetDevice()
{
RAWINPUTDEVICE rid;
if (MyRegisterRawInputDevices == NULL ||
MyGetRawInputDeviceList == NULL ||
MyGetRawInputDeviceInfoA == NULL)
{
return false;
}
rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE;
rid.usUsage = HID_GDP_JOYSTICK;
rid.dwFlags = RIDEV_INPUTSINK;
rid.hwndTarget = Window;
if (!MyRegisterRawInputDevices(&rid, 1, sizeof(rid)))
if (!RegisterRawInputDevices(&rid, 1, sizeof(rid)))
{
return false;
}
rid.dwFlags = RIDEV_REMOVE;
rid.hwndTarget = NULL; // Must be NULL for RIDEV_REMOVE.
MyRegisterRawInputDevices(&rid, 1, sizeof(rid));
RegisterRawInputDevices(&rid, 1, sizeof(rid));
EnumDevices();
return true;
}
@ -1025,7 +1018,7 @@ FRawPS2Controller *FRawPS2Manager::EnumDevices()
RAWINPUTDEVICELIST *devices;
UINT i, j;
if (MyGetRawInputDeviceList(NULL, &nDevices, sizeof(RAWINPUTDEVICELIST)) != 0)
if (GetRawInputDeviceList(NULL, &nDevices, sizeof(RAWINPUTDEVICELIST)) != 0)
{
return NULL;
}
@ -1033,7 +1026,7 @@ FRawPS2Controller *FRawPS2Manager::EnumDevices()
{
return NULL;
}
if ((numDevices = MyGetRawInputDeviceList(devices, &nDevices, sizeof(RAWINPUTDEVICELIST))) == (UINT)-1)
if ((numDevices = GetRawInputDeviceList(devices, &nDevices, sizeof(RAWINPUTDEVICELIST))) == (UINT)-1)
{
free(devices);
return NULL;
@ -1049,7 +1042,7 @@ FRawPS2Controller *FRawPS2Manager::EnumDevices()
UINT cbSize;
cbSize = rdi.cbSize = sizeof(rdi);
if ((INT)MyGetRawInputDeviceInfoA(devices[i].hDevice, RIDI_DEVICEINFO, &rdi, &cbSize) >= 0)
if ((INT)GetRawInputDeviceInfoA(devices[i].hDevice, RIDI_DEVICEINFO, &rdi, &cbSize) >= 0)
{
// All the PS2 adapters report themselves as joysticks.
// (By comparison, the 360 controller reports itself as a gamepad.)
@ -1092,41 +1085,41 @@ FRawPS2Controller *FRawPS2Manager::EnumDevices()
// <Enumerator>#<Device ID>#<Device Interface Class GUID>
// The Device ID has multiple #-seperated parts and uniquely identifies
// this device and which USB port it is connected to.
char name[256];
wchar_t name[256];
UINT namelen = countof(name);
char *devid, *devidend;
wchar_t *devid, *devidend;
if (MyGetRawInputDeviceInfoA(devices[i].hDevice, RIDI_DEVICENAME, name, &namelen) == (UINT)-1)
if (GetRawInputDeviceInfoW(devices[i].hDevice, RIDI_DEVICENAME, name, &namelen) == (UINT)-1)
{ // Can't get name. Skip it, since there's stuff in there we need for config.
continue;
}
devid = strchr(name, '#');
devid = wcschr(name, '#');
if (devid == NULL)
{ // Should not happen
continue;
}
devidend = strrchr(++devid, '#');
devidend = wcsrchr(++devid, '#');
if (devidend != NULL)
{
*devidend = '\0';
}
FAdapterHandle handle = { devices[i].hDevice, type, 0, devid };
FAdapterHandle handle = { devices[i].hDevice, type, 0, FString(devid) };
// Adapters that support more than one controller have a seperate device
// entry for each controller. We can examine the name to determine which
// controller this device is for.
if (Descriptors[type].ControllerNumber >= 0)
{
char *col = strstr(devid, "&Col");
wchar_t *col = wcsstr(devid, L"&Col");
if (col != NULL)
{
// I have no idea if this number is base 16 or base 10. Every
// other number in the name is base 16, so I assume this one is
// too, but since I don't have anything that goes higher than 02,
// I can't be sure.
handle.ControllerNumber = strtoul(col + 4, NULL, 16);
handle.ControllerNumber = wcstoul(col + 4, NULL, 16);
}
}
adapters.Push(handle);
@ -1279,7 +1272,7 @@ void FRawPS2Manager::DoRegister()
{
rid.dwFlags = RIDEV_REMOVE;
rid.hwndTarget = NULL;
if (MyRegisterRawInputDevices(&rid, 1, sizeof(rid)))
if (RegisterRawInputDevices(&rid, 1, sizeof(rid)))
{
Registered = false;
}
@ -1291,7 +1284,7 @@ void FRawPS2Manager::DoRegister()
{
rid.dwFlags = RIDEV_INPUTSINK;
rid.hwndTarget = Window;
if (MyRegisterRawInputDevices(&rid, 1, sizeof(rid)))
if (RegisterRawInputDevices(&rid, 1, sizeof(rid)))
{
Registered = true;
}

View file

@ -869,7 +869,8 @@ bool Win32GLVideo::SetupPixelFormat(int multisample)
if (pfd.dwFlags & PFD_GENERIC_FORMAT)
{
vid_renderer = 0;
I_Error("R_OPENGL: OpenGL driver not accelerated! Falling back to software renderer.\n");
I_Error("R_OPENGL: OpenGL driver not accelerated! Falling back to software renderer. Get a driver from your manufacturer.\n"
"For some Intel cards run " GAMENAME " in Windows 8 compatibility mode or use the wtfi tool.\n");
return false;
}
}

View file

@ -1,11 +1,7 @@
cmake_minimum_required( VERSION 2.8.7 )
add_subdirectory( lemon )
add_subdirectory( re2c )
if( WIN32 AND NOT CMAKE_SIZEOF_VOID_P MATCHES "8" )
add_subdirectory( fixrtext )
endif()
add_subdirectory( updaterevision )
add_subdirectory( lemon )
add_subdirectory( zipdir )
set( CROSS_EXPORTS ${CROSS_EXPORTS} PARENT_SCOPE )

View file

@ -1,24 +0,0 @@
cmake_minimum_required( VERSION 2.8.7 )
if( WIN32 )
if( MSVC_VERSION GREATER 1399 )
# VC 8+ adds a manifest automatically to the executable. We need to
# merge ours with it.
set( MT_MERGE ON )
else()
set( TRUSTINFO trustinfo.rc )
endif()
else( WIN32 )
set( TRUSTINFO "" )
endif()
if( NOT CMAKE_CROSSCOMPILING )
add_executable( updaterevision updaterevision.c ${TRUSTINFO} )
set( CROSS_EXPORTS ${CROSS_EXPORTS} updaterevision PARENT_SCOPE )
endif()
if( MT_MERGE )
add_custom_command(TARGET updaterevision POST_BUILD
COMMAND mt -inputresource:$<TARGET_FILE:updaterevision> -manifest ${CMAKE_CURRENT_SOURCE_DIR}/trustinfo.txt -outputresource:$<TARGET_FILE:updaterevision> -nologo
COMMENT "Embedding trustinfo into updaterevision" )
endif()

View file

@ -0,0 +1,91 @@
#!/usr/bin/cmake -P
# UpdateRevision.cmake
#
# Public domain. This program uses git commands command to get
# various bits of repository status for a particular directory
# and writes it into a header file so that it can be used for a
# project's versioning.
# Boilerplate to return a variable from a function.
macro(ret_var VAR)
set(${VAR} "${${VAR}}" PARENT_SCOPE)
endmacro()
# Populate variables "Hash", "Tag", and "Timestamp" with relevant information
# from source repository. If anything goes wrong return something in "Error."
function(query_repo_info)
execute_process(
COMMAND git describe --tags --dirty=-m
RESULT_VARIABLE Error
OUTPUT_VARIABLE Tag
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(NOT "${Error}" STREQUAL "0")
ret_var(Error)
return()
endif()
execute_process(
COMMAND git log -1 "--format=%ai;%H"
RESULT_VARIABLE Error
OUTPUT_VARIABLE CommitInfo
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(NOT "${Error}" STREQUAL "0")
ret_var(Error)
return()
endif()
list(GET CommitInfo 0 Timestamp)
list(GET CommitInfo 1 Hash)
ret_var(Tag)
ret_var(Timestamp)
ret_var(Hash)
endfunction()
# Although configure_file doesn't overwrite the file if the contents are the
# same we can't easily observe that to change the status message. This
# function parses the existing file (if it exists) and puts the hash in
# variable "OldHash"
function(get_existing_hash File)
if(EXISTS "${File}")
file(STRINGS "${File}" OldHash LIMIT_COUNT 1)
if(OldHash)
string(SUBSTRING "${OldHash}" 3 -1 OldHash)
ret_var(OldHash)
endif()
endif()
endfunction()
function(main)
if(NOT CMAKE_ARGC EQUAL 4) # cmake -P UpdateRevision.cmake <OutputFile>
message("Usage: ${CMAKE_ARGV2} <path to gitinfo.h>")
return()
endif()
set(OutputFile "${CMAKE_ARGV3}")
get_filename_component(ScriptDir "${CMAKE_SCRIPT_MODE_FILE}" DIRECTORY)
query_repo_info()
if(NOT Hash)
message("Failed to get commit info: ${Error}")
set(Hash "0")
set(Tag "<unknown version>")
set(Timestamp "")
endif()
get_existing_hash("${OutputFile}")
if(Hash STREQUAL OldHash)
message("${OutputFile} is up to date at commit ${Tag}.")
return()
endif()
configure_file("${ScriptDir}/gitinfo.h.in" "${OutputFile}")
message("${OutputFile} updated to commit ${Tag}.")
endfunction()
main()

View file

@ -0,0 +1,8 @@
// @Hash@
//
// This file was automatically generated by the
// updaterevision tool. Do not edit by hand.
#define GIT_DESCRIPTION "@Tag@"
#define GIT_HASH "@Hash@"
#define GIT_TIME "@Timestamp@"

View file

@ -1,6 +0,0 @@
// This resource script is for compiling with MinGW only. Visual C++
// compilations use the manifest tool to insert the manifest instead.
#include <WinUser.h>
1 RT_MANIFEST "trustinfo.txt"

View file

@ -1,16 +0,0 @@
<!-- Ignore any warnings about Unrecognized Element "trustInfo" -->
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity version="1.0.0.0"
processorArchitecture="X86"
name="UpdateRevision"
type="win32" />
<description>Update svnrevision.h for the ZDoom source build process.</description>
<ms_asmv3:trustInfo xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
<ms_asmv3:security>
<ms_asmv3:requestedPrivileges>
<ms_asmv3:requestedExecutionLevel level="asInvoker" uiAccess="false" />
</ms_asmv3:requestedPrivileges>
</ms_asmv3:security>
</ms_asmv3:trustInfo>
</assembly>

View file

@ -0,0 +1,3 @@
/* Default keybindings for Hexen */
/ +jump

View file

@ -1,6 +1,5 @@
/* Default keybindings for Hexen */
/ +jump
backspace invuseall
\ "use ArtiHealth"
0 useflechette

View file

@ -1,5 +1,5 @@
/* Default keybindings for Strife */
w "showpop 1"
u "showpop 2"
s "showpop 2"

View file

@ -1919,6 +1919,7 @@ DSPLYMNU_GPUSWITCH = "Notebook Switchable GPU";
DSPLYMNU_CUSTOMINVERTMAP = "Custom Inverse Map";
DSPLYMNU_CUSTOMINVERTC1 = "Custom Inverse Color Dark";
DSPLYMNU_CUSTOMINVERTC2 = "Custom Inverse Color Light";
DSPLYMNU_RENDERSKYDOME = "Render the skydome";
// HUD Options
HUDMNU_TITLE = "HUD Options";
@ -2762,6 +2763,7 @@ GLLIGHTMNU_LIGHTPARTICLES = "Lights affect particles";
GLLIGHTMNU_LIGHTSHADOWMAP = "Light shadowmaps";
GLLIGHTMNU_LIGHTSHADOWMAPQUALITY = "Shadowmap quality";
GLLIGHTMNU_LIGHTSHADOWMAPFILTER = "Shadowmap filter";
GLLIGHTMNU_LIGHTBUFFERTYPE = "Light buffer type";
// OpenGL Preferences
GLPREFMNU_TITLE = "OPENGL PREFERENCES";
@ -2882,6 +2884,8 @@ OPTVAL_CLASSICZ = "Classic ZDoom";
OPTVAL_MODERN = "Modern (WASD)";
OPTVAL_MODERN2 = "Modern (ESDF)";
OPTVAL_MODERN3 = "Modern (OKL;)";
OPTVAL_UBO = "UBO (faster, unstable)";
OPTVAL_SSBO = "SSBO (slower)";
// QZDoom exclusive:

View file

@ -929,6 +929,7 @@ OptionMenu "VideoOptions" protected
Option "$DSPLYMNU_TELEZOOM", "telezoom", "OnOff"
Slider "$DSPLYMNU_QUAKEINTENSITY", "r_quakeintensity", 0.0, 1.0, 0.05, 2
Option "$DSPLYMNU_NOMONSTERINTERPOLATION", "nomonsterinterpolation", "NoYes"
Option "$DSPLYMNU_RENDERSKYDOME", "gl_skydome", "YesNo"
}
@ -2588,6 +2589,12 @@ OptionValue "FogMode"
2, "$OPTVAL_RADIAL"
}
OptionValue "LightBuffer"
{
0, "$OPTVAL_UBO"
1, "$OPTVAL_SSBO"
}
OptionValue "FuzzStyle"
{
0, "$OPTVAL_SHADOW"
@ -2669,6 +2676,7 @@ OptionMenu "GLLightOptions" protected
Option "$GLLIGHTMNU_LIGHTSPRITES", gl_light_sprites, "YesNo"
Option "$GLLIGHTMNU_LIGHTPARTICLES", gl_light_particles, "YesNo"
Option "$GLLIGHTMNU_LIGHTSHADOWMAP", gl_light_shadowmap, "YesNo"
Option "$GLLIGHTMNU_LIGHTBUFFERTYPE", gl_light_buffer_type, "LightBuffer"
Option "$GLLIGHTMNU_LIGHTSHADOWMAPQUALITY", gl_shadowmap_quality, "ShadowMapQuality"
Option "$GLLIGHTMNU_LIGHTSHADOWMAPFILTER", gl_shadowmap_filter, "ShadowMapFilter"
}

View file

@ -246,7 +246,7 @@ float sampleShadowmap(vec3 planePoint, float v)
vec3 ray = planePoint;
vec2 isize = textureSize(ShadowMap, 0);
ivec2 isize = textureSize(ShadowMap, 0);
float scale = float(isize.x) * 0.25;
// Snap to shadow map texel grid
@ -285,12 +285,12 @@ float sampleShadowmapPCF(vec3 planePoint, float v)
else
ray.y = ray.y / abs(ray.x);
vec2 isize = textureSize(ShadowMap, 0);
ivec2 isize = textureSize(ShadowMap, 0);
float scale = float(isize.x);
float texelPos = floor(shadowDirToU(ray.xz) * scale);
float sum = 0.0;
float step_count = uShadowmapFilter;
float step_count = float(uShadowmapFilter);
texelPos -= step_count + 0.5;
for (float x = -step_count; x <= step_count; x++)
@ -307,7 +307,7 @@ float sampleShadowmapPCF(vec3 planePoint, float v)
sum += step(dist2, texture(ShadowMap, vec2(u, v)).x);
texelPos++;
}
return sum / (uShadowmapFilter * 2.0 + 1.0);
return sum / (float(uShadowmapFilter) * 2.0 + 1.0);
}
float shadowmapAttenuation(vec4 lightpos, float shadowIndex)

View file

@ -74,8 +74,8 @@ extend class StateProvider
if (psp)
{
State cur = psp.CurState;
int theflash = atk == cur? 0:1;
player.SetSafeFlash(weap, flash, theflash);
int theflash = atk == cur? 0:1;
player.SetSafeFlash(weap, flash, theflash);
}
}
}

View file

@ -366,11 +366,11 @@ class Weapon : StateProvider
let pspr = player.GetPSprite(PSP_WEAPON);
if (pspr)
{
pspr.x = 0;
pspr.y = WEAPONTOP;
pspr.x = 0;
pspr.y = WEAPONTOP;
}
}
}
}
static int GetButtonStateFlags(int flags)
{
@ -877,9 +877,9 @@ class Weapon : StateProvider
let pspr = p.GetPSprite(PSP_WEAPON);
if (pspr)
{
pspr.y = WEAPONBOTTOM;
pspr.ResetInterpolation();
pspr.SetState(GetUpState());
pspr.y = WEAPONBOTTOM;
pspr.ResetInterpolation();
pspr.SetState(GetUpState());
}
}

View file

@ -123,22 +123,22 @@ class StrifePlayer : PlayerPawn
if (psp)
{
if (psp.CurState != null && firehandslower != null && firehands != null)
{
// Calculate state to go to.
int dist = firehands.DistanceTo(psp.curState);
if (dist > 0)
if (psp.CurState != null && firehandslower != null && firehands != null)
{
player.playerstate = PST_DEAD;
psp.SetState(firehandslower + dist);
return;
// Calculate state to go to.
int dist = firehands.DistanceTo(psp.curState);
if (dist > 0)
{
player.playerstate = PST_DEAD;
psp.SetState(firehandslower + dist);
return;
}
}
player.playerstate = PST_DEAD;
psp.SetState(null);
}
player.playerstate = PST_DEAD;
psp.SetState(null);
}
}
}
void A_HandLower()
{
@ -147,17 +147,17 @@ class StrifePlayer : PlayerPawn
PSprite psp = player.GetPSprite(PSP_STRIFEHANDS);
if (psp)
{
if (psp.CurState == null)
{
psp.SetState(null);
return;
}
psp.y += 9;
if (psp.y > WEAPONBOTTOM*2)
{
psp.SetState(null);
}
if (psp.CurState == null)
{
psp.SetState(null);
return;
}
psp.y += 9;
if (psp.y > WEAPONBOTTOM*2)
{
psp.SetState(null);
}
}
if (player.extralight > 0) player.extralight--;
}

View file

@ -139,8 +139,8 @@ class Mauler2 : Mauler
PSprite psp = player.GetPSprite(PSP_WEAPON);
if (psp)
{
psp.x += Random2[Mauler2]() / 64.;
psp.y += Random2[Mauler2]() / 64.;
psp.x += Random2[Mauler2]() / 64.;
psp.y += Random2[Mauler2]() / 64.;
}
}
}

View file

@ -202,9 +202,9 @@ struct InputEvent native play version("2.4")
Key_Mouse8 = 0x107,
Key_FirstJoyButton = 0x108,
Key_FirstJoy2Button = 0x128,
Key_FirstJoy3Button = 0x148,
Key_FirstJoy4Button = 0x168,
Key_FirstJoy2Button = 0x128,
Key_FirstJoy3Button = 0x148,
Key_FirstJoy4Button = 0x168,
Key_Joy1 = (Key_FirstJoyButton+0),
Key_Joy2 = (Key_FirstJoyButton+1),
Key_Joy3 = (Key_FirstJoyButton+2),

View file

@ -148,7 +148,7 @@ class AltHud ui
//
//---------------------------------------------------------------------------
void DrawHudNumber(Font fnt, int color, int num, int x, int y, double trans = 0.75)
void DrawHudNumber(Font fnt, int color, int num, int x, int y, double trans = 0.75)
{
DrawHudText(fnt, color, String.Format("%d", num), x, y, trans);
}
@ -748,14 +748,14 @@ class AltHud ui
if (withmapname)
{
screen.DrawText(SmallFont, hudcolor_titl, hudwidth - 6 - SmallFont.StringWidth(level.MapName), ypos, level.MapName,
DTA_KeepRatio, true,
DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight);
screen.DrawText(SmallFont, hudcolor_titl, hudwidth - 6 - SmallFont.StringWidth(level.LevelName), ypos + h, level.LevelName,
DTA_KeepRatio, true,
DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight);
screen.DrawText(SmallFont, hudcolor_titl, hudwidth - 6 - SmallFont.StringWidth(level.MapName), ypos, level.MapName,
DTA_KeepRatio, true,
DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight);
screen.DrawText(SmallFont, hudcolor_titl, hudwidth - 6 - SmallFont.StringWidth(level.LevelName), ypos + h, level.LevelName,
DTA_KeepRatio, true,
DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight);
ypos += 3 * h;
}

View file

@ -1130,7 +1130,7 @@ class BaseStatusBar native ui
if (offtex.IsValid() && TexMan.GetScaledSize(offtex) == texsize) DrawTexture(offtex, position, flags | DI_ITEM_LEFT_TOP, alpha);
else Fill(color(int(255*alpha),0,0,0), position.X + Clip[0], position.Y + Clip[1], texsize.X - Clip[0] - Clip[2], texsize.Y - Clip[1] - Clip[3]);
if (border == 0)
if (border == 0)
{
SetClipRect(position.X + Clip[0], position.Y + Clip[1], texsize.X - Clip[0] - Clip[2], texsize.Y - Clip[1] - Clip[3], flags);
DrawTexture(ontex, position, flags | DI_ITEM_LEFT_TOP, alpha);

View file

@ -1733,7 +1733,7 @@ flickerlight TIMEBOMB_X1
attenuate 1
}
flickerlight TIMEBOMB_X1
flickerlight TIMEBOMB_X2
{
color 0.8 0.4 0.3
size 88
@ -1742,7 +1742,7 @@ flickerlight TIMEBOMB_X1
attenuate 1
}
flickerlight TIMEBOMB_X1
flickerlight TIMEBOMB_X3
{
color 0.6 0.3 0.2
size 96
@ -1751,7 +1751,7 @@ flickerlight TIMEBOMB_X1
attenuate 1
}
flickerlight TIMEBOMB_X1
flickerlight TIMEBOMB_X4
{
color 0.4 0.2 0.1
size 108
@ -1760,7 +1760,7 @@ flickerlight TIMEBOMB_X1
attenuate 1
}
flickerlight TIMEBOMB_X1
flickerlight TIMEBOMB_X5
{
color 0.2 0.1 0.0
size 120
@ -1774,7 +1774,7 @@ object ActivatedTimeBomb
frame XPL1A { light TIMEBOMB_X1 }
frame XPL1B { light TIMEBOMB_X2 }
frame XPL1C { light TIMEBOMB_X3 }
frame XPL1D { light TIMEBOMB_X4 }
frame XPL1D { light TIMEBOMB_X12 }
frame XPL1E { light TIMEBOMB_X5 }
frame XPL1F { light TIMEBOMB_X5 }
}

Binary file not shown.