diff --git a/.gitignore b/.gitignore index 342b2ec..fff9196 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,7 @@ $RECYCLE.BIN/ *.lnk # build files -*.o +# *.o *.d #binaries diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/CMakeLists.txt b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/CMakeLists.txt index 3d3319a..6c87b58 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/CMakeLists.txt +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/CMakeLists.txt @@ -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 ) diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/mobile/Android_lzma.mk b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/mobile/Android_lzma.mk index 473318f..94fe02e 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/mobile/Android_lzma.mk +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/mobile/Android_lzma.mk @@ -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) diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/mobile/Android_zlib.mk b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/mobile/Android_zlib.mk index 88679a4..f1894cc 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/mobile/Android_zlib.mk +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/mobile/Android_zlib.mk @@ -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 diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/CMakeLists.txt b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/CMakeLists.txt index 1ed6455..3462229 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/CMakeLists.txt +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/CMakeLists.txt @@ -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/.+") diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/dynlights/gl_lightbuffer.cpp b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/dynlights/gl_lightbuffer.cpp index 260b5ae..72ff0cb 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/dynlights/gl_lightbuffer.cpp +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/dynlights/gl_lightbuffer.cpp @@ -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(©ptr[0], parmcnt, 4 * sizeof(float)); - memcpy(©ptr[4], &data.arrays[0][0], 4 * size0*sizeof(float)); - memcpy(©ptr[4 + 4*size0], &data.arrays[1][0], 4 * size1*sizeof(float)); - memcpy(©ptr[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(©ptr[0], parmcnt, ELEMENT_SIZE); + memcpy(©ptr[4], &data.arrays[0][0], size0 * ELEMENT_SIZE); + memcpy(©ptr[4 + 4*size0], &data.arrays[1][0], size1 * ELEMENT_SIZE); + memcpy(©ptr[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; +} + diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/dynlights/gl_lightbuffer.h b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/dynlights/gl_lightbuffer.h index 8781ccb..103dce1 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/dynlights/gl_lightbuffer.h +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/dynlights/gl_lightbuffer.h @@ -2,6 +2,7 @@ #define __GL_LIGHTBUFFER_H #include "tarray.h" +#include struct FDynLightData; class FLightBuffer @@ -11,13 +12,13 @@ class FLightBuffer float * mBufferPointer; unsigned int mBufferType; - unsigned int mIndex; - unsigned int mUploadIndex; + std::atomic 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(); } diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/scene/gl_scene.cpp b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/scene/gl_scene.cpp index 83d14c3..e3b0ad0 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/scene/gl_scene.cpp +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/scene/gl_scene.cpp @@ -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; iCopyToBackbuffer(&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); } diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/scene/gl_skydome.cpp b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/scene/gl_skydome.cpp index 39d2a10..dfb56b6 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/scene/gl_skydome.cpp +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/scene/gl_skydome.cpp @@ -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. diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/shaders/gl_shader.cpp b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/shaders/gl_shader.cpp index dd8c5e1..ede376f 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/shaders/gl_shader.cpp +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/shaders/gl_shader.cpp @@ -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) { diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/system/gl_cvars.h b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/system/gl_cvars.h index 09a27e5..65e6024 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/system/gl_cvars.h +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/system/gl_cvars.h @@ -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) diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/system/gl_interface.cpp b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/system/gl_interface.cpp index 2ba8bac..29b6019 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/system/gl_interface.cpp +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/system/gl_interface.cpp @@ -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 diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/utility/gl_templates.h b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/utility/gl_templates.h index 1ebef1f..282c777 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/utility/gl_templates.h +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/utility/gl_templates.h @@ -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; diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/cocoa/i_common.h b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/cocoa/i_common.h new file mode 100644 index 0000000..be395cf --- /dev/null +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/cocoa/i_common.h @@ -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 + + +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 diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/cocoa/i_input.mm b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/cocoa/i_input.mm new file mode 100644 index 0000000..54b301a --- /dev/null +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/cocoa/i_input.mm @@ -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 + +#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([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( viewPos.x); + outEvent->data2 = static_cast(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; + } +} diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/cocoa/i_joystick.cpp b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/cocoa/i_joystick.cpp new file mode 100644 index 0000000..d481531 --- /dev/null +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/cocoa/i_joystick.cpp @@ -0,0 +1,1246 @@ +/* + ** i_joystick.cpp + ** + **--------------------------------------------------------------------------- + ** 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 +#include +#include +#include +#include + +#include "d_event.h" +#include "doomdef.h" +#include "i_system.h" +#include "m_argv.h" +#include "m_joy.h" +#include "templates.h" +#include "v_text.h" + + +EXTERN_CVAR(Bool, joy_axespolling) + + +namespace +{ + +FString ToFString(const CFStringRef string) +{ + if (NULL == string) + { + return FString(); + } + + const CFIndex stringLength = CFStringGetLength(string); + + if (0 == stringLength) + { + return FString(); + } + + const size_t bufferSize = CFStringGetMaximumSizeForEncoding(stringLength, kCFStringEncodingUTF8) + 1; + + char buffer[bufferSize]; + memset(buffer, 0, bufferSize); + + CFStringGetCString(string, buffer, bufferSize, kCFStringEncodingUTF8); + + return FString(buffer); +} + + +// --------------------------------------------------------------------------- + + +class IOKitJoystick : public IJoystickConfig +{ +public: + explicit IOKitJoystick(io_object_t device); + virtual ~IOKitJoystick(); + + virtual FString GetName(); + virtual float GetSensitivity(); + virtual void SetSensitivity(float scale); + + virtual int GetNumAxes(); + virtual float GetAxisDeadZone(int axis); + virtual EJoyAxis GetAxisMap(int axis); + virtual const char* GetAxisName(int axis); + virtual float GetAxisScale(int axis); + + virtual void SetAxisDeadZone(int axis, float deadZone); + virtual void SetAxisMap(int axis, EJoyAxis gameAxis); + virtual void SetAxisScale(int axis, float scale); + + virtual bool IsSensitivityDefault(); + virtual bool IsAxisDeadZoneDefault(int axis); + virtual bool IsAxisMapDefault(int axis); + virtual bool IsAxisScaleDefault(int axis); + + virtual void SetDefaultConfig(); + virtual FString GetIdentifier(); + + void AddAxes(float axes[NUM_JOYAXIS]) const; + + void Update(); + + void UseAxesPolling(bool axesPolling); + + io_object_t* GetNotificationPtr(); + +private: + IOHIDDeviceInterface** m_interface; + IOHIDQueueInterface** m_queue; + + FString m_name; + FString m_identifier; + + float m_sensitivity; + + struct AnalogAxis + { + IOHIDElementCookie cookie; + + char name[64]; + + float value; + + int32_t minValue; + int32_t maxValue; + + float deadZone; + float defaultDeadZone; + float sensitivity; + float defaultSensitivity; + + EJoyAxis gameAxis; + EJoyAxis defaultGameAxis; + + AnalogAxis() + { + memset(this, 0, sizeof *this); + } + }; + + TArray m_axes; + + struct DigitalButton + { + IOHIDElementCookie cookie; + int32_t value; + + explicit DigitalButton(const IOHIDElementCookie cookie) + : cookie(cookie) + , value(0) + { } + }; + + TArray m_buttons; + TArray m_POVs; + + bool m_useAxesPolling; + + io_object_t m_notification; + + + static const float DEFAULT_DEADZONE; + static const float DEFAULT_SENSITIVITY; + + void ProcessAxes(); + bool ProcessAxis (const IOHIDEventStruct& event); + bool ProcessButton(const IOHIDEventStruct& event); + bool ProcessPOV (const IOHIDEventStruct& event); + + void GatherDeviceInfo(io_object_t device, CFDictionaryRef properties); + + static void GatherElementsHandler(const void* value, void* parameter); + void GatherCollectionElements(CFDictionaryRef properties); + + void AddAxis(CFDictionaryRef element); + void AddButton(CFDictionaryRef element); + void AddPOV(CFDictionaryRef element); + + void AddToQueue(IOHIDElementCookie cookie); + void RemoveFromQueue(IOHIDElementCookie cookie); +}; + + +const float IOKitJoystick::DEFAULT_DEADZONE = 0.25f; +const float IOKitJoystick::DEFAULT_SENSITIVITY = 1.0f; + + +IOHIDDeviceInterface** CreateDeviceInterface(const io_object_t device) +{ + IOCFPlugInInterface** plugInInterface = NULL; + SInt32 score = 0; + + const kern_return_t pluginResult = IOCreatePlugInInterfaceForService(device, + kIOHIDDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score); + + IOHIDDeviceInterface** interface = NULL; + + if (KERN_SUCCESS == pluginResult) + { + // Call a method of the intermediate plug-in to create the device interface + + const HRESULT queryResult = (*plugInInterface)->QueryInterface(plugInInterface, + CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID), reinterpret_cast(&interface)); + + IODestroyPlugInInterface(plugInInterface); // [?] or maybe (*plugInInterface)->Release(plugInInterface); + + if (S_OK == queryResult) + { + const IOReturn openResult = (*interface)->open(interface, 0); + + if (kIOReturnSuccess != openResult) + { + (*interface)->Release(interface); + + Printf(TEXTCOLOR_RED "IOHIDDeviceInterface::open() failed with code 0x%08X\n", openResult); + return NULL; + } + } + else + { + Printf(TEXTCOLOR_RED "IOCFPlugInInterface::QueryInterface() failed with code 0x%08X\n", + static_cast(queryResult)); + return NULL; + } + } + else + { + Printf(TEXTCOLOR_RED "IOCreatePlugInInterfaceForService() failed with code %i\n", pluginResult); + return NULL; + } + + return interface; +} + +IOHIDQueueInterface** CreateDeviceQueue(IOHIDDeviceInterface** const interface) +{ + if (NULL == interface) + { + return NULL; + } + + IOHIDQueueInterface** queue = (*interface)->allocQueue(interface); + + if (NULL == queue) + { + Printf(TEXTCOLOR_RED "IOHIDDeviceInterface::allocQueue() failed\n"); + return NULL; + } + + static const uint32_t QUEUE_FLAGS = 0; + static const uint32_t QUEUE_DEPTH = 0; + + const IOReturn queueResult = (*queue)->create(queue, QUEUE_FLAGS, QUEUE_DEPTH); + + if (kIOReturnSuccess != queueResult) + { + (*queue)->Release(queue); + + Printf(TEXTCOLOR_RED "IOHIDQueueInterface::create() failed with code 0x%08X\n", queueResult); + return NULL; + } + + return queue; +} + + +IOKitJoystick::IOKitJoystick(const io_object_t device) +: m_interface(CreateDeviceInterface(device)) +, m_queue(CreateDeviceQueue(m_interface)) +, m_sensitivity(DEFAULT_SENSITIVITY) +, m_useAxesPolling(true) +, m_notification(0) +{ + if (NULL == m_interface || NULL == m_queue) + { + return; + } + + CFMutableDictionaryRef properties = NULL; + const kern_return_t propertiesResult = + IORegistryEntryCreateCFProperties(device, &properties, kCFAllocatorDefault, kNilOptions); + + if (KERN_SUCCESS != propertiesResult || NULL == properties) + { + Printf(TEXTCOLOR_RED "IORegistryEntryCreateCFProperties() failed with code %i\n", propertiesResult); + return; + } + + GatherDeviceInfo(device, properties); + GatherCollectionElements(properties); + + CFRelease(properties); + + UseAxesPolling(joy_axespolling); + + (*m_queue)->start(m_queue); + + SetDefaultConfig(); +} + +IOKitJoystick::~IOKitJoystick() +{ + M_SaveJoystickConfig(this); + + if (0 != m_notification) + { + IOObjectRelease(m_notification); + } + + if (NULL != m_queue) + { + (*m_queue)->stop(m_queue); + (*m_queue)->dispose(m_queue); + (*m_queue)->Release(m_queue); + } + + if (NULL != m_interface) + { + (*m_interface)->close(m_interface); + (*m_interface)->Release(m_interface); + } +} + + +FString IOKitJoystick::GetName() +{ + return m_name; +} + + +float IOKitJoystick::GetSensitivity() +{ + return m_sensitivity; +} + +void IOKitJoystick::SetSensitivity(float scale) +{ + m_sensitivity = scale; +} + + +int IOKitJoystick::GetNumAxes() +{ + return static_cast(m_axes.Size()); +} + +#define IS_AXIS_VALID (static_cast(axis) < m_axes.Size()) + +float IOKitJoystick::GetAxisDeadZone(int axis) +{ + return IS_AXIS_VALID ? m_axes[axis].deadZone : 0.0f; +} + +EJoyAxis IOKitJoystick::GetAxisMap(int axis) +{ + return IS_AXIS_VALID ? m_axes[axis].gameAxis : JOYAXIS_None; +} + +const char* IOKitJoystick::GetAxisName(int axis) +{ + return IS_AXIS_VALID ? m_axes[axis].name : "Invalid"; +} + +float IOKitJoystick::GetAxisScale(int axis) +{ + return IS_AXIS_VALID ? m_axes[axis].sensitivity : 0.0f; +} + +void IOKitJoystick::SetAxisDeadZone(int axis, float deadZone) +{ + if (IS_AXIS_VALID) + { + m_axes[axis].deadZone = clamp(deadZone, 0.0f, 1.0f); + } +} + +void IOKitJoystick::SetAxisMap(int axis, EJoyAxis gameAxis) +{ + if (IS_AXIS_VALID) + { + m_axes[axis].gameAxis = (gameAxis> JOYAXIS_None && gameAxis = 3) + { + m_axes[0].gameAxis = JOYAXIS_Side; + m_axes[1].gameAxis = JOYAXIS_Forward; + m_axes[2].gameAxis = JOYAXIS_Yaw; + + // Four axes? First two are movement, last two are looking around. + + if (axisCount >= 4) + { + m_axes[3].gameAxis = JOYAXIS_Pitch; +// ??? m_axes[3].sensitivity = 0.75f; + + // Five axes? Use the fifth one for moving up and down. + + if (axisCount >= 5) + { + m_axes[4].gameAxis = JOYAXIS_Up; + } + } + } + + // If there is only one axis, then we make no assumptions about how + // the user might want to use it. + + // Preserve defaults for config saving. + + for (size_t i = 0; i < axisCount; ++i) + { + m_axes[i].defaultDeadZone = m_axes[i].deadZone; + m_axes[i].defaultSensitivity = m_axes[i].sensitivity; + m_axes[i].defaultGameAxis = m_axes[i].gameAxis; + } +} + + +FString IOKitJoystick::GetIdentifier() +{ + return m_identifier; +} + + +void IOKitJoystick::AddAxes(float axes[NUM_JOYAXIS]) const +{ + for (size_t i = 0, count = m_axes.Size(); i < count; ++i) + { + const EJoyAxis axis = m_axes[i].gameAxis; + + if (JOYAXIS_None == axis) + { + continue; + } + + axes[axis] -= m_axes[i].value; + } +} + + +void IOKitJoystick::UseAxesPolling(const bool axesPolling) +{ + m_useAxesPolling = axesPolling; + + for (size_t i = 0, count = m_axes.Size(); i < count; ++i) + { + AnalogAxis& axis = m_axes[i]; + + if (m_useAxesPolling) + { + RemoveFromQueue(axis.cookie); + } + else + { + AddToQueue(axis.cookie); + } + } +} + + +void IOKitJoystick::Update() +{ + if (NULL == m_queue) + { + return; + } + + IOHIDEventStruct event = { }; + AbsoluteTime zeroTime = { }; + + const IOReturn eventResult = (*m_queue)->getNextEvent(m_queue, &event, zeroTime, 0); + + if (kIOReturnSuccess == eventResult) + { + if (use_joystick) + { + ProcessAxis(event) || ProcessButton(event) || ProcessPOV(event); + } + } + else if (kIOReturnUnderrun != eventResult) + { + Printf(TEXTCOLOR_RED "IOHIDQueueInterface::getNextEvent() failed with code 0x%08X\n", eventResult); + } + + ProcessAxes(); +} + + +void IOKitJoystick::ProcessAxes() +{ + if (NULL == m_interface || !m_useAxesPolling) + { + return; + } + + for (size_t i = 0, count = m_axes.Size(); i < count; ++i) + { + AnalogAxis& axis = m_axes[i]; + + static const double scaledMin = -1; + static const double scaledMax = 1; + + IOHIDEventStruct event; + + if (kIOReturnSuccess == (*m_interface)->getElementValue(m_interface, axis.cookie, &event)) + { + const double scaledValue = scaledMin + + (event.value - axis.minValue) * (scaledMax - scaledMin) / (axis.maxValue - axis.minValue); + const double filteredValue = Joy_RemoveDeadZone(scaledValue, axis.deadZone, NULL); + + axis.value = static_cast(filteredValue * m_sensitivity * axis.sensitivity); + } + else + { + axis.value = 0.0f; + } + } +} + + +bool IOKitJoystick::ProcessAxis(const IOHIDEventStruct& event) +{ + if (m_useAxesPolling) + { + return false; + } + + for (size_t i = 0, count = m_axes.Size(); i < count; ++i) + { + if (event.elementCookie != m_axes[i].cookie) + { + continue; + } + + AnalogAxis& axis = m_axes[i]; + + static const double scaledMin = -1; + static const double scaledMax = 1; + + const double scaledValue = scaledMin + + (event.value - axis.minValue) * (scaledMax - scaledMin) / (axis.maxValue - axis.minValue); + const double filteredValue = Joy_RemoveDeadZone(scaledValue, axis.deadZone, NULL); + + axis.value = static_cast(filteredValue * m_sensitivity * axis.sensitivity); + + return true; + } + + return false; +} + +bool IOKitJoystick::ProcessButton(const IOHIDEventStruct& event) +{ + for (size_t i = 0, count = m_buttons.Size(); i < count; ++i) + { + if (event.elementCookie != m_buttons[i].cookie) + { + continue; + } + + int32_t& current = m_buttons[i].value; + const int32_t previous = current; + current = event.value; + + Joy_GenerateButtonEvents(previous, current, 1, static_cast(KEY_FIRSTJOYBUTTON + i)); + + return true; + } + + return false; +} + +bool IOKitJoystick::ProcessPOV(const IOHIDEventStruct& event) +{ + for (size_t i = 0, count = m_POVs.Size(); i ( + CFDictionaryGetValue(properties, CFSTR(kIOHIDManufacturerKey))); + CFStringRef productRef = static_cast( + CFDictionaryGetValue(properties, CFSTR(kIOHIDProductKey))); + CFNumberRef vendorIDRef = static_cast( + CFDictionaryGetValue(properties, CFSTR(kIOHIDVendorIDKey))); + CFNumberRef productIDRef = static_cast( + CFDictionaryGetValue(properties, CFSTR(kIOHIDProductIDKey))); + + CFMutableDictionaryRef usbProperties = NULL; + + if ( NULL == vendorRef || NULL == productRef + || NULL == vendorIDRef || NULL == productIDRef) + { + // OS X is not mirroring all USB properties to HID page, so need to look at USB device page also + // Step up two levels and get dictionary of USB properties + + io_registry_entry_t parent1; + kern_return_t ioResult = IORegistryEntryGetParentEntry(device, kIOServicePlane, &parent1); + + if (KERN_SUCCESS == ioResult) + { + io_registry_entry_t parent2; + ioResult = IORegistryEntryGetParentEntry(device, kIOServicePlane, &parent2); + + if (KERN_SUCCESS == ioResult) + { + ioResult = IORegistryEntryCreateCFProperties(parent2, &usbProperties, kCFAllocatorDefault, kNilOptions); + + if (KERN_SUCCESS != ioResult) + { + Printf(TEXTCOLOR_RED "IORegistryEntryCreateCFProperties() failed with code %i\n", ioResult); + } + + IOObjectRelease(parent2); + } + else + { + Printf(TEXTCOLOR_RED "IORegistryEntryGetParentEntry(2) failed with code %i\n", ioResult); + } + + IOObjectRelease(parent1); + } + else + { + Printf(TEXTCOLOR_RED "IORegistryEntryGetParentEntry(1) failed with code %i\n", ioResult); + } + } + + if (NULL != usbProperties) + { + if (NULL == vendorRef) + { + vendorRef = static_cast( + CFDictionaryGetValue(usbProperties, CFSTR("USB Vendor Name"))); + } + + if (NULL == productRef) + { + productRef = static_cast( + CFDictionaryGetValue(usbProperties, CFSTR("USB Product Name"))); + } + + if (NULL == vendorIDRef) + { + vendorIDRef = static_cast( + CFDictionaryGetValue(usbProperties, CFSTR("idVendor"))); + } + + if (NULL == productIDRef) + { + productIDRef = static_cast( + CFDictionaryGetValue(usbProperties, CFSTR("idProduct"))); + } + } + + m_name += ToFString(vendorRef); + m_name += " "; + m_name += ToFString(productRef); + + int vendorID = 0, productID = 0; + + if (NULL != vendorIDRef) + { + CFNumberGetValue(vendorIDRef, kCFNumberIntType, &vendorID); + } + + if (NULL != productIDRef) + { + CFNumberGetValue(productIDRef, kCFNumberIntType, &productID); + } + + m_identifier.AppendFormat("VID_%04x_PID_%04x", vendorID, productID); + + if (NULL != usbProperties) + { + CFRelease(usbProperties); + } +} + + +long GetElementValue(const CFDictionaryRef element, const CFStringRef key) +{ + const CFNumberRef number = + static_cast(CFDictionaryGetValue(element, key)); + long result = 0; + + if (NULL != number && CFGetTypeID(number) == CFNumberGetTypeID()) + { + CFNumberGetValue(number, kCFNumberLongType, &result); + } + + return result; +} + +void IOKitJoystick::GatherElementsHandler(const void* value, void* parameter) +{ + assert(NULL != value); + assert(NULL != parameter); + + const CFDictionaryRef element = static_cast(value); + IOKitJoystick* thisPtr = static_cast(parameter); + + if (CFGetTypeID(element) != CFDictionaryGetTypeID()) + { + Printf(TEXTCOLOR_RED "IOKitJoystick: Encountered wrong element type\n"); + return; + } + + const long type = GetElementValue(element, CFSTR(kIOHIDElementTypeKey)); + + if (kIOHIDElementTypeCollection == type) + { + thisPtr->GatherCollectionElements(element); + } + else if (0 != type) + { + const long usagePage = GetElementValue(element, CFSTR(kIOHIDElementUsagePageKey)); + + if (kHIDPage_GenericDesktop == usagePage) + { + const long usage = GetElementValue(element, CFSTR(kIOHIDElementUsageKey)); + + if ( kHIDUsage_GD_Slider == usage + || kHIDUsage_GD_X == usage || kHIDUsage_GD_Y == usage || kHIDUsage_GD_Z == usage + || kHIDUsage_GD_Rx == usage || kHIDUsage_GD_Ry == usage || kHIDUsage_GD_Rz == usage) + { + thisPtr->AddAxis(element); + } + else if (kHIDUsage_GD_Hatswitch == usage && thisPtr->m_POVs.Size() < 4) + { + thisPtr->AddPOV(element); + } + } + else if (kHIDPage_Button == usagePage) + { + thisPtr->AddButton(element); + } + } +} + +void IOKitJoystick::GatherCollectionElements(const CFDictionaryRef properties) +{ + const CFArrayRef topElement = static_cast( + CFDictionaryGetValue(properties, CFSTR(kIOHIDElementKey))); + + if (NULL == topElement || CFGetTypeID(topElement) != CFArrayGetTypeID()) + { + Printf(TEXTCOLOR_RED "GatherCollectionElements: invalid properties dictionary\n"); + return; + } + + const CFRange range = { 0, CFArrayGetCount(topElement) }; + + CFArrayApplyFunction(topElement, range, GatherElementsHandler, this); +} + + +IOHIDElementCookie GetElementCookie(const CFDictionaryRef element) +{ + // Use C-style cast to avoid 32/64-bit IOHIDElementCookie type issue + return (IOHIDElementCookie)GetElementValue(element, CFSTR(kIOHIDElementCookieKey)); +} + +void IOKitJoystick::AddAxis(const CFDictionaryRef element) +{ + AnalogAxis axis; + + axis.cookie = GetElementCookie(element); + axis.minValue = GetElementValue(element, CFSTR(kIOHIDElementMinKey)); + axis.maxValue = GetElementValue(element, CFSTR(kIOHIDElementMaxKey)); + + const CFStringRef nameRef = static_cast( + CFDictionaryGetValue(element, CFSTR(kIOHIDElementNameKey))); + + if (NULL != nameRef && CFStringGetTypeID() == CFGetTypeID(nameRef)) + { + CFStringGetCString(nameRef, axis.name, sizeof(axis.name) - 1, kCFStringEncodingUTF8); + } + else + { + snprintf(axis.name, sizeof(axis.name), "Axis %i", m_axes.Size() + 1); + } + + m_axes.Push(axis); +} + +void IOKitJoystick::AddButton(CFDictionaryRef element) +{ + const DigitalButton button(GetElementCookie(element)); + + m_buttons.Push(button); + + AddToQueue(button.cookie); +} + +void IOKitJoystick::AddPOV(CFDictionaryRef element) +{ + const DigitalButton pov(GetElementCookie(element)); + + m_POVs.Push(pov); + + AddToQueue(pov.cookie); +} + + +void IOKitJoystick::AddToQueue(const IOHIDElementCookie cookie) +{ + if (NULL == m_queue) + { + return; + } + + if (!(*m_queue)->hasElement(m_queue, cookie)) + { + (*m_queue)->addElement(m_queue, cookie, 0); + } +} + +void IOKitJoystick::RemoveFromQueue(const IOHIDElementCookie cookie) +{ + if (NULL == m_queue) + { + return; + } + + if ((*m_queue)->hasElement(m_queue, cookie)) + { + (*m_queue)->removeElement(m_queue, cookie); + } +} + + +io_object_t* IOKitJoystick::GetNotificationPtr() +{ + return &m_notification; +} + + +// --------------------------------------------------------------------------- + + +class IOKitJoystickManager +{ +public: + IOKitJoystickManager(); + ~IOKitJoystickManager(); + + void GetJoysticks(TArray& joysticks) const; + + void AddAxes(float axes[NUM_JOYAXIS]) const; + + // Updates axes/buttons states + void Update(); + + void UseAxesPolling(bool axesPolling); + +private: + typedef TDeletingArray JoystickList; + JoystickList m_joysticks; + + static const size_t NOTIFICATION_PORT_COUNT = 2; + + IONotificationPortRef m_notificationPorts[NOTIFICATION_PORT_COUNT]; + io_iterator_t m_notifications [NOTIFICATION_PORT_COUNT]; + + // Rebuilds device list + void Rescan(int usagePage, int usage, size_t notificationPortIndex); + void AddDevices(IONotificationPortRef notificationPort, const io_iterator_t iterator); + + static void OnDeviceAttached(void* refcon, io_iterator_t iterator); + static void OnDeviceRemoved(void* refcon, io_service_t service, + natural_t messageType, void* messageArgument); +}; + + +IOKitJoystickManager* s_joystickManager; + + +IOKitJoystickManager::IOKitJoystickManager() +{ + memset(m_notifications, 0, sizeof m_notifications); + + for (size_t i = 0; i < NOTIFICATION_PORT_COUNT; ++i) + { + m_notificationPorts[i] = IONotificationPortCreate(kIOMasterPortDefault); + + if (NULL == m_notificationPorts[i]) + { + Printf(TEXTCOLOR_RED "IONotificationPortCreate(%zu) failed\n", i); + return; + } + + CFRunLoopAddSource(CFRunLoopGetCurrent(), + IONotificationPortGetRunLoopSource(m_notificationPorts[i]), kCFRunLoopDefaultMode); + } + + Rescan(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick, 0); + Rescan(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad, 1); +} + +IOKitJoystickManager::~IOKitJoystickManager() +{ + for (size_t i = 0; i < NOTIFICATION_PORT_COUNT; ++i) + { + IONotificationPortRef& port = m_notificationPorts[i]; + + if (NULL != port) + { + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), + IONotificationPortGetRunLoopSource(port), kCFRunLoopDefaultMode); + + IONotificationPortDestroy(port); + port = NULL; + } + + io_iterator_t& notification = m_notifications[i]; + + if (0 != notification) + { + IOObjectRelease(notification); + notification = 0; + } + } +} + + +void IOKitJoystickManager::GetJoysticks(TArray& joysticks) const +{ + const size_t joystickCount = m_joysticks.Size(); + + joysticks.Resize(joystickCount); + + for (size_t i = 0; i < joystickCount; ++i) + { + M_LoadJoystickConfig(m_joysticks[i]); + + joysticks[i] = m_joysticks[i]; + } +} + +void IOKitJoystickManager::AddAxes(float axes[NUM_JOYAXIS]) const +{ + for (size_t i = 0, count = m_joysticks.Size(); i < count; ++i) + { + m_joysticks[i]->AddAxes(axes); + } +} + + +void IOKitJoystickManager::Update() +{ + for (size_t i = 0, count = m_joysticks.Size(); i < count; ++i) + { + m_joysticks[i]->Update(); + } +} + + +void IOKitJoystickManager::UseAxesPolling(const bool axesPolling) +{ + for (size_t i = 0, count = m_joysticks.Size(); i < count; ++i) + { + m_joysticks[i]->UseAxesPolling(axesPolling); + } +} + + +void PostDeviceChangeEvent() +{ + const event_t event = { EV_DeviceChange }; + D_PostEvent(&event); +} + + +void IOKitJoystickManager::Rescan(const int usagePage, const int usage, const size_t notificationPortIndex) +{ + CFMutableDictionaryRef deviceMatching = IOServiceMatching(kIOHIDDeviceKey); + + if (NULL == deviceMatching) + { + Printf(TEXTCOLOR_RED "IOServiceMatching() returned NULL\n"); + return; + } + + const CFNumberRef usagePageRef = + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usagePage); + CFDictionarySetValue(deviceMatching, CFSTR(kIOHIDPrimaryUsagePageKey), usagePageRef); + + const CFNumberRef usageRef = + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage); + CFDictionarySetValue(deviceMatching, CFSTR(kIOHIDPrimaryUsageKey), usageRef); + + assert(notificationPortIndex < NOTIFICATION_PORT_COUNT); + io_iterator_t* iteratorPtr = &m_notifications[notificationPortIndex]; + + const IONotificationPortRef notificationPort = m_notificationPorts[notificationPortIndex]; + assert(NULL != notificationPort); + + const kern_return_t notificationResult = IOServiceAddMatchingNotification(notificationPort, + kIOFirstMatchNotification, deviceMatching, OnDeviceAttached, notificationPort, iteratorPtr); + + // IOServiceAddMatchingNotification() consumes one reference of matching dictionary + // Thus CFRelease(deviceMatching) is not needed + + CFRelease(usageRef); + CFRelease(usagePageRef); + + if (KERN_SUCCESS != notificationResult) + { + Printf(TEXTCOLOR_RED "IOServiceAddMatchingNotification() failed with code %i\n", notificationResult); + } + + AddDevices(notificationPort, *iteratorPtr); +} + +void IOKitJoystickManager::AddDevices(const IONotificationPortRef notificationPort, const io_iterator_t iterator) +{ + while (io_object_t device = IOIteratorNext(iterator)) + { + IOKitJoystick* joystick = new IOKitJoystick(device); + m_joysticks.Push(joystick); + + const kern_return_t notificationResult = IOServiceAddInterestNotification(notificationPort, + device, kIOGeneralInterest, OnDeviceRemoved, joystick, joystick->GetNotificationPtr()); + if (KERN_SUCCESS != notificationResult) + { + Printf(TEXTCOLOR_RED "IOServiceAddInterestNotification() failed with code %i\n", notificationResult); + } + + IOObjectRelease(device); + + PostDeviceChangeEvent(); + } +} + + +void IOKitJoystickManager::OnDeviceAttached(void* const refcon, const io_iterator_t iterator) +{ + assert(NULL != refcon); + const IONotificationPortRef notificationPort = static_cast(refcon); + + assert(NULL != s_joystickManager); + s_joystickManager->AddDevices(notificationPort, iterator); +} + +void IOKitJoystickManager::OnDeviceRemoved(void* const refcon, io_service_t, const natural_t messageType, void*) +{ + if (messageType != kIOMessageServiceIsTerminated) + { + return; + } + + assert(NULL != refcon); + IOKitJoystick* const joystick = static_cast(refcon); + + assert(NULL != s_joystickManager); + JoystickList& joysticks = s_joystickManager->m_joysticks; + + for (unsigned int i = 0, count = joysticks.Size(); i < count; ++i) + { + if (joystick == joysticks[i]) + { + joysticks.Delete(i); + break; + } + } + + delete joystick; + + PostDeviceChangeEvent(); +} + +} // unnamed namespace + + +// --------------------------------------------------------------------------- + + +void I_ShutdownInput() +{ + delete s_joystickManager; + s_joystickManager = NULL; +} + +void I_GetJoysticks(TArray& sticks) +{ + // Instances of IOKitJoystick depend on GameConfig object. + // M_SaveDefaultsFinal() must be called after destruction of IOKitJoystickManager. + // To ensure this, its initialization is moved here. + // As M_LoadDefaults() was already called at this moment, + // the order of atterm's functions will be correct + + if (NULL == s_joystickManager && !Args->CheckParm("-nojoy")) + { + s_joystickManager = new IOKitJoystickManager; + } + + if (NULL != s_joystickManager) + { + s_joystickManager->GetJoysticks(sticks); + } +} + +void I_GetAxes(float axes[NUM_JOYAXIS]) +{ + for (size_t i = 0; i < NUM_JOYAXIS; ++i) + { + axes[i] = 0.0f; + } + + if (use_joystick && NULL != s_joystickManager) + { + s_joystickManager->AddAxes(axes); + } +} + +IJoystickConfig* I_UpdateDeviceList() +{ + // Does nothing, device list is always kept up-to-date + + return NULL; +} + + +// --------------------------------------------------------------------------- + + +void I_ProcessJoysticks() +{ + if (NULL != s_joystickManager) + { + s_joystickManager->Update(); + } +} + + +// --------------------------------------------------------------------------- + + +CUSTOM_CVAR(Bool, joy_axespolling, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + if (NULL != s_joystickManager) + { + s_joystickManager->UseAxesPolling(self); + } +} diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/cocoa/i_main.mm b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/cocoa/i_main.mm new file mode 100644 index 0000000..75ebba8 --- /dev/null +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/cocoa/i_main.mm @@ -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 +#include + +#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 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(screenSize.width); + vid_defheight = static_cast(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 +{ +} + +- (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 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; +} diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/cocoa/i_system.mm b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/cocoa/i_system.mm new file mode 100644 index 0000000..d27ccd6 --- /dev/null +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/cocoa/i_system.mm @@ -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 + +#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(arc4random()); +} diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/cocoa/i_video.mm b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/cocoa/i_video.mm new file mode 100644 index 0000000..f02918b --- /dev/null +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/cocoa/i_video.mm @@ -0,0 +1,1445 @@ +/* + ** i_video.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 "gl/system/gl_load.h" + +#include "i_common.h" + +#include "bitmap.h" +#include "c_dispatch.h" +#include "doomstat.h" +#include "hardware.h" +#include "i_system.h" +#include "m_argv.h" +#include "m_png.h" +#include "r_renderer.h" +#include "swrenderer/r_swrenderer.h" +#include "st_console.h" +#include "stats.h" +#include "textures.h" +#include "v_palette.h" +#include "v_pfx.h" +#include "v_text.h" +#include "v_video.h" +#include "version.h" +#include "videomodes.h" + +#include "gl/system/gl_system.h" +#include "gl/data/gl_vertexbuffer.h" +#include "gl/renderer/gl_renderer.h" +#include "gl/system/gl_framebuffer.h" +#include "gl/system/gl_interface.h" +#include "gl/textures/gl_samplers.h" +#include "gl/utility/gl_clock.h" + + +@implementation NSWindow(ExitAppOnClose) + +- (void)exitAppOnClose +{ + NSButton* closeButton = [self standardWindowButton:NSWindowCloseButton]; + [closeButton setAction:@selector(sendExitEvent:)]; + [closeButton setTarget:[NSApp delegate]]; +} + +@end + +@interface NSWindow(EnterFullscreenOnZoom) +- (void)enterFullscreenOnZoom; +@end + +@implementation NSWindow(EnterFullscreenOnZoom) + +- (void)enterFullscreen:(id)sender +{ + ToggleFullscreen = true; +} + +- (void)enterFullscreenOnZoom +{ + NSButton* zoomButton = [self standardWindowButton:NSWindowZoomButton]; + [zoomButton setEnabled:YES]; + [zoomButton setAction:@selector(enterFullscreen:)]; + [zoomButton setTarget:self]; +} + +@end + +DFrameBuffer *CreateGLSWFrameBuffer(int width, int height, bool bgra, bool fullscreen); + +int currentrenderer; + +CUSTOM_CVAR(Bool, vid_glswfb, true, CVAR_NOINITCALL | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + Printf("This won't take effect until " GAMENAME " is restarted.\n"); +} + +EXTERN_CVAR(Bool, ticker ) +EXTERN_CVAR(Bool, vid_vsync) +EXTERN_CVAR(Bool, vid_hidpi) + +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) + { + extern int NewWidth, NewHeight, NewBits, DisplayBits; + NewWidth = screen->VideoWidth; + NewHeight = screen->VideoHeight; + NewBits = DisplayBits; + setmodeneeded = true; + } +} + +CUSTOM_CVAR(Bool, fullscreen, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + extern int NewWidth, NewHeight, NewBits, DisplayBits; + + NewWidth = screen->VideoWidth; + NewHeight = screen->VideoHeight; + NewBits = DisplayBits; + setmodeneeded = true; +} + +CUSTOM_CVAR(Bool, vid_autoswitch, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + Printf("You must restart " GAMENAME " to apply graphics switching mode\n"); +} + +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", + static_cast(vid_renderer)); + self = 0; + break; + } + + Printf("You must restart " GAMENAME " to switch the renderer\n"); + } +} + +EXTERN_CVAR(Bool, gl_smooth_rendered) + + +RenderBufferOptions rbOpts; + + +// --------------------------------------------------------------------------- + + +namespace +{ + const NSInteger LEVEL_FULLSCREEN = NSMainMenuWindowLevel + 1; + const NSInteger LEVEL_WINDOWED = NSNormalWindowLevel; + + const NSUInteger STYLE_MASK_FULLSCREEN = NSBorderlessWindowMask; + const NSUInteger STYLE_MASK_WINDOWED = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask; +} + + +// --------------------------------------------------------------------------- + + +@interface CocoaWindow : NSWindow +{ + NSString* m_title; +} + +- (BOOL)canBecomeKeyWindow; +- (void)setTitle:(NSString*)title; +- (void)updateTitle; + +@end + + +@implementation CocoaWindow + +- (BOOL)canBecomeKeyWindow +{ + return true; +} + +- (void)setTitle:(NSString*)title +{ + m_title = title; + + [self updateTitle]; +} + +- (void)updateTitle +{ + if (nil == m_title) + { + m_title = [NSString stringWithFormat:@"%s %s", GAMESIG, GetVersionString()]; + } + + [super setTitle:m_title]; +} + +@end + + +// --------------------------------------------------------------------------- + + +@interface CocoaView : NSOpenGLView +{ + NSCursor* m_cursor; +} + +- (void)resetCursorRects; + +- (void)setCursor:(NSCursor*)cursor; + +@end + + +@implementation CocoaView + +- (void)resetCursorRects +{ + [super resetCursorRects]; + + NSCursor* const cursor = nil == m_cursor + ? [NSCursor arrowCursor] + : m_cursor; + + [self addCursorRect:[self bounds] + cursor:cursor]; +} + +- (void)setCursor:(NSCursor*)cursor +{ + m_cursor = cursor; +} + +@end + + +// --------------------------------------------------------------------------- + + +class CocoaVideo : public IVideo +{ +public: + CocoaVideo(); + + virtual EDisplayType GetDisplayType() { return DISPLAY_Both; } + virtual void SetWindowedScale(float scale); + + virtual DFrameBuffer* CreateFrameBuffer(int width, int height, bool bgra, bool fs, DFrameBuffer* old); + + virtual void StartModeIterator(int bits, bool fullscreen); + virtual bool NextMode(int* width, int* height, bool* letterbox); + + static bool IsFullscreen(); + static void UseHiDPI(bool hiDPI); + static void SetCursor(NSCursor* cursor); + static void SetWindowVisible(bool visible); + static void SetWindowTitle(const char* title); + +private: + struct ModeIterator + { + size_t index; + int bits; + bool fullscreen; + }; + + ModeIterator m_modeIterator; + + CocoaWindow* m_window; + + int m_width; + int m_height; + bool m_fullscreen; + bool m_hiDPI; + + void SetFullscreenMode(int width, int height); + void SetWindowedMode(int width, int height); + void SetMode(int width, int height, bool fullscreen, bool hiDPI); + + static CocoaVideo* GetInstance(); +}; + + +// --------------------------------------------------------------------------- + + +class CocoaFrameBuffer : public DFrameBuffer +{ +public: + CocoaFrameBuffer(int width, int height, bool bgra, bool fullscreen); + ~CocoaFrameBuffer(); + + virtual bool Lock(bool buffer); + virtual void Unlock(); + virtual void Update(); + + virtual PalEntry* GetPalette(); + virtual void GetFlashedPalette(PalEntry pal[256]); + virtual void UpdatePalette(); + + virtual bool SetGamma(float gamma); + virtual bool SetFlash(PalEntry rgb, int amount); + virtual void GetFlash(PalEntry &rgb, int &amount); + + virtual int GetPageCount(); + + virtual bool IsFullscreen(); + + virtual void SetVSync(bool vsync); + +private: + static const size_t BYTES_PER_PIXEL = 4; + + PalEntry m_palette[256]; + bool m_needPaletteUpdate; + + uint8_t m_gammaTable[3][256]; + float m_gamma; + bool m_needGammaUpdate; + + PalEntry m_flashColor; + int m_flashAmount; + + bool UpdatePending; + + uint8_t* m_pixelBuffer; + GLuint m_texture; + + void Flip(); + + void UpdateColors(); +}; + + +// --------------------------------------------------------------------------- + + +EXTERN_CVAR(Float, Gamma) + +CUSTOM_CVAR(Float, rgamma, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (NULL != screen) + { + screen->SetGamma(Gamma); + } +} + +CUSTOM_CVAR(Float, ggamma, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (NULL != screen) + { + screen->SetGamma(Gamma); + } +} + +CUSTOM_CVAR(Float, bgamma, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (NULL != screen) + { + screen->SetGamma(Gamma); + } +} + + +// --------------------------------------------------------------------------- + + +extern id appCtrl; + + +namespace +{ + +cycle_t BlitCycles; +cycle_t FlipCycles; + + +CocoaWindow* CreateCocoaWindow(const NSUInteger styleMask) +{ + static const CGFloat TEMP_WIDTH = VideoModes[0].width - 1; + static const CGFloat TEMP_HEIGHT = VideoModes[0].height - 1; + + CocoaWindow* const window = [CocoaWindow alloc]; + [window initWithContentRect:NSMakeRect(0, 0, TEMP_WIDTH, TEMP_HEIGHT) + styleMask:styleMask + backing:NSBackingStoreBuffered + defer:NO]; + [window setOpaque:YES]; + [window makeFirstResponder:appCtrl]; + [window setAcceptsMouseMovedEvents:YES]; + + return window; +} + +NSOpenGLPixelFormat* CreatePixelFormat(const NSOpenGLPixelFormatAttribute profile) +{ + NSOpenGLPixelFormatAttribute attributes[16]; + size_t i = 0; + + attributes[i++] = NSOpenGLPFADoubleBuffer; + attributes[i++] = NSOpenGLPFAColorSize; + attributes[i++] = NSOpenGLPixelFormatAttribute(32); + attributes[i++] = NSOpenGLPFADepthSize; + attributes[i++] = NSOpenGLPixelFormatAttribute(24); + attributes[i++] = NSOpenGLPFAStencilSize; + attributes[i++] = NSOpenGLPixelFormatAttribute(8); + attributes[i++] = NSOpenGLPFAOpenGLProfile; + attributes[i++] = profile; + + if (!vid_autoswitch) + { + attributes[i++] = NSOpenGLPFAAllowOfflineRenderers; + } + + attributes[i] = NSOpenGLPixelFormatAttribute(0); + + return [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; +} + +} // unnamed namespace + + +// --------------------------------------------------------------------------- + + +CocoaVideo::CocoaVideo() +: m_window(CreateCocoaWindow(STYLE_MASK_WINDOWED)) +, m_width(-1) +, m_height(-1) +, m_fullscreen(false) +, m_hiDPI(false) +{ + memset(&m_modeIterator, 0, sizeof m_modeIterator); + + extern void gl_CalculateCPUSpeed(); + gl_CalculateCPUSpeed(); + + // Create OpenGL pixel format + NSOpenGLPixelFormatAttribute defaultProfile = NSOpenGLProfileVersion3_2Core; + + if (1 == vid_renderer && NSAppKitVersionNumber < AppKit10_9) + { + // There is no support for OpenGL 3.3 before Mavericks + defaultProfile = NSOpenGLProfileVersionLegacy; + } + else if (0 == vid_renderer && 0 == vid_glswfb) + { + // Software renderer uses OpenGL 2.1 for blitting + defaultProfile = NSOpenGLProfileVersionLegacy; + } + else if (const char* const glversion = Args->CheckValue("-glversion")) + { + // Check for explicit version specified in command line + const double version = strtod(glversion, nullptr) + 0.01; + if (version < 3.3) + { + defaultProfile = NSOpenGLProfileVersionLegacy; + } + } + + NSOpenGLPixelFormat* pixelFormat = CreatePixelFormat(defaultProfile); + + if (nil == pixelFormat && NSOpenGLProfileVersion3_2Core == defaultProfile) + { + pixelFormat = CreatePixelFormat(NSOpenGLProfileVersionLegacy); + } + + if (nil == pixelFormat) + { + I_FatalError("Cannot create OpenGL pixel format, graphics hardware is not supported"); + } + + // Create OpenGL context and view + + const NSRect contentRect = [m_window contentRectForFrameRect:[m_window frame]]; + NSOpenGLView* glView = [[CocoaView alloc] initWithFrame:contentRect + pixelFormat:pixelFormat]; + [[glView openGLContext] makeCurrentContext]; + + [m_window setContentView:glView]; + + FConsoleWindow::GetInstance().Show(false); +} + +void CocoaVideo::StartModeIterator(const int bits, const bool fullscreen) +{ + m_modeIterator.index = 0; + m_modeIterator.bits = bits; + m_modeIterator.fullscreen = fullscreen; +} + +bool CocoaVideo::NextMode(int* const width, int* const height, bool* const letterbox) +{ + assert(NULL != width); + assert(NULL != height); + + const int bits = m_modeIterator.bits; + + if (8 != bits && 16 != bits && 24 != bits && 32 != bits) + { + return false; + } + + size_t& index = m_modeIterator.index; + + if (index < sizeof(VideoModes) / sizeof(VideoModes[0])) + { + *width = VideoModes[index].width; + *height = VideoModes[index].height; + + if (m_modeIterator.fullscreen && NULL != letterbox) + { + const NSSize screenSize = [[m_window screen] frame].size; + const float screenRatio = screenSize.width / screenSize.height; + const float modeRatio = float(*width) / *height; + + *letterbox = fabs(screenRatio - modeRatio) > 0.001f; + } + + ++index; + + return true; + } + + return false; +} + +DFrameBuffer* CocoaVideo::CreateFrameBuffer(const int width, const int height, const bool bgra, const bool fullscreen, DFrameBuffer* const old) +{ + PalEntry flashColor = 0; + int flashAmount = 0; + + if (NULL != old) + { + if (width == m_width && height == m_height && bgra == old->IsBgra()) + { + SetMode(width, height, fullscreen, vid_hidpi); + return old; + } + + old->GetFlash(flashColor, flashAmount); + + if (old == screen) + { + screen = NULL; + } + + delete old; + } + + DFrameBuffer* fb = NULL; + + if (1 == currentrenderer) + { + fb = new OpenGLFrameBuffer(NULL, width, height, 32, 60, fullscreen); + } + else if (vid_glswfb) + { + fb = CreateGLSWFrameBuffer(width, height, bgra, fullscreen); + + if (!fb->IsValid()) + { + delete fb; + + fb = new CocoaFrameBuffer(width, height, bgra, fullscreen); + } + } + else + { + fb = new CocoaFrameBuffer(width, height, bgra, fullscreen); + } + + fb->SetFlash(flashColor, flashAmount); + + SetMode(width, height, fullscreen, vid_hidpi); + + return fb; +} + +void CocoaVideo::SetWindowedScale(float scale) +{ +} + + +bool CocoaVideo::IsFullscreen() +{ + CocoaVideo* const video = GetInstance(); + return NULL == video + ? false + : video->m_fullscreen; +} + +void CocoaVideo::UseHiDPI(const bool hiDPI) +{ + if (CocoaVideo* const video = GetInstance()) + { + video->SetMode(video->m_width, video->m_height, video->m_fullscreen, hiDPI); + } +} + +void CocoaVideo::SetCursor(NSCursor* cursor) +{ + if (CocoaVideo* const video = GetInstance()) + { + NSWindow* const window = video->m_window; + CocoaView* const view = [window contentView]; + + [view setCursor:cursor]; + [window invalidateCursorRectsForView:view]; + } +} + +void CocoaVideo::SetWindowVisible(bool visible) +{ + if (CocoaVideo* const video = GetInstance()) + { + if (visible) + { + [video->m_window orderFront:nil]; + } + else + { + [video->m_window orderOut:nil]; + } + + I_SetNativeMouse(!visible); + } +} + +void CocoaVideo::SetWindowTitle(const char* title) +{ + if (CocoaVideo* const video = GetInstance()) + { + NSString* const nsTitle = nullptr == title ? nil : + [NSString stringWithCString:title encoding:NSISOLatin1StringEncoding]; + [video->m_window setTitle:nsTitle]; + } +} + + +void CocoaVideo::SetFullscreenMode(const int width, const int height) +{ + NSScreen* screen = [m_window screen]; + + const NSRect screenFrame = [screen frame]; + const NSRect displayRect = vid_hidpi + ? [screen convertRectToBacking:screenFrame] + : screenFrame; + + const float displayWidth = displayRect.size.width; + const float displayHeight = displayRect.size.height; + + const float pixelScaleFactorX = displayWidth / static_cast(width ); + const float pixelScaleFactorY = displayHeight / static_cast(height); + + rbOpts.pixelScale = MIN(pixelScaleFactorX, pixelScaleFactorY); + + rbOpts.width = width * rbOpts.pixelScale; + rbOpts.height = height * rbOpts.pixelScale; + + rbOpts.shiftX = (displayWidth - rbOpts.width ) / 2.0f; + rbOpts.shiftY = (displayHeight - rbOpts.height) / 2.0f; + + if (!m_fullscreen) + { + [m_window setLevel:LEVEL_FULLSCREEN]; + [m_window setStyleMask:STYLE_MASK_FULLSCREEN]; + + [m_window setHidesOnDeactivate:YES]; + } + + [m_window setFrame:screenFrame display:YES]; +} + +void CocoaVideo::SetWindowedMode(const int width, const int height) +{ + rbOpts.pixelScale = 1.0f; + + rbOpts.width = static_cast(width ); + rbOpts.height = static_cast(height); + + rbOpts.shiftX = 0.0f; + rbOpts.shiftY = 0.0f; + + const NSSize windowPixelSize = NSMakeSize(width, height); + const NSSize windowSize = vid_hidpi + ? [[m_window contentView] convertSizeFromBacking:windowPixelSize] + : windowPixelSize; + + if (m_fullscreen) + { + [m_window setLevel:LEVEL_WINDOWED]; + [m_window setStyleMask:STYLE_MASK_WINDOWED]; + + [m_window setHidesOnDeactivate:NO]; + } + + [m_window setContentSize:windowSize]; + [m_window center]; + [m_window enterFullscreenOnZoom]; + [m_window exitAppOnClose]; +} + +void CocoaVideo::SetMode(const int width, const int height, const bool fullscreen, const bool hiDPI) +{ + if (fullscreen == m_fullscreen + && width == m_width + && height == m_height + && hiDPI == m_hiDPI) + { + return; + } + + NSOpenGLView* const glView = [m_window contentView]; + [glView setWantsBestResolutionOpenGLSurface:hiDPI]; + + if (fullscreen) + { + SetFullscreenMode(width, height); + } + else + { + SetWindowedMode(width, height); + } + + rbOpts.dirty = true; + + const NSSize viewSize = I_GetContentViewSize(m_window); + + glViewport(0, 0, static_cast(viewSize.width), static_cast(viewSize.height)); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + [[NSOpenGLContext currentContext] flushBuffer]; + + [m_window updateTitle]; + + if (![m_window isKeyWindow]) + { + [m_window makeKeyAndOrderFront:nil]; + } + + m_fullscreen = fullscreen; + m_width = width; + m_height = height; + m_hiDPI = hiDPI; +} + + +CocoaVideo* CocoaVideo::GetInstance() +{ + return static_cast(Video); +} + + +// --------------------------------------------------------------------------- + + +CocoaFrameBuffer::CocoaFrameBuffer(int width, int height, bool bgra, bool fullscreen) +: DFrameBuffer(width, height, bgra) +, m_needPaletteUpdate(false) +, m_gamma(0.0f) +, m_needGammaUpdate(false) +, m_flashAmount(0) +, UpdatePending(false) +, m_pixelBuffer(new uint8_t[width * height * BYTES_PER_PIXEL]) +, m_texture(0) +{ + static bool isOpenGLInitialized; + + if (!isOpenGLInitialized) + { + if (ogl_LoadFunctions() == ogl_LOAD_FAILED) + { + I_FatalError("Failed to load OpenGL functions."); + } + isOpenGLInitialized = true; + } + + glEnable(GL_TEXTURE_RECTANGLE_ARB); + + glGenTextures(1, &m_texture); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_texture); + + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0, width, height, 0.0, -1.0, 1.0); + + GPfx.SetFormat(32, 0x000000FF, 0x0000FF00, 0x00FF0000); + + for (size_t i = 0; i < 256; ++i) + { + m_gammaTable[0][i] = m_gammaTable[1][i] = m_gammaTable[2][i] = i; + } + + memcpy(m_palette, GPalette.BaseColors, sizeof(PalEntry) * 256); + UpdateColors(); + + SetVSync(vid_vsync); +} + + +CocoaFrameBuffer::~CocoaFrameBuffer() +{ + glBindTexture(GL_TEXTURE_2D, 0); + glDeleteTextures(1, &m_texture); + + delete[] m_pixelBuffer; +} + +int CocoaFrameBuffer::GetPageCount() +{ + return 1; +} + +bool CocoaFrameBuffer::Lock(bool buffered) +{ + return DSimpleCanvas::Lock(buffered); +} + +void CocoaFrameBuffer::Unlock() +{ + if (UpdatePending && LockCount == 1) + { + Update(); + } + else if (--LockCount <= 0) + { + Buffer = NULL; + LockCount = 0; + } +} + +void CocoaFrameBuffer::Update() +{ + if (LockCount != 1) + { + if (LockCount > 0) + { + UpdatePending = true; + --LockCount; + } + return; + } + + DrawRateStuff(); + + Buffer = NULL; + LockCount = 0; + UpdatePending = false; + + BlitCycles.Reset(); + FlipCycles.Reset(); + BlitCycles.Clock(); + + if (IsBgra()) + { + CopyWithGammaBgra(m_pixelBuffer, Width * BYTES_PER_PIXEL, m_gammaTable[0], m_gammaTable[1], m_gammaTable[2], m_flashColor, m_flashAmount); + } + else + { + GPfx.Convert(MemBuffer, Pitch, m_pixelBuffer, Width * BYTES_PER_PIXEL, + Width, Height, FRACUNIT, FRACUNIT, 0, 0); + } + + FlipCycles.Clock(); + Flip(); + FlipCycles.Unclock(); + + BlitCycles.Unclock(); + + if (m_needGammaUpdate) + { + CalcGamma(rgamma == 0.0f ? m_gamma : m_gamma * rgamma, m_gammaTable[0]); + CalcGamma(ggamma == 0.0f ? m_gamma : m_gamma * ggamma, m_gammaTable[1]); + CalcGamma(bgamma == 0.0f ? m_gamma : m_gamma * bgamma, m_gammaTable[2]); + + m_needGammaUpdate = false; + m_needPaletteUpdate = true; + } + + if (m_needPaletteUpdate) + { + m_needPaletteUpdate = false; + UpdateColors(); + } +} + +void CocoaFrameBuffer::UpdateColors() +{ + PalEntry palette[256]; + + for (size_t i = 0; i < 256; ++i) + { + palette[i].r = m_gammaTable[0][m_palette[i].r]; + palette[i].g = m_gammaTable[1][m_palette[i].g]; + palette[i].b = m_gammaTable[2][m_palette[i].b]; + } + + if (0 != m_flashAmount) + { + DoBlending(palette, palette, 256, + m_gammaTable[0][m_flashColor.r], + m_gammaTable[1][m_flashColor.g], + m_gammaTable[2][m_flashColor.b], + m_flashAmount); + } + + GPfx.SetPalette(palette); +} + +PalEntry* CocoaFrameBuffer::GetPalette() +{ + return m_palette; +} + +void CocoaFrameBuffer::UpdatePalette() +{ + m_needPaletteUpdate = true; +} + +bool CocoaFrameBuffer::SetGamma(float gamma) +{ + m_gamma = gamma; + m_needGammaUpdate = true; + + return true; +} + +bool CocoaFrameBuffer::SetFlash(PalEntry rgb, int amount) +{ + m_flashColor = rgb; + m_flashAmount = amount; + m_needPaletteUpdate = true; + + return true; +} + +void CocoaFrameBuffer::GetFlash(PalEntry &rgb, int &amount) +{ + rgb = m_flashColor; + amount = m_flashAmount; +} + +void CocoaFrameBuffer::GetFlashedPalette(PalEntry pal[256]) +{ + memcpy(pal, m_palette, sizeof m_palette); + + if (0 != m_flashAmount) + { + DoBlending(pal, pal, 256, + m_flashColor.r, m_flashColor.g, m_flashColor.b, + m_flashAmount); + } +} + +bool CocoaFrameBuffer::IsFullscreen() +{ + return CocoaVideo::IsFullscreen(); +} + +void CocoaFrameBuffer::SetVSync(bool vsync) +{ + const GLint value = vsync ? 1 : 0; + + [[NSOpenGLContext currentContext] setValues:&value + forParameter:NSOpenGLCPSwapInterval]; +} + +void CocoaFrameBuffer::Flip() +{ + assert(NULL != screen); + + if (rbOpts.dirty) + { + glViewport(rbOpts.shiftX, rbOpts.shiftY, rbOpts.width, rbOpts.height); + + // TODO: Figure out why the following glClear() call is needed + // to avoid drawing of garbage in fullscreen mode when + // in-game's aspect ratio is different from display one + glClear(GL_COLOR_BUFFER_BIT); + + rbOpts.dirty = false; + } + + const GLenum format = IsBgra() ? GL_BGRA : GL_RGBA; + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, Width, Height, 0, format, GL_UNSIGNED_BYTE, m_pixelBuffer); + + glBegin(GL_QUADS); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glTexCoord2f(0.0f, 0.0f); + glVertex2f(0.0f, 0.0f); + glTexCoord2f(Width, 0.0f); + glVertex2f(Width, 0.0f); + glTexCoord2f(Width, Height); + glVertex2f(Width, Height); + glTexCoord2f(0.0f, Height); + glVertex2f(0.0f, Height); + glEnd(); + + glFlush(); + + [[NSOpenGLContext currentContext] flushBuffer]; +} + + +// --------------------------------------------------------------------------- + + +SDLGLFB::SDLGLFB(void*, const int width, const int height, int, int, const bool fullscreen, bool bgra) +: DFrameBuffer(width, height, bgra) +, m_Lock(0) +, UpdatePending(false) +{ + CGGammaValue gammaTable[GAMMA_TABLE_SIZE]; + uint32_t actualChannelSize; + + const CGError result = CGGetDisplayTransferByTable(kCGDirectMainDisplay, GAMMA_CHANNEL_SIZE, + gammaTable, &gammaTable[GAMMA_CHANNEL_SIZE], &gammaTable[GAMMA_CHANNEL_SIZE * 2], &actualChannelSize); + m_supportsGamma = kCGErrorSuccess == result && GAMMA_CHANNEL_SIZE == actualChannelSize; + + if (m_supportsGamma) + { + for (uint32_t i = 0; i < GAMMA_TABLE_SIZE; ++i) + { + m_originalGamma[i] = static_cast(gammaTable[i] * 65535.0f); + } + } +} + +SDLGLFB::SDLGLFB() +{ +} + +SDLGLFB::~SDLGLFB() +{ +} + + +bool SDLGLFB::Lock(bool buffered) +{ + m_Lock++; + + Buffer = MemBuffer; + + return true; +} + +void SDLGLFB::Unlock() +{ + if (UpdatePending && 1 == m_Lock) + { + Update(); + } + else if (--m_Lock <= 0) + { + m_Lock = 0; + } +} + +bool SDLGLFB::IsLocked() +{ + return m_Lock > 0; +} + + +bool SDLGLFB::IsFullscreen() +{ + return CocoaVideo::IsFullscreen(); +} + +void SDLGLFB::SetVSync(bool vsync) +{ + const GLint value = vsync ? 1 : 0; + + [[NSOpenGLContext currentContext] setValues:&value + forParameter:NSOpenGLCPSwapInterval]; +} + + +void SDLGLFB::InitializeState() +{ +} + +void SDLGLFB::SwapBuffers() +{ + [[NSOpenGLContext currentContext] flushBuffer]; +} + +void SDLGLFB::SetGammaTable(uint16_t* table) +{ + if (m_supportsGamma) + { + CGGammaValue gammaTable[GAMMA_TABLE_SIZE]; + + for (uint32_t i = 0; i < GAMMA_TABLE_SIZE; ++i) + { + gammaTable[i] = static_cast(table[i] / 65535.0f); + } + + CGSetDisplayTransferByTable(kCGDirectMainDisplay, GAMMA_CHANNEL_SIZE, + gammaTable, &gammaTable[GAMMA_CHANNEL_SIZE], &gammaTable[GAMMA_CHANNEL_SIZE * 2]); + } +} + +void SDLGLFB::ResetGammaTable() +{ + if (m_supportsGamma) + { + SetGammaTable(m_originalGamma); + } +} + +int SDLGLFB::GetClientWidth() +{ + NSView *view = [[NSOpenGLContext currentContext] view]; + NSRect backingBounds = [view convertRectToBacking: [view bounds]]; + int clientWidth = (int)backingBounds.size.width; + return clientWidth > 0 ? clientWidth : Width; +} + +int SDLGLFB::GetClientHeight() +{ + NSView *view = [[NSOpenGLContext currentContext] view]; + NSRect backingBounds = [view convertRectToBacking: [view bounds]]; + int clientHeight = (int)backingBounds.size.height; + return clientHeight > 0 ? clientHeight : Height; +} + + +// --------------------------------------------------------------------------- + + +ADD_STAT(blit) +{ + FString result; + result.Format("blit=%04.1f ms flip=%04.1f ms", BlitCycles.TimeMS(), FlipCycles.TimeMS()); + return result; +} + + +IVideo* Video; + + +// --------------------------------------------------------------------------- + + +void I_ShutdownGraphics() +{ + if (NULL != screen) + { + delete screen; + screen = NULL; + } + + delete Video; + Video = NULL; +} + +void I_InitGraphics() +{ + UCVarValue val; + + val.Bool = !!Args->CheckParm("-devparm"); + ticker.SetGenericRepDefault(val, CVAR_Bool); + + Video = new CocoaVideo; +} + + +void I_DeleteRenderer() +{ + delete Renderer; + Renderer = NULL; +} + +void I_CreateRenderer() +{ + currentrenderer = vid_renderer; + + if (NULL == Renderer) + { + extern FRenderer* gl_CreateInterface(); + + Renderer = 1 == currentrenderer + ? gl_CreateInterface() + : new FSoftwareRenderer; + } +} + + +DFrameBuffer* I_SetMode(int &width, int &height, DFrameBuffer* old) +{ + return Video->CreateFrameBuffer(width, height, swtruecolor, fullscreen, old); +} + +bool I_CheckResolution(const int width, const int height, const int bits) +{ + int twidth, theight; + + Video->StartModeIterator(bits, 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) +{ + int twidth, theight; + int cwidth = 0, cheight = 0; + int iteration; + uint32_t closest = uint32_t(-1); + + for (iteration = 0; iteration < 2; ++iteration) + { + Video->StartModeIterator(bits, fullscreen); + + while (Video->NextMode(&twidth, &theight, NULL)) + { + if (twidth == *width && theight == *height) + { + return; + } + + if (iteration == 0 && (twidth < *width || theight < *height)) + { + continue; + } + + const uint32_t dist = (twidth - *width) * (twidth - *width) + + (theight - *height) * (theight - *height); + + if (dist < closest) + { + closest = dist; + cwidth = twidth; + cheight = theight; + } + } + + if (closest != uint32_t(-1)) + { + *width = cwidth; + *height = cheight; + return; + } + } +} + + +// --------------------------------------------------------------------------- + + +EXTERN_CVAR(Int, vid_maxfps); +EXTERN_CVAR(Bool, cl_capfps); + +// 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) +{ +} + +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); + } +} + +CUSTOM_CVAR(Bool, vid_hidpi, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + CocoaVideo::UseHiDPI(self); +} + + +// --------------------------------------------------------------------------- + + +CCMD(vid_listmodes) +{ + if (Video == NULL) + { + return; + } + + static const char* const ratios[7] = { "", " - 16:9", " - 16:10", " - 17:10", " - 5:4", "", " - 21:9" }; + int width, height; + bool letterbox; + + Video->StartModeIterator(32, screen->IsFullscreen()); + + while (Video->NextMode(&width, &height, &letterbox)) + { + const bool current = width == DisplayWidth && height == DisplayHeight; + const int ratio = CheckRatio(width, height); + + Printf(current ? PRINT_BOLD : PRINT_HIGH, "%s%4d x%5d x%3d%s%s\n", + current || !(ratio & 3) ? "" : TEXTCOLOR_GOLD, + width, height, 32, ratios[ratio], + current || !letterbox ? "" : TEXTCOLOR_BROWN " LB"); + } +} + +CCMD(vid_currentmode) +{ + Printf("%dx%dx%d\n", DisplayWidth, DisplayHeight, DisplayBits); +} + + +// --------------------------------------------------------------------------- + + +bool I_SetCursor(FTexture* cursorpic) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSCursor* cursor = nil; + + if (NULL != cursorpic && ETextureType::Null != cursorpic->UseType) + { + // Create bitmap image representation + + const NSInteger imageWidth = cursorpic->GetWidth(); + const NSInteger imageHeight = cursorpic->GetHeight(); + const NSInteger imagePitch = imageWidth * 4; + + NSBitmapImageRep* bitmapImageRep = [NSBitmapImageRep alloc]; + [bitmapImageRep initWithBitmapDataPlanes:NULL + pixelsWide:imageWidth + pixelsHigh:imageHeight + bitsPerSample:8 + samplesPerPixel:4 + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSDeviceRGBColorSpace + bytesPerRow:imagePitch + bitsPerPixel:0]; + + // Load bitmap data to representation + + uint8_t* buffer = [bitmapImageRep bitmapData]; + memset(buffer, 0, imagePitch * imageHeight); + + FBitmap bitmap(buffer, imagePitch, imageWidth, imageHeight); + cursorpic->CopyTrueColorPixels(&bitmap, 0, 0); + + // Swap red and blue components in each pixel + + for (size_t i = 0; i < size_t(imageWidth * imageHeight); ++i) + { + const size_t offset = i * 4; + + const uint8_t temp = buffer[offset ]; + buffer[offset ] = buffer[offset + 2]; + buffer[offset + 2] = temp; + } + + // Create image from representation and set it as cursor + + NSData* imageData = [bitmapImageRep representationUsingType:NSPNGFileType + properties:[NSDictionary dictionary]]; + NSImage* cursorImage = [[NSImage alloc] initWithData:imageData]; + + cursor = [[NSCursor alloc] initWithImage:cursorImage + hotSpot:NSMakePoint(0.0f, 0.0f)]; + } + + CocoaVideo::SetCursor(cursor); + + [pool release]; + + return true; +} + + +NSSize I_GetContentViewSize(const NSWindow* const window) +{ + const NSView* const view = [window contentView]; + const NSSize frameSize = [view frame].size; + + return (vid_hidpi) + ? [view convertSizeToBacking:frameSize] + : frameSize; +} + +void I_SetMainWindowVisible(bool visible) +{ + CocoaVideo::SetWindowVisible(visible); +} + +// each platform has its own specific version of this function. +void I_SetWindowTitle(const char* title) +{ + CocoaVideo::SetWindowTitle(title); +} diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/cocoa/sdlglvideo.h b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/cocoa/sdlglvideo.h new file mode 100644 index 0000000..7e98d95 --- /dev/null +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/cocoa/sdlglvideo.h @@ -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 diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/cocoa/st_console.h b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/cocoa/st_console.h new file mode 100644 index 0000000..b2af7ba --- /dev/null +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/cocoa/st_console.h @@ -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 diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/cocoa/st_console.mm b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/cocoa/st_console.mm new file mode 100644 index 0000000..fa96941 --- /dev/null +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/cocoa/st_console.mm @@ -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 +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 +unsigned int TimedUpdater::m_previousTime; + +template +static void UpdateTimed(const Function& function) +{ + TimedUpdater 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(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; + } +} diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/cocoa/st_start.mm b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/cocoa/st_start.mm new file mode 100644 index 0000000..a98ca98 --- /dev/null +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/cocoa/st_start.mm @@ -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 + +#import + +#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); +} diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/i_system_posix.cpp b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/i_system_posix.cpp new file mode 100644 index 0000000..0f4f559 --- /dev/null +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/i_system_posix.cpp @@ -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 + +#ifdef __APPLE__ +#include +#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(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(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 I_GetGogPaths() +{ + // GOG's Doom games are Windows only at the moment + return TArray(); +} diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/osx/i_specialpaths.mm b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/osx/i_specialpaths.mm new file mode 100644 index 0000000..540aaf4 --- /dev/null +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/osx/i_specialpaths.mm @@ -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 + +#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-.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; +} diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/osx/iwadpicker_cocoa.mm b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/osx/iwadpicker_cocoa.mm new file mode 100644 index 0000000..f1fa313 --- /dev/null +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/osx/iwadpicker_cocoa.mm @@ -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 +#include +#include + + +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 +{ + 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; +} diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/osx/zdoom-info.plist b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/osx/zdoom-info.plist new file mode 100644 index 0000000..da2263f --- /dev/null +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/osx/zdoom-info.plist @@ -0,0 +1,52 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${MACOSX_BUNDLE_EXECUTABLE_NAME} + CFBundleIconFile + zdoom.icns + CFBundleIdentifier + org.drdteam.gzdoom + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + GZDoom + CFBundlePackageType + APPL + CFBundleShortVersionString + Development Version + CFBundleSignature + ???? + LSApplicationCategoryType + public.app-category.action-games + LSMinimumSystemVersion + 10.7 + CFBundleDocumentTypes + + + CFBundleTypeName + Doom Resource File + CFBundleTypeRole + Viewer + CFBundleTypeExtensions + + wad + pk3 + zip + pk7 + 7z + iwad + ipk3 + ipk7 + + + + NSPrincipalClass + NSApplication + NSSupportsAutomaticGraphicsSwitching + + + diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/osx/zdoom.icns b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/osx/zdoom.icns new file mode 100644 index 0000000..af60dd2 Binary files /dev/null and b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/osx/zdoom.icns differ diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/crashcatcher.c b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/crashcatcher.c new file mode 100644 index 0000000..11e5d17 --- /dev/null +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/crashcatcher.c @@ -0,0 +1,428 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __linux__ +#include +#ifndef PR_SET_PTRACER +#define PR_SET_PTRACER 0x59616d61 +#endif +#elif defined (__APPLE__) || defined (__FreeBSD__) || defined(__OpenBSD__) +#include +#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; +} diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/hardware.cpp b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/hardware.cpp new file mode 100644 index 0000000..753434f --- /dev/null +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/hardware.cpp @@ -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 +#include +#include + +#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 /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); +} diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/i_gui.cpp b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/i_gui.cpp new file mode 100644 index 0000000..1a895a6 --- /dev/null +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/i_gui.cpp @@ -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 + +#include + +#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; +} diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/i_input.cpp b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/i_input.cpp new file mode 100644 index 0000000..596eea2 --- /dev/null +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/i_input.cpp @@ -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 +#include +#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 InitKeySymMap () +{ + TMap 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 KeySymToDIK(InitKeySymMap()); + +static TMap InitKeyScanMap () +{ + TMap KeyScanToDIK; + + for (int i = 0; i < 256; ++i) + { + KeyScanToDIK[DIKToKeyScan[i]] = i; + } + + return KeyScanToDIK; +} +static const TMap 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(); +} diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/i_joystick.cpp b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/i_joystick.cpp new file mode 100644 index 0000000..3c6ff0d --- /dev/null +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/i_joystick.cpp @@ -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 + +#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 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 &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 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 &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; +} diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/i_main.cpp b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/i_main.cpp new file mode 100644 index 0000000..00fa154 --- /dev/null +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/i_main.cpp @@ -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 +#include +#include +#include +#include +#include +#include + +#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 +#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; +} diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/i_system.cpp b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/i_system.cpp new file mode 100644 index 0000000..0816276 --- /dev/null +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/i_system.cpp @@ -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 +#include +#include + +#include + +#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 +#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 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; +} diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/i_system.mm b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/i_system.mm new file mode 100644 index 0000000..50faf94 --- /dev/null +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/i_system.mm @@ -0,0 +1,19 @@ +#include +#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 ); + } +} diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/sdlglvideo.cpp b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/sdlglvideo.cpp new file mode 100644 index 0000000..a36a8ec --- /dev/null +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/sdlglvideo.cpp @@ -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 (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(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(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); + } +} diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/sdlvideo.cpp b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/sdlvideo.cpp new file mode 100644 index 0000000..5eb8a7b --- /dev/null +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/sdlvideo.cpp @@ -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 + +#ifdef __APPLE__ +#include +#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(screen)->GetSDLWindow(); + if (caption) + SDL_SetWindowTitle(static_cast(screen)->GetSDLWindow(), caption); + else + { + FString default_caption; + default_caption.Format(GAMESIG " %s (%s)", GetVersionString(), GetGitTime()); + SDL_SetWindowTitle(static_cast(screen)->GetSDLWindow(), default_caption); + } +} + diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/st_start.cpp b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/st_start.cpp new file mode 100644 index 0000000..6905108 --- /dev/null +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/posix/sdl/st_start.cpp @@ -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 +#include +#include +#include + +#include "st_start.h" +#include "doomdef.h" +#include "i_system.h" +#include "c_cvars.h" +#include "doomerrors.h" + +#ifdef __ANDROID__ +#include "JNITouchControlsUtils.h" +#include +#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); +} diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/version.h b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/version.h index 1593979..61dc1dc 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/version.h +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/version.h @@ -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 diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/win32/fb_d3d9.cpp b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/win32/fb_d3d9.cpp index 52c844e..286f237 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/win32/fb_d3d9.cpp +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/win32/fb_d3d9.cpp @@ -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); diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/win32/i_dijoy.cpp b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/win32/i_dijoy.cpp index bbec6d7..1f237fc 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/win32/i_dijoy.cpp +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/win32/i_dijoy.cpp @@ -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); diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/win32/i_input.cpp b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/win32/i_input.cpp index 5347b12..46320a7 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/win32/i_input.cpp +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/win32/i_input.cpp @@ -45,6 +45,7 @@ #ifndef __GNUC__ #define INITGUID #endif +#define DIRECTINPUT_VERSION 0x800 #include #include #include @@ -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" - } -} diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/win32/i_keyboard.cpp b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/win32/i_keyboard.cpp index 04dd5a1..eb16586 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/win32/i_keyboard.cpp +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/win32/i_keyboard.cpp @@ -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; } diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/win32/i_mouse.cpp b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/win32/i_mouse.cpp index f091f5e..7684428 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/win32/i_mouse.cpp +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/win32/i_mouse.cpp @@ -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; diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/win32/i_rawps2.cpp b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/win32/i_rawps2.cpp index 9ae4416..178f9fd 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/win32/i_rawps2.cpp +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/win32/i_rawps2.cpp @@ -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() // ## // 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; } diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/win32/win32gliface.cpp b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/win32/win32gliface.cpp index 34129e1..ec6dc72 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/win32/win32gliface.cpp +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/win32/win32gliface.cpp @@ -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; } } diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/tools/CMakeLists.txt b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/tools/CMakeLists.txt index 8a97243..b3fed70 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/tools/CMakeLists.txt +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/tools/CMakeLists.txt @@ -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 ) diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/tools/updaterevision/CMakeLists.txt b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/tools/updaterevision/CMakeLists.txt deleted file mode 100644 index db99ab8..0000000 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/tools/updaterevision/CMakeLists.txt +++ /dev/null @@ -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:$ -manifest ${CMAKE_CURRENT_SOURCE_DIR}/trustinfo.txt -outputresource:$ -nologo - COMMENT "Embedding trustinfo into updaterevision" ) -endif() diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/tools/updaterevision/UpdateRevision.cmake b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/tools/updaterevision/UpdateRevision.cmake new file mode 100644 index 0000000..619a868 --- /dev/null +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/tools/updaterevision/UpdateRevision.cmake @@ -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 + message("Usage: ${CMAKE_ARGV2} ") + 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 "") + 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() diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/tools/updaterevision/gitinfo.h.in b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/tools/updaterevision/gitinfo.h.in new file mode 100644 index 0000000..7b8d132 --- /dev/null +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/tools/updaterevision/gitinfo.h.in @@ -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@" diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/tools/updaterevision/trustinfo.rc b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/tools/updaterevision/trustinfo.rc deleted file mode 100644 index 366f9b2..0000000 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/tools/updaterevision/trustinfo.rc +++ /dev/null @@ -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 - -1 RT_MANIFEST "trustinfo.txt" diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/tools/updaterevision/trustinfo.txt b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/tools/updaterevision/trustinfo.txt deleted file mode 100644 index 2bbed9f..0000000 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/tools/updaterevision/trustinfo.txt +++ /dev/null @@ -1,16 +0,0 @@ - - - - - Update svnrevision.h for the ZDoom source build process. - - - - - - - - diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/filter/game-hexen/defbind0.txt b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/filter/game-hexen/defbind0.txt new file mode 100644 index 0000000..d07e682 --- /dev/null +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/filter/game-hexen/defbind0.txt @@ -0,0 +1,3 @@ +/* Default keybindings for Hexen */ + +/ +jump diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/filter/game-hexen/defbinds.txt b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/filter/game-hexen/defbinds.txt index 58167f5..d951bc9 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/filter/game-hexen/defbinds.txt +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/filter/game-hexen/defbinds.txt @@ -1,6 +1,5 @@ /* Default keybindings for Hexen */ -/ +jump backspace invuseall \ "use ArtiHealth" 0 useflechette diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/filter/game-strife/acs/strfhelp.o b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/filter/game-strife/acs/strfhelp.o new file mode 100644 index 0000000..483056f Binary files /dev/null and b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/filter/game-strife/acs/strfhelp.o differ diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/filter/game-strife/defbind3.txt b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/filter/game-strife/defbind3.txt index 316c53e..8439ab1 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/filter/game-strife/defbind3.txt +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/filter/game-strife/defbind3.txt @@ -1,5 +1,5 @@ /* Default keybindings for Strife */ w "showpop 1" -u "showpop 2" +s "showpop 2" diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/language.enu b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/language.enu index 815f4fd..fa39eed 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/language.enu +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/language.enu @@ -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: diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/menudef.txt b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/menudef.txt index ffa088a..806a823 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/menudef.txt +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/menudef.txt @@ -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" } diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/shaders/glsl/main.fp b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/shaders/glsl/main.fp index 4ec892f..53e9466 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/shaders/glsl/main.fp +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/shaders/glsl/main.fp @@ -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) diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/zscript/actors/doom/weaponchaingun.zs b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/zscript/actors/doom/weaponchaingun.zs index 8153f0e..58a4506 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/zscript/actors/doom/weaponchaingun.zs +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/zscript/actors/doom/weaponchaingun.zs @@ -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); } } } diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/zscript/actors/inventory/weapons.zs b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/zscript/actors/inventory/weapons.zs index b35fab9..9973665 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/zscript/actors/inventory/weapons.zs +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/zscript/actors/inventory/weapons.zs @@ -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()); } } diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/zscript/actors/strife/strifeplayer.zs b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/zscript/actors/strife/strifeplayer.zs index c622f93..9028162 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/zscript/actors/strife/strifeplayer.zs +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/zscript/actors/strife/strifeplayer.zs @@ -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--; } diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/zscript/actors/strife/weaponmauler.zs b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/zscript/actors/strife/weaponmauler.zs index a8a56f5..b805c3a 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/zscript/actors/strife/weaponmauler.zs +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/zscript/actors/strife/weaponmauler.zs @@ -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.; } } } diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/zscript/events.zs b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/zscript/events.zs index 529c0c3..acb28b0 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/zscript/events.zs +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/zscript/events.zs @@ -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), diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/zscript/ui/statusbar/alt_hud.zs b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/zscript/ui/statusbar/alt_hud.zs index 0458b44..7504cb9 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/zscript/ui/statusbar/alt_hud.zs +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/zscript/ui/statusbar/alt_hud.zs @@ -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; } diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/zscript/ui/statusbar/statusbar.zs b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/zscript/ui/statusbar/statusbar.zs index 577bf24..a9795c6 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/zscript/ui/statusbar/statusbar.zs +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/zscript/ui/statusbar/statusbar.zs @@ -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); diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc_lights/static/filter/heretic/gldefs.txt b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc_lights/static/filter/heretic/gldefs.txt index 9076b2a..65796e7 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc_lights/static/filter/heretic/gldefs.txt +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc_lights/static/filter/heretic/gldefs.txt @@ -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 } } diff --git a/assets/res/lzdoom.pk3 b/assets/res/lzdoom.pk3 index cc81ca3..f440994 100644 Binary files a/assets/res/lzdoom.pk3 and b/assets/res/lzdoom.pk3 differ